Skip to content

Commit b33d9c1

Browse files
committed
start making \\ the separator instead of :>
1 parent 53701d1 commit b33d9c1

25 files changed

+219
-101
lines changed

Zend/zend_compile.c

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,21 @@ static zend_string *zend_concat_names(char *name1, size_t name1_len, char *name2
10991099
return zend_string_concat3(name1, name1_len, "\\", 1, name2, name2_len);
11001100
}
11011101

1102+
static zend_string *zend_prefix_with_outer_class(zend_string *name) {
1103+
ZEND_ASSERT(CG(active_class_entry));
1104+
1105+
zend_string *ns = CG(active_class_entry)->name;
1106+
1107+
// If the current ns is the current name, return the ns.
1108+
// we only need to check if ns[len(ns) - len(name) + 1] == '\\' and the end of ns is name.
1109+
if (ZSTR_LEN(ns) >= ZSTR_LEN(name) && ZSTR_VAL(ns)[ZSTR_LEN(ns) - ZSTR_LEN(name) - 1] == '\\'
1110+
&& memcmp(ZSTR_VAL(ns) + ZSTR_LEN(ns) - ZSTR_LEN(name), ZSTR_VAL(name), ZSTR_LEN(name)) == 0) {
1111+
return zend_string_copy(ns);
1112+
}
1113+
1114+
return zend_concat_names(ZSTR_VAL(ns), ZSTR_LEN(ns), ZSTR_VAL(name), ZSTR_LEN(name));
1115+
}
1116+
11021117
static zend_string *zend_prefix_with_ns(zend_string *name) {
11031118
if (FC(current_namespace)) {
11041119
zend_string *ns = FC(current_namespace);
@@ -1236,6 +1251,10 @@ static zend_string *zend_resolve_class_name(zend_string *name, uint32_t type) /*
12361251
}
12371252
}
12381253

1254+
if (CG(active_class_entry)) {
1255+
return zend_prefix_with_outer_class(name);
1256+
}
1257+
12391258
/* If not fully qualified and not an alias, prepend the current namespace */
12401259
return zend_prefix_with_ns(name);
12411260
}
@@ -9093,10 +9112,9 @@ static void zend_compile_use_trait(zend_ast *ast) /* {{{ */
90939112
}
90949113
/* }}} */
90959114

9096-
static void zend_compile_implements(zend_ast *ast) /* {{{ */
9115+
static void zend_compile_implements(zend_ast *ast, zend_class_entry *ce) /* {{{ */
90979116
{
90989117
zend_ast_list *list = zend_ast_get_list(ast);
9099-
zend_class_entry *ce = CG(active_class_entry);
91009118
zend_class_name *interface_names;
91019119
uint32_t i;
91029120

@@ -9154,6 +9172,18 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_
91549172
zend_type_release(type, 0);
91559173
}
91569174

9175+
HashTable *inner_class_queue = NULL;
9176+
9177+
static void zend_defer_class_decl(zend_ast *ast)
9178+
{
9179+
if (inner_class_queue == NULL) {
9180+
ALLOC_HASHTABLE(inner_class_queue);
9181+
zend_hash_init(inner_class_queue, 0, NULL, ZVAL_PTR_DTOR, 0);
9182+
}
9183+
9184+
zend_hash_next_index_insert_ptr(inner_class_queue, ast);
9185+
}
9186+
91579187
static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */
91589188
{
91599189
zend_ast_decl *decl = (zend_ast_decl *) ast;
@@ -9188,7 +9218,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
91889218
// rename the inner class so we may reference it by name
91899219
name = zend_string_concat3(
91909220
ZSTR_VAL(CG(active_class_entry)->name), ZSTR_LEN(CG(active_class_entry)->name),
9191-
":>", 2,
9221+
"\\", 1,
91929222
ZSTR_VAL(unqualified_name), ZSTR_LEN(unqualified_name)
91939223
);
91949224

@@ -9284,16 +9314,16 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
92849314
zend_resolve_const_class_name_reference(extends_ast, "class name");
92859315
}
92869316

9317+
if (implements_ast) {
9318+
zend_compile_implements(implements_ast, ce);
9319+
}
9320+
92879321
CG(active_class_entry) = ce;
92889322

92899323
if (decl->child[3]) {
92909324
zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0);
92919325
}
92929326

9293-
if (implements_ast) {
9294-
zend_compile_implements(implements_ast);
9295-
}
9296-
92979327
if (ce->ce_flags & ZEND_ACC_ENUM) {
92989328
if (enum_backing_type_ast != NULL) {
92999329
zend_compile_enum_backing_type(ce, enum_backing_type_ast);
@@ -9311,8 +9341,6 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
93119341
zend_verify_abstract_class(ce);
93129342
}
93139343

9314-
CG(active_class_entry) = original_ce;
9315-
93169344
if (toplevel) {
93179345
ce->ce_flags |= ZEND_ACC_TOP_LEVEL;
93189346
}
@@ -9333,7 +9361,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
93339361
&& !zend_compile_ignore_class(parent_ce, ce->info.user.filename)) {
93349362
if (zend_try_early_bind(ce, parent_ce, lcname, NULL)) {
93359363
zend_string_release(lcname);
9336-
return;
9364+
goto compile_inner_classes;
93379365
}
93389366
}
93399367
} else if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
@@ -9342,7 +9370,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
93429370
zend_inheritance_check_override(ce);
93439371
ce->ce_flags |= ZEND_ACC_LINKED;
93449372
zend_observer_class_linked_notify(ce, lcname);
9345-
return;
9373+
goto compile_inner_classes;
93469374
} else {
93479375
goto link_unbound;
93489376
}
@@ -9412,6 +9440,25 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
94129440
opline->result.opline_num = -1;
94139441
}
94149442
}
9443+
9444+
compile_inner_classes:
9445+
9446+
if (inner_class_queue == NULL) {
9447+
CG(active_class_entry) = original_ce;
9448+
return;
9449+
}
9450+
9451+
HashTable *queue = inner_class_queue;
9452+
inner_class_queue = NULL;
9453+
9454+
ZEND_HASH_FOREACH_PTR(queue, ast) {
9455+
zend_compile_class_decl(NULL, ast, 0);
9456+
} ZEND_HASH_FOREACH_END();
9457+
9458+
CG(active_class_entry) = original_ce;
9459+
9460+
zend_hash_destroy(queue);
9461+
FREE_HASHTABLE(queue);
94159462
}
94169463
/* }}} */
94179464

@@ -11682,6 +11729,10 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
1168211729
zend_compile_use_trait(ast);
1168311730
break;
1168411731
case ZEND_AST_CLASS:
11732+
if (CG(active_class_entry)) {
11733+
zend_defer_class_decl(ast);
11734+
break;
11735+
}
1168511736
zend_compile_class_decl(NULL, ast, 0);
1168611737
break;
1168711738
case ZEND_AST_GROUP_USE:

Zend/zend_compile.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,14 +1028,15 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
10281028
#define ZEND_FETCH_CLASS_EXCEPTION 0x0200
10291029
#define ZEND_FETCH_CLASS_ALLOW_UNLINKED 0x0400
10301030
#define ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED 0x0800
1031+
#define ZEND_FETCH_CLASS_NO_INNER 0x1000
10311032

10321033
/* These should not clash with ZEND_ACC_PPP_MASK and ZEND_ACC_PPP_SET_MASK */
10331034
#define ZEND_PARAM_REF (1<<3)
10341035
#define ZEND_PARAM_VARIADIC (1<<4)
10351036

1036-
#define ZEND_NAME_FQ 0
1037-
#define ZEND_NAME_NOT_FQ 1
1038-
#define ZEND_NAME_RELATIVE 2
1037+
#define ZEND_NAME_FQ 0
1038+
#define ZEND_NAME_NOT_FQ 1
1039+
#define ZEND_NAME_RELATIVE 2
10391040

10401041
/* ZEND_FETCH_ flags in class name AST of new const expression must not clash with ZEND_NAME_ flags */
10411042
#define ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT 2

Zend/zend_execute_API.c

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,79 @@ ZEND_API bool zend_is_valid_class_name(zend_string *name) {
11661166
return 1;
11671167
}
11681168

1169+
static zend_class_entry *zend_resolve_nested_class(zend_string *requested_name, uint32_t flags) {
1170+
zend_class_entry *ce = NULL;
1171+
char *separator;
1172+
zend_string *scope_name = zend_string_copy(requested_name);
1173+
1174+
const char *unqualified_name = strrchr(ZSTR_VAL(scope_name), '\\');
1175+
zend_string *inner_name = zend_string_init(unqualified_name, ZSTR_LEN(scope_name) - (unqualified_name - ZSTR_VAL(scope_name)), 0);
1176+
1177+
while ((separator = strrchr(ZSTR_VAL(scope_name), '\\'))) {
1178+
const size_t outer_len = separator - ZSTR_VAL(scope_name);
1179+
1180+
zend_string *outer_class_name = zend_string_init(ZSTR_VAL(scope_name), outer_len, 0);
1181+
const zend_class_entry *outer_ce = zend_lookup_class_ex(outer_class_name, NULL, flags | ZEND_FETCH_CLASS_NO_INNER);
1182+
1183+
if (outer_ce) {
1184+
// Outer class found; now explicitly check the requested class again
1185+
ce = zend_lookup_class_ex(requested_name, NULL, flags | ZEND_FETCH_CLASS_NO_INNER);
1186+
if (ce) {
1187+
zend_string_release(scope_name);
1188+
zend_string_release(outer_class_name);
1189+
zend_string_release(inner_name);
1190+
return ce;
1191+
}
1192+
1193+
// Check if the class is in the outer scope
1194+
zend_string *outer_name = zend_string_concat2(ZSTR_VAL(outer_class_name), ZSTR_LEN(outer_class_name), ZSTR_VAL(inner_name), ZSTR_LEN(inner_name));
1195+
ce = zend_lookup_class_ex(outer_name, NULL, flags | ZEND_FETCH_CLASS_NO_INNER);
1196+
zend_string_release(outer_name);
1197+
zend_string_release(outer_class_name);
1198+
if (ce) {
1199+
zend_string_release(scope_name);
1200+
zend_string_release(inner_name);
1201+
return ce;
1202+
}
1203+
1204+
// Continue moving upwards (perhaps the inner class is further up)
1205+
zend_string *shorter_scope = zend_string_init(ZSTR_VAL(scope_name), outer_len, 0);
1206+
zend_string_release(scope_name);
1207+
scope_name = shorter_scope;
1208+
} else {
1209+
// Outer class isn't found: reached namespace level.
1210+
zend_string_release(scope_name);
1211+
zend_string_release(outer_class_name);
1212+
zend_string_release(inner_name);
1213+
break;
1214+
}
1215+
}
1216+
1217+
// handle the edge case where the class is in the global scope
1218+
if (separator == NULL) {
1219+
separator = strrchr(ZSTR_VAL(requested_name), '\\');
1220+
if (separator) {
1221+
zend_string_release(scope_name);
1222+
// set scope name to just the separator minus the first character
1223+
scope_name = zend_string_init(separator + 1, ZSTR_LEN(requested_name) - (separator - ZSTR_VAL(requested_name) + 1), 0);
1224+
} else {
1225+
zend_string_release(scope_name);
1226+
zend_string_release(inner_name);
1227+
return NULL;
1228+
}
1229+
ce = zend_lookup_class_ex(scope_name, NULL, flags | ZEND_FETCH_CLASS_NO_INNER);
1230+
zend_string_release(scope_name);
1231+
zend_string_release(inner_name);
1232+
return ce;
1233+
}
1234+
1235+
zend_string_release(scope_name);
1236+
zend_string_release(inner_name);
1237+
1238+
// Final lookup directly at namespace/global scope
1239+
return zend_lookup_class_ex(requested_name, NULL, flags | ZEND_FETCH_CLASS_NO_INNER);
1240+
}
1241+
11691242
ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *key, uint32_t flags) /* {{{ */
11701243
{
11711244
zend_class_entry *ce = NULL;
@@ -1225,7 +1298,15 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
12251298
return ce;
12261299
}
12271300

1228-
/* The compiler is not-reentrant. Make sure we autoload only during run-time. */
1301+
/* Check to see if our current scope is an outer class; if it is, we need to check the outer class's
1302+
* namespace for the class we're looking for. */
1303+
if (!(flags & ZEND_FETCH_CLASS_NO_INNER)) {
1304+
if (!key) {
1305+
zend_string_release_ex(lc_name, 0);
1306+
}
1307+
return zend_resolve_nested_class(name, flags);
1308+
}
1309+
12291310
if ((flags & ZEND_FETCH_CLASS_NO_AUTOLOAD) || zend_is_compiling()) {
12301311
if (!key) {
12311312
zend_string_release_ex(lc_name, 0);

Zend/zend_language_parser.y

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,6 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
231231
%token T_DOLLAR_OPEN_CURLY_BRACES "'${'"
232232
%token T_CURLY_OPEN "'{$'"
233233
%token T_PAAMAYIM_NEKUDOTAYIM "'::'"
234-
%token T_INNER_REF "':>'"
235234
%token T_NS_SEPARATOR "'\\'"
236235
%token T_ELLIPSIS "'...'"
237236
%token T_COALESCE "'??'"
@@ -873,16 +872,9 @@ type_expr_without_static:
873872
;
874873

875874
type_without_static:
876-
T_ARRAY { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); }
877-
| T_CALLABLE { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); }
878-
| inner_type_without_static { $$ = $1; }
879-
;
880-
881-
inner_type_without_static:
882-
inner_type_without_static T_INNER_REF name
883-
{ $$ = zend_ast_create(ZEND_AST_INNER_CLASS, $1, $3); }
884-
| name
885-
{ $$ = $1; }
875+
T_ARRAY { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); }
876+
| T_CALLABLE { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); }
877+
| name { $$ = $1; }
886878
;
887879

888880
union_type_without_static_element:
@@ -1431,8 +1423,6 @@ class_name:
14311423
{ zval zv; ZVAL_INTERNED_STR(&zv, ZSTR_KNOWN(ZEND_STR_STATIC));
14321424
$$ = zend_ast_create_zval_ex(&zv, ZEND_NAME_NOT_FQ); }
14331425
| name { $$ = $1; }
1434-
| class_name T_INNER_REF name
1435-
{ $$ = zend_ast_create(ZEND_AST_INNER_CLASS, $1, $3); }
14361426
;
14371427

14381428
class_name_reference:

Zend/zend_language_scanner.l

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,10 +1601,6 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_
16011601
RETURN_TOKEN(T_PAAMAYIM_NEKUDOTAYIM);
16021602
}
16031603

1604-
<ST_IN_SCRIPTING>":>" {
1605-
RETURN_TOKEN(T_INNER_REF);
1606-
}
1607-
16081604
<ST_IN_SCRIPTING>"..." {
16091605
RETURN_TOKEN(T_ELLIPSIS);
16101606
}

ext/reflection/php_reflection.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4079,16 +4079,14 @@ static void add_class_vars(zend_class_entry *ce, bool statics, zval *return_valu
40794079
ZEND_METHOD(ReflectionClass, isInnerClass)
40804080
{
40814081
reflection_object *intern;
4082-
zend_class_entry *ce;
4083-
4084-
ZEND_PARSE_PARAMETERS_NONE();
4082+
zend_class_entry *ce;
40854083

4086-
GET_REFLECTION_OBJECT_PTR(ce);
4084+
ZEND_PARSE_PARAMETERS_NONE();
4085+
GET_REFLECTION_OBJECT_PTR(ce);
40874086

4088-
// If the class is an inner class, it will have a T_INNER_REF in its name
4089-
// todo: make this better?
40904087

4091-
RETURN_BOOL(strstr(ZSTR_VAL(ce->name), ":>") != NULL);
4088+
//inner classes have a lexical scope, normal classes do not
4089+
RETURN_BOOL(ce->lexical_scope != NULL);
40924090
}
40934091
/* }}} */
40944092

ext/tokenizer/tokenizer_data.c

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/tokenizer/tokenizer_data.stub.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -712,11 +712,6 @@
712712
* @cvalue T_PAAMAYIM_NEKUDOTAYIM
713713
*/
714714
const T_PAAMAYIM_NEKUDOTAYIM = UNKNOWN;
715-
/**
716-
* @var int
717-
* @cvalue T_INNER_REF
718-
*/
719-
const T_INNER_REF = UNKNOWN;
720715
/**
721716
* @var int
722717
* @cvalue T_NS_SEPARATOR

ext/tokenizer/tokenizer_data_arginfo.h

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)