diff --git a/Zend/tests/ctor/error-ctor-duplicate.phpt b/Zend/tests/ctor/error-ctor-duplicate.phpt new file mode 100644 index 0000000000000..86a1b11961438 --- /dev/null +++ b/Zend/tests/ctor/error-ctor-duplicate.phpt @@ -0,0 +1,14 @@ +--TEST-- +Disallow duplicate short and full ctors +--FILE-- + +--EXPECTF-- +Fatal error: Cannot redeclare DTO::__construct() in %s on line %d diff --git a/Zend/tests/ctor/no-ctor-simple.phpt b/Zend/tests/ctor/no-ctor-simple.phpt new file mode 100644 index 0000000000000..bee2a04ce6f7b --- /dev/null +++ b/Zend/tests/ctor/no-ctor-simple.phpt @@ -0,0 +1,9 @@ +--TEST-- +Class does not contain any statements +--FILE-- + +--EXPECTF-- diff --git a/Zend/tests/ctor/short-ctor-ast-printer.phpt b/Zend/tests/ctor/short-ctor-ast-printer.phpt new file mode 100644 index 0000000000000..4dddf8c5fb376 --- /dev/null +++ b/Zend/tests/ctor/short-ctor-ast-printer.phpt @@ -0,0 +1,23 @@ +--TEST-- +Pass short ctor parameters +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +assert(false && function () { + class Empty0 { + public function __construct() { + } + + } + +}) \ No newline at end of file diff --git a/Zend/tests/ctor/short-ctor-ast-printer2.phpt b/Zend/tests/ctor/short-ctor-ast-printer2.phpt new file mode 100644 index 0000000000000..5f0c3c01d54e0 --- /dev/null +++ b/Zend/tests/ctor/short-ctor-ast-printer2.phpt @@ -0,0 +1,27 @@ +--TEST-- +Pass short ctor parameters +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +assert(false && function () { + class DTO { + public function __construct(public int $number) { + } + + } + +}) \ No newline at end of file diff --git a/Zend/tests/ctor/short-ctor-ast-printer3.phpt b/Zend/tests/ctor/short-ctor-ast-printer3.phpt new file mode 100644 index 0000000000000..ad7bb6c473251 --- /dev/null +++ b/Zend/tests/ctor/short-ctor-ast-printer3.phpt @@ -0,0 +1,42 @@ +--TEST-- +Pass short ctor parameters +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +assert(false && function () { + abstract class Family { + public function __construct($string) { + } + + } + + class Child1 extends Family { + public function __construct($string) { + parent::__construct(); + } + + } + + class Child2 extends Family { + public function __construct($string) { + parent::__construct(555); + } + + } + +}) \ No newline at end of file diff --git a/Zend/tests/ctor/short-ctor-ast-printer4.phpt b/Zend/tests/ctor/short-ctor-ast-printer4.phpt new file mode 100644 index 0000000000000..1cd02143750fe --- /dev/null +++ b/Zend/tests/ctor/short-ctor-ast-printer4.phpt @@ -0,0 +1,35 @@ +--TEST-- +Pass short ctor parameters +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +assert(false && function () { + abstract class Family { + public function __construct(protected $protected) { + } + + } + + class Child1 extends Family { + public function __construct(public $string) { + parent::__construct($string); + } + + } + +}) \ No newline at end of file diff --git a/Zend/tests/ctor/short-ctor-const.phpt b/Zend/tests/ctor/short-ctor-const.phpt new file mode 100644 index 0000000000000..d55bced408710 --- /dev/null +++ b/Zend/tests/ctor/short-ctor-const.phpt @@ -0,0 +1,18 @@ +--TEST-- +Pass short ctor parameters +--FILE-- + +--EXPECTF-- +object(Child1)#1 (1) { + ["string":protected]=> + int(123) +} \ No newline at end of file diff --git a/Zend/tests/ctor/short-ctor-extends.phpt b/Zend/tests/ctor/short-ctor-extends.phpt new file mode 100644 index 0000000000000..d128eef78e8c3 --- /dev/null +++ b/Zend/tests/ctor/short-ctor-extends.phpt @@ -0,0 +1,16 @@ +--TEST-- +Pass short ctor parameters +--FILE-- + +--EXPECTF-- +object(Child1)#1 (1) { + ["string":protected]=> + string(4) "test" +} \ No newline at end of file diff --git a/Zend/tests/ctor/short-ctor-prop.phpt b/Zend/tests/ctor/short-ctor-prop.phpt new file mode 100644 index 0000000000000..165e99451332a --- /dev/null +++ b/Zend/tests/ctor/short-ctor-prop.phpt @@ -0,0 +1,20 @@ +--TEST-- +Pass short ctor parameters +--FILE-- +internal_string) { + private $internal_string = "internal"; +} + +var_dump(new Child1("test")); +?> +--EXPECTF-- +object(Child1)#1 (2) { + ["string":protected]=> + string(8) "internal" + ["internal_string":"Child1":private]=> + string(8) "internal" +} \ No newline at end of file diff --git a/Zend/tests/ctor/short-ctor-simple.phpt b/Zend/tests/ctor/short-ctor-simple.phpt new file mode 100644 index 0000000000000..8a849496bda48 --- /dev/null +++ b/Zend/tests/ctor/short-ctor-simple.phpt @@ -0,0 +1,28 @@ +--TEST-- +Pass short ctor parameters +--FILE-- + +--EXPECTF-- +object(Empty0)#1 (0) { +} +object(DTO)#1 (1) { + ["number"]=> + int(50) +} +object(Value)#1 (0) { +} \ No newline at end of file diff --git a/Zend/tests/grammar/regression_010.phpt b/Zend/tests/grammar/regression_010.phpt index 0e535aad6b87f..41cc363022465 100644 --- a/Zend/tests/grammar/regression_010.phpt +++ b/Zend/tests/grammar/regression_010.phpt @@ -12,4 +12,4 @@ echo "Done", PHP_EOL; ?> --EXPECTF-- -Parse error: syntax error, unexpected namespaced name "implements\A", expecting "{" in %s on line %d +Parse error: syntax error, unexpected namespaced name "implements\A", expecting ";" or "{" in %s on line %d diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 805f378cb983c..9b0354072c9bb 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -36,6 +36,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); #define YYMALLOC malloc #define YYFREE free #endif +void* temp; } %code requires { @@ -252,7 +253,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_ERROR %type top_statement namespace_name name statement function_declaration_statement -%type class_declaration_statement trait_declaration_statement legacy_namespace_name +%type class_declaration_statement class_body_statement parent_ctr_call class_short_ctor trait_declaration_statement legacy_namespace_name %type interface_declaration_statement interface_extends_list %type group_use_declaration inline_use_declarations inline_use_declaration %type mixed_group_use_declaration use_declaration unprefixed_use_declaration @@ -291,7 +292,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %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 -%type class_modifiers class_modifier anonymous_class_modifiers anonymous_class_modifiers_optional use_type backup_fn_flags +%type class_modifiers class_modifiers_optional class_modifier anonymous_class_modifiers anonymous_class_modifiers_optional use_type backup_fn_flags %type backup_lex_pos %type backup_doc_comment @@ -602,12 +603,44 @@ 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_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); } + class_modifiers_optional T_CLASS { $$ = CG(zend_lineno); } T_STRING + class_short_ctor + extends_from implements_list backup_doc_comment class_body_statement + { + zend_ast_decl *ctor = (zend_ast_decl *)$5; + if (ctor && ctor->child[2] && temp) { + ctor->child[2] = zend_ast_list_add(ctor->child[2], temp); + } + zend_ast* stmts = zend_ast_create_list(2, ZEND_AST_STMT_LIST, $5, $9); + $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $8, zend_ast_get_str($4), $6, $7, stmts, NULL, NULL); } +; + +class_short_ctor: + '(' parameter_list ')' + { $$ = zend_ast_create_decl(ZEND_AST_METHOD, ZEND_ACC_PUBLIC, CG(zend_lineno), NULL, + ZSTR_KNOWN(ZEND_STR_CTOR), $2, NULL, zend_ast_create_list(0, ZEND_AST_STMT_LIST), NULL, NULL); } + | %empty { $$ = NULL; } +; + +class_body_statement: + '{' class_statement_list '}' { $$ = $2; } + | ';' { $$ = NULL; } +; + +parent_ctr_call: + argument_list + { + zval zv; ZVAL_INTERNED_STR(&zv, ZSTR_KNOWN(ZEND_STR_PARENT)); + $$ = zend_ast_create(ZEND_AST_STATIC_CALL, + zend_ast_create_zval_ex(&zv, ZEND_NAME_NOT_FQ), + zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CTOR)), + $1); } + | %empty { $$ = NULL; } +; + +class_modifiers_optional: + %empty { $$ = 0; } + | class_modifiers { $$ = $1; } ; class_modifiers: @@ -669,7 +702,7 @@ enum_case_expr: extends_from: %empty { $$ = NULL; } - | T_EXTENDS class_name { $$ = $2; } + | T_EXTENDS class_name parent_ctr_call { $$ = $2; temp = $3; } ; interface_extends_list: diff --git a/Zend/zend_string.h b/Zend/zend_string.h index f60e4dec4e71f..168bed2b71199 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -588,6 +588,7 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_VALUE, "value") \ _(ZEND_STR_KEY, "key") \ _(ZEND_STR_MAGIC_INVOKE, "__invoke") \ + _(ZEND_STR_CTOR, "__construct") \ _(ZEND_STR_PREVIOUS, "previous") \ _(ZEND_STR_CODE, "code") \ _(ZEND_STR_MESSAGE, "message") \