diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000000..13566b81b018a --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/customTargets.xml b/.idea/customTargets.xml new file mode 100644 index 0000000000000..d1c7a7505d836 --- /dev/null +++ b/.idea/customTargets.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000000000..626ca2200383c --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,247 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000000..e7747caa47a8d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php-src.iml b/.idea/php-src.iml new file mode 100644 index 0000000000000..bc2cd87409057 --- /dev/null +++ b/.idea/php-src.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000..35eb1ddfbbc02 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000..9afeb9e49ddfe --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM ubuntu:noble + +RUN apt update && apt install -y pkg-config build-essential autoconf bison re2c \ + libxml2-dev libsqlite3-dev cmake gdb +WORKDIR /app + +CMD while true; do sleep 3600; done diff --git a/Dockerfile.php b/Dockerfile.php new file mode 100644 index 0000000000000..bcccb66b9b83f --- /dev/null +++ b/Dockerfile.php @@ -0,0 +1,8 @@ +FROM ubuntu:noble + +RUN apt update && apt install -y pkg-config build-essential autoconf bison re2c \ + libxml2-dev libsqlite3-dev cmake gdb +WORKDIR /app + +COPY ./sapi/cli/php php +RUN chmod +x /app/php \ No newline at end of file diff --git a/LE.md b/LE.md new file mode 100644 index 0000000000000..179c1986bd82a --- /dev/null +++ b/LE.md @@ -0,0 +1,154 @@ +# Generics syntax + +``` +interface Comparable { + +} + +interface Equatable { + +} + +int implements Comparable, Equatable; + +class Foo { + + class Bar { + + } + +} + +enum FooBar { + + case Blum(int bam); + case Bam(T boom); + +} + +``` + +## Generic classes and interfaces + +### Basic case +```lhp +class GenericTestClass { + // T can be used as type alias inside of 'GenericTestClass' + public function __construct( + private T $property + ) {} + + public function getMyT(): T { + return $this->property; + } + + public function setMyT(T $newValue) { + $this->property = $newValue; + } +} +``` + +### Multiple Generics +```lehp +class GenericTestClass { + // T and U can now be used as type aliases inside of 'GenericTestClass' + + public function __construct( + private T $first, + private U $second + ) {} + + public function getFirst(): T { + return $this->first; + } + + public function setFirst(T $value) { + $this->first = $value; + } + + public function getSecond(): U { + return $this->second; + } + + public function setSecond(U $value) { + $this->second = $value; + } +} +``` + +### Bounded Gerics +```lehp +interface Serializable { + public function serialize(): string; +} + +class BoundedGenericClass { + public function __construct( + private T $serializable + ) {} + + public function getSerialized(): string + { + return $this->serializable->serialize(); + } + + public function getAsSerializable(): Serializable + { + return (Serializable) $this->serializable; // cast optional + } + + public function getObject(): T + { + return $this->serializable; + } +} +``` + +### A little bit more complex +``` +interface Serializer { + public function serialize(T $object): string; +} + +class RandomAssSerializableContaier> { + + private array $content = []; + + public function __construct( + private K serializer + ) {} + + public function add(T $toAdd) + { + $content[] = $toAdd; + } + + public function asSerializedList(): array + { + return array_map( + fn ($obj) $this->serializer->serialize($obj), + $this->content + ); + } +} +``` +## Functions +### Basic case +``` +function noOp(T $in): T +{ + return $in; +} +``` + +### A bit more complex +``` +interface Serializer { + public function serialize(T $object): string; +} + +function serialize>(T $in, K $serializer): string +{ + return $serializer->serialize($in); +} +``` \ No newline at end of file diff --git a/Zend/zend.h b/Zend/zend.h index 0cf1faeb653fe..dc3d41244363a 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -144,8 +144,17 @@ struct _zend_inheritance_cache_entry { zend_class_entry *traits_and_interfaces[1]; }; +typedef struct _zend_type_parameter { + struct _zend_generic_type *elements; + size_t element_count; + zend_string *name; +} zend_type_parameter; + struct _zend_class_entry { + // TYPE HERE char type; + size_t generic_type_count; + zend_type_parameter *generic_type; zend_string *name; /* class_entry or string depending on ZEND_ACC_LINKED */ union { diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 9348c35f6cc07..c3b09a7ddb1ac 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -70,6 +70,7 @@ enum _zend_ast_kind { ZEND_AST_ATTRIBUTE_GROUP, ZEND_AST_MATCH_ARM_LIST, ZEND_AST_MODIFIER_LIST, + ZEND_AST_GENERIC_TYPE_PARAM_LIST, /* 0 child nodes */ ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -155,6 +156,8 @@ enum _zend_ast_kind { ZEND_AST_NAMED_ARG, ZEND_AST_PARENT_PROPERTY_HOOK_CALL, + ZEND_AST_GENERIC_TYPE, + /* 3 child nodes */ ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_NULLSAFE_METHOD_CALL, diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 8e4221673c4cf..f0a999a30e32b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9043,6 +9043,53 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_ zend_type_release(type, 0); } +// LEHP-RELEVANT +// compiles generic class params from the AST to the zend_class_entry +static void zend_compile_class_generic_params(zend_class_entry *class_entry, zend_ast *ast) { + ZEND_ASSERT(ast->kind == ZEND_AST_GENERIC_TYPE_PARAM_LIST); + // we know this is a list as it has GENERIC_LIST_TYPE + zend_ast_list *generic_param_list = zend_ast_get_list(ast); + + class_entry->generic_type_count = generic_param_list->children; + class_entry->generic_type = ecalloc(generic_param_list->children, sizeof(zend_type_parameter)); + for (uint32_t i = 0; i < generic_param_list->children; i++) { + zend_ast *param_ast = generic_param_list->child[i]; + zend_type_parameter *type_param = &class_entry->generic_type[i]; + + if (param_ast->kind == ZEND_AST_ZVAL) { + // Simple type parameter T + zend_ast_zval *zval_ast = (zend_ast_zval*)param_ast; + if (Z_TYPE(zval_ast->val) != IS_STRING) { + zend_error_noreturn(E_COMPILE_ERROR, "Generic type parameter name must be a string"); + } + type_param->name = zend_string_copy(Z_STR(zval_ast->val)); + type_param->element_count = 0; + type_param->elements = NULL; + zend_resolve_const_class_name_reference(zval_ast, "class name"); + } else if (param_ast->kind == ZEND_AST_GENERIC_TYPE) { + // Type parameter with constraints like T:string + if (param_ast->child[0]->kind != ZEND_AST_ZVAL) { + zend_error_noreturn(E_COMPILE_ERROR, "Generic type parameter name must be a simple identifier"); + } + + zend_ast_zval *name_ast = (zend_ast_zval*)param_ast->child[0]; + if (Z_TYPE(name_ast->val) != IS_STRING) { + zend_error_noreturn(E_COMPILE_ERROR, "Generic type parameter name must be a string"); + } + + type_param->name = zend_string_copy(Z_STR(name_ast->val)); + + // LEHP-TODO add constraints to model + type_param->element_count = 0; + type_param->elements = NULL; + + // TODO: In the future, process constraint list from param_ast->child[1] + } else { + zend_error_noreturn(E_COMPILE_ERROR, "Invalid generic type parameter"); + } + } +} + static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */ { zend_ast_decl *decl = (zend_ast_decl *) ast; @@ -9134,7 +9181,12 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) CG(active_class_entry) = ce; if (decl->child[3]) { - zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0); + if(decl->child[3]->kind == ZEND_AST_GENERIC_TYPE_PARAM_LIST){ + zend_compile_class_generic_params(ce, decl->child[3]); + } else { + // LEHP-TODO this should never happen, attributes are never passed as third child to zend_ast_create_decl(ZEND_AST_CLASS, ...)??? + zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0); + } } if (implements_ast) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 224a68be749cb..ddd71a56751b5 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -483,9 +483,11 @@ typedef struct _zend_internal_arg_info { } zend_internal_arg_info; /* arg_info for user functions */ +//LEHP-RELEVANT typedef struct _zend_arg_info { zend_string *name; zend_type type; + //LEHP-ASSUMPTION: LEPHP code evaluated at runtime zend_string *default_value; } zend_arg_info; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 9483a83b4e955..bbdd70f186041 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -237,6 +237,11 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_COALESCE "'??'" %token T_POW "'**'" %token T_POW_EQUAL "'**='" + +// Generics +%token T_GENERIC_START "'<'" +%token T_GENERIC_END "'>'" + /* We need to split the & token in two to avoid a shift/reduce conflict. For T1&$v and T1&T2, * with only one token lookahead, bison does not know whether to reduce T1 as a complete type, * or shift to continue parsing an intersection type. */ @@ -286,6 +291,16 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type function_name non_empty_member_modifiers %type property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body %type optional_parameter_list +%type nested_generic_type_parameter_list +%type nested_generic_type_parameters +%type generic_type +%type non_empty_generic_type_parameters +%type generic_type_with_constraint +%type generic_type_parameter_list_with_constraints +%type class_or_fuction_decl_generic_type_parameters +%type simple_generic_type +%type simple_generic_type_parameter_list +%type non_empty_simple_generic_type_parameters %type returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers @@ -597,11 +612,65 @@ is_variadic: class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL, NULL); } + T_STRING class_or_fuction_decl_generic_type_parameters extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $8, zend_ast_get_str($4), $6, $7, $10, $5, NULL); } | T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL, NULL); } + T_STRING class_or_fuction_decl_generic_type_parameters extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $7, zend_ast_get_str($3), $5, $6, $9, $4, NULL); } +; + +// only support e.g. "" +non_empty_simple_generic_type_parameters: + T_GENERIC_START simple_generic_type_parameter_list T_GENERIC_END { $$ = $2; } +; + +// only support e.g. "SomeType" +simple_generic_type: + T_STRING { $$ = zend_ast_create_zval_from_str(zend_ast_get_str($1)); } + | T_STRING non_empty_simple_generic_type_parameters { $$ = zend_ast_create(ZEND_AST_GENERIC_TYPE, zend_ast_create_zval_from_str(zend_ast_get_str($1)), $2); } +; + +// only support e.g. "T, S, A" +simple_generic_type_parameter_list: + T_STRING { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_PARAM_LIST, $1); } + | generic_type_parameter_list_with_constraints ',' T_STRING { $$ = zend_ast_list_add($1, $3); } +; + +// supports bounded types, e.g. "" +class_or_fuction_decl_generic_type_parameters: + T_GENERIC_START generic_type_parameter_list_with_constraints T_GENERIC_END { $$ = $2; } + | %empty { $$ = NULL; } +; + +// supports bounded types, e.g. "T : SomeInterface, S : SomeClass" +generic_type_parameter_list_with_constraints: + generic_type_with_constraint { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_PARAM_LIST, $1); } + | generic_type_parameter_list_with_constraints ',' generic_type_with_constraint { $$ = zend_ast_list_add($1, $3); } +; + +// supports bounded types, e.g. "T : SomeInterface" +generic_type_with_constraint: + T_STRING { $$ = zend_ast_create_zval_from_str(zend_ast_get_str($1)); } + | T_STRING ':' simple_generic_type { $$ = zend_ast_create(ZEND_AST_GENERIC_TYPE, zend_ast_create_zval_from_str(zend_ast_get_str($1)), $3); } +; + +nested_generic_type_parameters: + T_GENERIC_START nested_generic_type_parameter_list T_GENERIC_END { $$ = $2; } + | %empty { $$ = NULL; } +; + +nested_generic_type_parameter_list: + generic_type { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_PARAM_LIST, $1); } + | nested_generic_type_parameter_list ',' generic_type { $$ = zend_ast_list_add($1, $3); } +; + +non_empty_generic_type_parameters: + T_GENERIC_START nested_generic_type_parameter_list T_GENERIC_END { $$ = $2; } +; + +generic_type: + T_STRING { $$ = zend_ast_create_zval_from_str(zend_ast_get_str($1)); } + | T_STRING non_empty_generic_type_parameters { $$ = zend_ast_create(ZEND_AST_GENERIC_TYPE, zend_ast_create_zval_from_str(zend_ast_get_str($1)), $2); } ; class_modifiers: @@ -865,10 +934,11 @@ type_expr_without_static: | intersection_type_without_static { $$ = $1; } ; +//TODO add generic type params to AST type_without_static: - T_ARRAY { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); } + T_ARRAY nested_generic_type_parameters { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); } | T_CALLABLE { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); } - | name { $$ = $1; } + | name nested_generic_type_parameters { $$ = $1; } ; union_type_without_static_element: diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 4c883b81c5f7d..aa3381a8a580e 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1395,6 +1395,7 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ } "function" { + yy_push_state(ST_IN_FUNC_DEF); RETURN_TOKEN_WITH_IDENT(T_FUNCTION); } @@ -1537,10 +1538,35 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN_WITH_IDENT(T_PRINT); } + "<" { + yy_push_state(ST_IN_GENERIC); + RETURN_TOKEN(T_GENERIC_START); +} + + ">" { + yy_pop_state(); + RETURN_TOKEN(T_GENERIC_END); +} + +{LABEL} { + RETURN_TOKEN_WITH_STR(T_STRING, 0); +} + + "class" { + yy_push_state(ST_IN_CLASS_DEF); RETURN_TOKEN_WITH_IDENT(T_CLASS); } + + +"{" { + yy_pop_state(); + yy_push_state(ST_IN_SCRIPTING); + enter_nesting('{'); + RETURN_TOKEN('{'); +} + "interface" { RETURN_TOKEN_WITH_IDENT(T_INTERFACE); } @@ -1562,11 +1588,11 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN_WITH_IDENT(T_ENUM); } -"extends" { +"extends" { RETURN_TOKEN_WITH_IDENT(T_EXTENDS); } -"implements" { +"implements" { RETURN_TOKEN_WITH_IDENT(T_IMPLEMENTS); } @@ -1580,7 +1606,7 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN(T_NULLSAFE_OBJECT_OPERATOR); } -{WHITESPACE}+ { +{WHITESPACE}+ { goto return_whitespace; } @@ -1722,9 +1748,14 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ } "private" { + yy_push_state(ST_IN_PROPERTY_TYPE_DEF); RETURN_TOKEN_WITH_IDENT(T_PRIVATE); } + { + +} + "protected" { RETURN_TOKEN_WITH_IDENT(T_PROTECTED); } @@ -1908,6 +1939,16 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN(yytext[0]); } +"(" { + yy_pop_state(); + enter_nesting(yytext[0]); + RETURN_TOKEN(yytext[0]); +} + +":"|"," { + RETURN_TOKEN(yytext[0]); +} + {TOKENS} { RETURN_TOKEN(yytext[0]); } @@ -2394,7 +2435,7 @@ inline_char_handler: RETURN_TOKEN(T_NS_SEPARATOR); } -{LABEL} { +{LABEL} { RETURN_TOKEN_WITH_STR(T_STRING, 0); } diff --git a/Zend/zend_types.h b/Zend/zend_types.h index f839cec3b3667..72b814d9f5d88 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -142,6 +142,7 @@ typedef struct { zend_type types[1]; } zend_type_list; +//LEHP-RELEVANT new generic type #define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25 #define _ZEND_TYPE_MASK ((1u << 25) - 1) /* Only one of these bits may be set. */ @@ -553,11 +554,20 @@ typedef struct _HashTableIterator { uint32_t next_copy; // circular linked list via index into EG(ht_iterators) } HashTableIterator; +typedef struct _zend_generic_value { + struct _zend_generic_value *elements; + size_t element_count; + zend_string *name; +} zend_generic_value; + struct _zend_object { zend_refcounted_h gc; uint32_t handle; // TODO: may be removed ??? uint32_t extra_flags; /* OBJ_EXTRA_FLAGS() */ zend_class_entry *ce; + //LEHP-OPTIMIZATION: COPY THIS FROM _zend_class_entry + size_t generic_value_count; + zend_generic_value *generic_values; const zend_object_handlers *handlers; HashTable *properties; zval properties_table[1]; diff --git a/dev.sh b/dev.sh new file mode 100755 index 0000000000000..5d4d2bdd4577f --- /dev/null +++ b/dev.sh @@ -0,0 +1,2 @@ +docker build -t php-dev-setup . +docker run --volume .:/app php-dev-setup diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index a1e131032bcfb..e8a4d3006aa4f 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -173,6 +173,8 @@ char *get_token_type_name(int token_type) case T_COALESCE: return "T_COALESCE"; case T_POW: return "T_POW"; case T_POW_EQUAL: return "T_POW_EQUAL"; + case T_GENERIC_START: return "T_GENERIC_START"; + case T_GENERIC_END: return "T_GENERIC_END"; case T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG: return "T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG"; case T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG: return "T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG"; case T_BAD_CHARACTER: return "T_BAD_CHARACTER"; diff --git a/ext/tokenizer/tokenizer_data.stub.php b/ext/tokenizer/tokenizer_data.stub.php index c1e1fd254dfaa..fe7321e9cd40d 100644 --- a/ext/tokenizer/tokenizer_data.stub.php +++ b/ext/tokenizer/tokenizer_data.stub.php @@ -742,6 +742,16 @@ * @cvalue T_POW_EQUAL */ const T_POW_EQUAL = UNKNOWN; +/** + * @var int + * @cvalue T_GENERIC_START + */ +const T_GENERIC_START = UNKNOWN; +/** + * @var int + * @cvalue T_GENERIC_END + */ +const T_GENERIC_END = UNKNOWN; /** * @var int * @cvalue T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG diff --git a/ext/tokenizer/tokenizer_data_arginfo.h b/ext/tokenizer/tokenizer_data_arginfo.h index 9c488d19f1890..6ac43d4a612b8 100644 --- a/ext/tokenizer/tokenizer_data_arginfo.h +++ b/ext/tokenizer/tokenizer_data_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 19d25d22098f46283b517352cbb302db962b50fd */ + * Stub hash: 69f8ad0852d9f99a17d98192dfc54fd8961d3d0a */ static void register_tokenizer_data_symbols(int module_number) { @@ -151,6 +151,8 @@ static void register_tokenizer_data_symbols(int module_number) REGISTER_LONG_CONSTANT("T_COALESCE", T_COALESCE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_POW", T_POW, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_POW_EQUAL", T_POW_EQUAL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_GENERIC_START", T_GENERIC_START, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_GENERIC_END", T_GENERIC_END, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG", T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG", T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_BAD_CHARACTER", T_BAD_CHARACTER, CONST_PERSISTENT); diff --git a/le-test/simple-class.php b/le-test/simple-class.php new file mode 100644 index 0000000000000..73423ce0166f2 --- /dev/null +++ b/le-test/simple-class.php @@ -0,0 +1,16 @@ + { + // T can be used as type alias inside of 'GenericTestClass' + public function __construct( + private T $property + ) {} + + public function getMyT(): T { + return $this->property; + } + + public function setMyT(T $newValue) { + $this->property = $newValue; + } +} \ No newline at end of file diff --git a/le-test/simple-function.php b/le-test/simple-function.php new file mode 100644 index 0000000000000..307fa2d5f8d03 --- /dev/null +++ b/le-test/simple-function.php @@ -0,0 +1,3 @@ +(): void {} \ No newline at end of file diff --git a/t.php b/t.php new file mode 100644 index 0000000000000..93668b01e91a6 --- /dev/null +++ b/t.php @@ -0,0 +1,25 @@ +> { + + private array $content = []; + + public function __construct( + private K $serializer + ) {} + + public function add(T $toAdd) + { + $content[] = $toAdd; + } + + public function asSerializedList(): array + { + return array_map( + fn ($obj) => $this->serializer->serialize($obj), + $this->content + ); + } +} + +echo "hi\n"; \ No newline at end of file diff --git a/t2.php b/t2.php new file mode 100644 index 0000000000000..520a1c4557105 --- /dev/null +++ b/t2.php @@ -0,0 +1,9 @@ +{ + public function __construct( + public K $lol +){ + } +} + +echo "hi\n"; diff --git a/tree.php b/tree.php new file mode 100644 index 0000000000000..934f17b7dd468 --- /dev/null +++ b/tree.php @@ -0,0 +1,10 @@ + { + public Serializable $test; + function __construct() {} +} +'); + +var_export($classTokens); \ No newline at end of file