From b3a4c6ac5f4c7f468d4e20d33266bffaa026da0f Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Sat, 22 Nov 2014 04:57:55 +0000 Subject: [PATCH 1/3] Removed ZEND_ACC_FINAL_CLASS which is unnecessary. This also fixed some currently defined classes as final which were just not being considered as such before. --- Zend/zend_closures.c | 2 +- Zend/zend_compile.h | 1 - Zend/zend_generators.c | 2 +- Zend/zend_inheritance.c | 2 +- Zend/zend_language_parser.y | 2 +- ext/mysqli/mysqli.c | 4 ++-- ext/pdo/pdo_stmt.c | 2 +- ext/phar/phar_object.c | 4 ++-- ext/reflection/php_reflection.c | 10 +++++----- .../tests/ReflectionClass_getModifiers_basic.phpt | 2 +- .../tests/ReflectionClass_modifiers_001.phpt | 2 +- ext/reflection/tests/ReflectionClass_toString_001.phpt | 2 +- ext/tidy/tidy.c | 2 +- 13 files changed, 18 insertions(+), 19 deletions(-) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index e4827f83b772c..db565929fe7f1 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -444,7 +444,7 @@ void zend_register_closure_ce(TSRMLS_D) /* {{{ */ INIT_CLASS_ENTRY(ce, "Closure", closure_functions); zend_ce_closure = zend_register_internal_class(&ce TSRMLS_CC); - zend_ce_closure->ce_flags |= ZEND_ACC_FINAL_CLASS; + zend_ce_closure->ce_flags |= ZEND_ACC_FINAL; zend_ce_closure->create_object = zend_closure_new; zend_ce_closure->serialize = zend_class_serialize_deny; zend_ce_closure->unserialize = zend_class_unserialize_deny; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index a885518caf78b..b7b9ad097edc4 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -161,7 +161,6 @@ typedef struct _zend_try_catch_element { /* ZEND_ACC_EXPLICIT_ABSTRACT_CLASS denotes that a class was explicitly defined as abstract by using the keyword. */ #define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS 0x10 #define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20 -#define ZEND_ACC_FINAL_CLASS 0x40 #define ZEND_ACC_INTERFACE 0x80 #define ZEND_ACC_TRAIT 0x120 diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 3d95cde44da09..353f40b4fbd3d 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -670,7 +670,7 @@ void zend_register_generator_ce(TSRMLS_D) /* {{{ */ INIT_CLASS_ENTRY(ce, "Generator", generator_functions); zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC); - zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS; + zend_ce_generator->ce_flags |= ZEND_ACC_FINAL; zend_ce_generator->create_object = zend_generator_create; zend_ce_generator->serialize = zend_class_serialize_deny; zend_ce_generator->unserialize = zend_class_unserialize_deny; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 43ef22074faf7..60eed6ccf721b 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -702,7 +702,7 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent && !(parent_ce->ce_flags & ZEND_ACC_INTERFACE)) { zend_error_noreturn(E_COMPILE_ERROR, "Interface %s may not inherit from class (%s)", ce->name->val, parent_ce->name->val); } - if (parent_ce->ce_flags & ZEND_ACC_FINAL_CLASS) { + if (parent_ce->ce_flags & ZEND_ACC_FINAL) { zend_error_noreturn(E_COMPILE_ERROR, "Class %s may not inherit from final class (%s)", ce->name->val, parent_ce->name->val); } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 88286203da953..b293ca80039f1 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -435,7 +435,7 @@ class_declaration_statement: class_type: T_CLASS { $$ = 0; } | T_ABSTRACT T_CLASS { $$ = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } - | T_FINAL T_CLASS { $$ = ZEND_ACC_FINAL_CLASS; } + | T_FINAL T_CLASS { $$ = ZEND_ACC_FINAL; } | T_TRAIT { $$ = ZEND_ACC_TRAIT; } ; diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 878c8fb94677f..6d7210fe592c2 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -625,7 +625,7 @@ PHP_MINIT_FUNCTION(mysqli) zend_declare_property_null(ce, "embedded", sizeof("embedded") - 1, ZEND_ACC_PUBLIC TSRMLS_CC); zend_declare_property_null(ce, "reconnect", sizeof("reconnect") - 1, ZEND_ACC_PUBLIC TSRMLS_CC); zend_declare_property_null(ce, "report_mode", sizeof("report_mode") - 1, ZEND_ACC_PUBLIC TSRMLS_CC); - ce->ce_flags |= ZEND_ACC_FINAL_CLASS; + ce->ce_flags |= ZEND_ACC_FINAL; zend_hash_add_ptr(&classes, ce->name, &mysqli_driver_properties); REGISTER_MYSQLI_CLASS_ENTRY("mysqli", mysqli_link_class_entry, mysqli_link_methods); @@ -655,7 +655,7 @@ PHP_MINIT_FUNCTION(mysqli) REGISTER_MYSQLI_CLASS_ENTRY("mysqli_warning", mysqli_warning_class_entry, mysqli_warning_methods); ce = mysqli_warning_class_entry; - ce->ce_flags |= ZEND_ACC_FINAL_CLASS | ZEND_ACC_PROTECTED; + ce->ce_flags |= ZEND_ACC_FINAL; zend_hash_init(&mysqli_warning_properties, 0, NULL, free_prop_handler, 1); MYSQLI_ADD_PROPERTIES(&mysqli_warning_properties, mysqli_warning_property_entries); zend_declare_property_null(ce, "message", sizeof("message") - 1, ZEND_ACC_PUBLIC TSRMLS_CC); diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 27cc157ed7240..1491c80cfdddc 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2292,7 +2292,7 @@ void pdo_stmt_init(TSRMLS_D) INIT_CLASS_ENTRY(ce, "PDORow", pdo_row_functions); pdo_row_ce = zend_register_internal_class(&ce TSRMLS_CC); - pdo_row_ce->ce_flags |= ZEND_ACC_FINAL_CLASS; /* when removing this a lot of handlers need to be redone */ + pdo_row_ce->ce_flags |= ZEND_ACC_FINAL; /* when removing this a lot of handlers need to be redone */ pdo_row_ce->create_object = pdo_row_new; pdo_row_ce->serialize = pdo_row_serialize; } diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index f82b82563144f..d23c7cd6c6f09 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -5308,11 +5308,11 @@ void phar_object_init(TSRMLS_D) /* {{{ */ #else INIT_CLASS_ENTRY(ce, "Phar", php_archive_methods); phar_ce_archive = zend_register_internal_class(&ce TSRMLS_CC); - phar_ce_archive->ce_flags |= ZEND_ACC_FINAL_CLASS; + phar_ce_archive->ce_flags |= ZEND_ACC_FINAL; INIT_CLASS_ENTRY(ce, "PharData", php_archive_methods); phar_ce_data = zend_register_internal_class(&ce TSRMLS_CC); - phar_ce_data->ce_flags |= ZEND_ACC_FINAL_CLASS; + phar_ce_data->ce_flags |= ZEND_ACC_FINAL; #endif REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "BZ2", PHAR_ENT_COMPRESSED_BZ2) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index e9c59ea86e935..b5a6682a67102 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -395,7 +395,7 @@ static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *in if (ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) { string_printf(str, "abstract "); } - if (ce->ce_flags & ZEND_ACC_FINAL_CLASS) { + if (ce->ce_flags & ZEND_ACC_FINAL) { string_printf(str, "final "); } string_printf(str, "class "); @@ -1547,7 +1547,7 @@ ZEND_METHOD(reflection, getModifierNames) if (modifiers & (ZEND_ACC_ABSTRACT | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) { add_next_index_stringl(return_value, "abstract", sizeof("abstract")-1); } - if (modifiers & (ZEND_ACC_FINAL | ZEND_ACC_FINAL_CLASS)) { + if (modifiers & ZEND_ACC_FINAL) { add_next_index_stringl(return_value, "final", sizeof("final")-1); } if (modifiers & ZEND_ACC_IMPLICIT_PUBLIC) { @@ -4119,7 +4119,7 @@ ZEND_METHOD(reflection_class, isTrait) Returns whether this class is final */ ZEND_METHOD(reflection_class, isFinal) { - _class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_FINAL_CLASS); + _class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_FINAL); } /* }}} */ @@ -4247,7 +4247,7 @@ ZEND_METHOD(reflection_class, newInstanceWithoutConstructor) METHOD_NOTSTATIC(reflection_class_ptr); GET_REFLECTION_OBJECT_PTR(ce); - if (ce->create_object != NULL && ce->ce_flags & ZEND_ACC_FINAL_CLASS) { + if (ce->create_object != NULL && ce->ce_flags & ZEND_ACC_FINAL) { zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, "Class %s is an internal class marked as final that cannot be instantiated without invoking its constructor", ce->name->val); } @@ -6144,7 +6144,7 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_IMPLICIT_ABSTRACT", ZEND_ACC_IMPLICIT_ABSTRACT_CLASS); REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_EXPLICIT_ABSTRACT", ZEND_ACC_EXPLICIT_ABSTRACT_CLASS); - REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_FINAL", ZEND_ACC_FINAL_CLASS); + REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_FINAL", ZEND_ACC_FINAL); INIT_CLASS_ENTRY(_reflection_entry, "ReflectionObject", reflection_object_functions); _reflection_entry.create_object = reflection_objects_new; diff --git a/ext/reflection/tests/ReflectionClass_getModifiers_basic.phpt b/ext/reflection/tests/ReflectionClass_getModifiers_basic.phpt index 65f23c935f91e..8d2bd47d12da1 100644 --- a/ext/reflection/tests/ReflectionClass_getModifiers_basic.phpt +++ b/ext/reflection/tests/ReflectionClass_getModifiers_basic.phpt @@ -30,7 +30,7 @@ dump_modifiers('g'); --EXPECT-- int(0) int(32) -int(64) +int(4) int(128) int(524288) int(524416) diff --git a/ext/reflection/tests/ReflectionClass_modifiers_001.phpt b/ext/reflection/tests/ReflectionClass_modifiers_001.phpt index 941bfe5f2b9f9..1e0a97dfe088d 100644 --- a/ext/reflection/tests/ReflectionClass_modifiers_001.phpt +++ b/ext/reflection/tests/ReflectionClass_modifiers_001.phpt @@ -37,7 +37,7 @@ int(0) bool(true) bool(false) bool(false) -int(64) +int(4) bool(false) bool(true) bool(false) diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index 8dd571c3a9cc2..c47aba8b8d622 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -14,7 +14,7 @@ Class [ class ReflectionClass implements Reflector ] { - Constants [3] { Constant [ integer IS_IMPLICIT_ABSTRACT ] { 16 } Constant [ integer IS_EXPLICIT_ABSTRACT ] { 32 } - Constant [ integer IS_FINAL ] { 64 } + Constant [ integer IS_FINAL ] { 4 } } - Static properties [0] { diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 8ca27d7b9a454..23531e9ae3e9c 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -1037,7 +1037,7 @@ static PHP_MINIT_FUNCTION(tidy) REGISTER_INI_ENTRIES(); REGISTER_TIDY_CLASS(tidy, doc, NULL, 0); - REGISTER_TIDY_CLASS(tidyNode, node, NULL, ZEND_ACC_FINAL_CLASS); + REGISTER_TIDY_CLASS(tidyNode, node, NULL, ZEND_ACC_FINAL); tidy_object_handlers_doc.cast_object = tidy_doc_cast_handler; tidy_object_handlers_node.cast_object = tidy_node_cast_handler; From fa38897139c5d59f51ad6ffd38a40a9bfba7948f Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Mon, 1 Dec 2014 18:44:07 +0000 Subject: [PATCH 2/3] Decoupled class declaration statement into more granular pieces. --- Zend/tests/access_modifiers_003.phpt | 2 +- Zend/tests/access_modifiers_013.phpt | 12 ++++++ Zend/zend_compile.c | 16 ++++++++ Zend/zend_compile.h | 1 + Zend/zend_language_parser.y | 60 ++++++++++++++++++---------- 5 files changed, 70 insertions(+), 21 deletions(-) create mode 100644 Zend/tests/access_modifiers_013.phpt diff --git a/Zend/tests/access_modifiers_003.phpt b/Zend/tests/access_modifiers_003.phpt index dc21278025f4c..2b3a88242f723 100644 --- a/Zend/tests/access_modifiers_003.phpt +++ b/Zend/tests/access_modifiers_003.phpt @@ -10,4 +10,4 @@ final final class test { echo "Done\n"; ?> --EXPECTF-- -Parse error: %s error,%sexpecting %s in %s on line %d +Fatal error: Multiple final modifiers are not allowed in %s on line %d diff --git a/Zend/tests/access_modifiers_013.phpt b/Zend/tests/access_modifiers_013.phpt new file mode 100644 index 0000000000000..f9b72c1f52aa8 --- /dev/null +++ b/Zend/tests/access_modifiers_013.phpt @@ -0,0 +1,12 @@ +--TEST-- +Prevent abstract and final in the same class declaration +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use the final modifier on an abstract class in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5dbb41f94d62e..e35be89165829 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -550,6 +550,22 @@ void zend_do_free(znode *op1 TSRMLS_DC) /* {{{ */ } /* }}} */ +uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */ +{ + uint32_t new_flags = flags | new_flag; + if ((flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) && (new_flag & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) { + zend_error_noreturn(E_COMPILE_ERROR, "Multiple abstract modifiers are not allowed"); + } + if ((flags & ZEND_ACC_FINAL) && (new_flag & ZEND_ACC_FINAL)) { + zend_error_noreturn(E_COMPILE_ERROR, "Multiple final modifiers are not allowed"); + } + if ((new_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) && (new_flags & ZEND_ACC_FINAL)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use the final modifier on an abstract class"); + } + return new_flags; +} +/* }}} */ + uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */ { uint32_t new_flags = flags | new_flag; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index b7b9ad097edc4..99c1e0bdafb54 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -468,6 +468,7 @@ ZEND_API binary_op_type get_binary_op(int opcode); void zend_stop_lexing(TSRMLS_D); void zend_emit_final_return(zval *zv TSRMLS_DC); zend_ast *zend_ast_append_str(zend_ast *left, zend_ast *right); +uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag); uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag); zend_ast *zend_ast_append_doc_comment(zend_ast *list TSRMLS_DC); void zend_handle_encoding_declaration(zend_ast *ast TSRMLS_DC); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index b293ca80039f1..1a8df0a0efbad 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -232,7 +232,9 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_POW_EQUAL "**= (T_POW_EQUAL)" %type top_statement namespace_name name statement function_declaration_statement -%type class_declaration_statement use_declaration const_decl inner_statement +%type class_declaration_statement trait_declaration_statement +%type interface_declaration_statement interface_extends_list +%type use_declaration const_decl inner_statement %type expr optional_expr while_statement for_statement foreach_variable %type foreach_statement declare_statement finally_statement unset_variable variable %type extends_from parameter optional_type argument expr_without_variable global_var @@ -247,15 +249,16 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type top_statement_list use_declarations const_list inner_statement_list if_stmt %type alt_if_stmt for_exprs switch_case_list global_var_list static_var_list %type echo_expr_list unset_variables catch_list parameter_list class_statement_list -%type implements_list interface_extends_list case_list if_stmt_without_else +%type implements_list case_list if_stmt_without_else %type non_empty_parameter_list argument_list non_empty_argument_list property_list %type class_const_list name_list trait_adaptations method_body non_empty_for_exprs %type ctor_arguments alt_if_stmt_without_else trait_adaptation_list lexical_vars %type lexical_var_list encaps_list array_pair_list non_empty_array_pair_list %type assignment_list -%type returns_ref function is_reference is_variadic class_type variable_modifiers +%type returns_ref function is_reference is_variadic variable_modifiers %type method_modifiers trait_modifiers non_empty_member_modifiers member_modifier +%type class_modifiers class_modifier %type backup_doc_comment @@ -282,9 +285,11 @@ name: ; top_statement: - statement { $$ = $1; } - | function_declaration_statement { $$ = $1; } - | class_declaration_statement { $$ = $1; } + statement { $$ = $1; } + | function_declaration_statement { $$ = $1; } + | class_declaration_statement { $$ = $1; } + | trait_declaration_statement { $$ = $1; } + | interface_declaration_statement { $$ = $1; } | T_HALT_COMPILER '(' ')' ';' { $$ = zend_ast_create(ZEND_AST_HALT_COMPILER, zend_ast_create_zval_from_long(zend_get_scanned_file_offset(TSRMLS_C))); @@ -337,8 +342,10 @@ inner_statement_list: inner_statement: statement { $$ = $1; } - | function_declaration_statement { $$ = $1; } - | class_declaration_statement { $$ = $1; } + | function_declaration_statement { $$ = $1; } + | class_declaration_statement { $$ = $1; } + | trait_declaration_statement { $$ = $1; } + | interface_declaration_statement { $$ = $1; } | T_HALT_COMPILER '(' ')' ';' { $$ = NULL; zend_error_noreturn(E_COMPILE_ERROR, "__HALT_COMPILER() can only be used from the outermost scope"); } @@ -422,21 +429,34 @@ is_variadic: ; class_declaration_statement: - class_type { $$ = CG(zend_lineno); } + 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, $2, $6, - zend_ast_get_str($3), $4, $5, $8); } - | T_INTERFACE { $$ = CG(zend_lineno); } - T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, - zend_ast_get_str($3), NULL, $4, $7); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9); } + | 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); } +; + +class_modifiers: + class_modifier { $$ = $1; } + | class_modifiers class_modifier { $$ = zend_add_class_modifier($1, $2); } +; + +class_modifier: + T_ABSTRACT { $$ = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } + | T_FINAL { $$ = ZEND_ACC_FINAL; } ; -class_type: - T_CLASS { $$ = 0; } - | T_ABSTRACT T_CLASS { $$ = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } - | T_FINAL T_CLASS { $$ = ZEND_ACC_FINAL; } - | T_TRAIT { $$ = ZEND_ACC_TRAIT; } +trait_declaration_statement: + T_TRAIT { $$ = CG(zend_lineno); } + T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $6, zend_ast_get_str($3), $4, $5, $8); } +; + +interface_declaration_statement: + T_INTERFACE { $$ = CG(zend_lineno); } + T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, zend_ast_get_str($3), NULL, $4, $7); } ; extends_from: From 3bf4a5bc7493d541ce7c71cd81e6ac40d8b81c7a Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Mon, 1 Dec 2014 22:05:01 +0000 Subject: [PATCH 3/3] Added support for static classes. --- Zend/tests/class_modifiers_001.phpt | 21 ++++++++++ Zend/tests/class_modifiers_002.phpt | 21 ++++++++++ Zend/tests/class_modifiers_003.phpt | 17 ++++++++ Zend/tests/class_modifiers_004.phpt | 16 +++++++ Zend/tests/class_modifiers_005.phpt | 13 ++++++ Zend/tests/class_modifiers_006.phpt | 13 ++++++ Zend/zend_API.c | 15 ++++--- Zend/zend_compile.c | 42 ++++++++++++++++--- Zend/zend_language_parser.y | 1 + Zend/zend_vm_def.h | 4 +- Zend/zend_vm_execute.h | 8 +++- ext/reflection/php_reflection.c | 19 +++++++-- .../ReflectionClass_isCloneable_001.phpt | 8 ++++ .../tests/ReflectionClass_isStatic_basic.phpt | 18 ++++++++ .../tests/ReflectionClass_toString_001.phpt | 11 ++++- 15 files changed, 208 insertions(+), 19 deletions(-) create mode 100644 Zend/tests/class_modifiers_001.phpt create mode 100644 Zend/tests/class_modifiers_002.phpt create mode 100644 Zend/tests/class_modifiers_003.phpt create mode 100644 Zend/tests/class_modifiers_004.phpt create mode 100644 Zend/tests/class_modifiers_005.phpt create mode 100644 Zend/tests/class_modifiers_006.phpt create mode 100644 ext/reflection/tests/ReflectionClass_isStatic_basic.phpt diff --git a/Zend/tests/class_modifiers_001.phpt b/Zend/tests/class_modifiers_001.phpt new file mode 100644 index 0000000000000..e13d2a2fdc176 --- /dev/null +++ b/Zend/tests/class_modifiers_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +Check if static classes are supported +--FILE-- + +--EXPECTF-- +foo +Hello Foo + +Fatal error: Cannot instantiate static class %s in %s on line %d diff --git a/Zend/tests/class_modifiers_002.phpt b/Zend/tests/class_modifiers_002.phpt new file mode 100644 index 0000000000000..03989d6bb4469 --- /dev/null +++ b/Zend/tests/class_modifiers_002.phpt @@ -0,0 +1,21 @@ +--TEST-- +Check if final static classes are supported +--FILE-- + +--EXPECTF-- +Fatal error: Class %s may not inherit from final class (%s) in %s on line %d \ No newline at end of file diff --git a/Zend/tests/class_modifiers_003.phpt b/Zend/tests/class_modifiers_003.phpt new file mode 100644 index 0000000000000..7a1e13c4517e9 --- /dev/null +++ b/Zend/tests/class_modifiers_003.phpt @@ -0,0 +1,17 @@ +--TEST-- +Check if abstract static classes generates fatal error +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use the static modifier on an abstract class in %s on line %d \ No newline at end of file diff --git a/Zend/tests/class_modifiers_004.phpt b/Zend/tests/class_modifiers_004.phpt new file mode 100644 index 0000000000000..0bb5534c99f17 --- /dev/null +++ b/Zend/tests/class_modifiers_004.phpt @@ -0,0 +1,16 @@ +--TEST-- +Check if static classes enforces static methods +--FILE-- + +--EXPECTF-- +Fatal error: Class %s contains non-static method (%s) and therefore cannot be declared 'static' in %s on line %d \ No newline at end of file diff --git a/Zend/tests/class_modifiers_005.phpt b/Zend/tests/class_modifiers_005.phpt new file mode 100644 index 0000000000000..bd1d3605dafb6 --- /dev/null +++ b/Zend/tests/class_modifiers_005.phpt @@ -0,0 +1,13 @@ +--TEST-- +Check if static classes enforces static properties +--FILE-- + +--EXPECTF-- +Fatal error: Class %s contains non-static property declaration and therefore cannot be declared 'static' in %s on line %d diff --git a/Zend/tests/class_modifiers_006.phpt b/Zend/tests/class_modifiers_006.phpt new file mode 100644 index 0000000000000..edbc5288a6a40 --- /dev/null +++ b/Zend/tests/class_modifiers_006.phpt @@ -0,0 +1,13 @@ +--TEST-- +Check if static classes prevents abstract static methods +--FILE-- + +--EXPECTF-- +Fatal error: Class %s contains abstract method (%s) and therefore cannot be declared 'static' in %s on line %d diff --git a/Zend/zend_API.c b/Zend/zend_API.c index e95276d4c80a2..9894b7b6d2aa6 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1296,11 +1296,16 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties * calling zend_merge_properties(). */ ZEND_API int _object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties ZEND_FILE_LINE_DC TSRMLS_DC) /* {{{ */ { - if (class_type->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) { - char *what = (class_type->ce_flags & ZEND_ACC_INTERFACE) ? "interface" - :((class_type->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) ? "trait" - : "abstract class"; - zend_error(E_ERROR, "Cannot instantiate %s %s", what, class_type->name->val); + if (class_type->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_STATIC|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) { + if (class_type->ce_flags & ZEND_ACC_INTERFACE) { + zend_error_noreturn(E_ERROR, "Cannot instantiate interface %s", class_type->name->val); + } else if (class_type->ce_flags & ZEND_ACC_STATIC) { + zend_error_noreturn(E_ERROR, "Cannot instantiate static class %s", class_type->name->val); + } else if ((class_type->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { + zend_error_noreturn(E_ERROR, "Cannot instantiate trait %s", class_type->name->val); + } else { + zend_error_noreturn(E_ERROR, "Cannot instantiate abstract class %s", class_type->name->val); + } } zend_update_class_constants(class_type TSRMLS_CC); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e35be89165829..c988757e933c9 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -562,6 +562,9 @@ uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */ if ((new_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) && (new_flags & ZEND_ACC_FINAL)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use the final modifier on an abstract class"); } + if ((new_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) && (new_flags & ZEND_ACC_STATIC)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use the static modifier on an abstract class"); + } return new_flags; } /* }}} */ @@ -3938,24 +3941,40 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo { zend_class_entry *ce = CG(active_class_entry); zend_bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0; + zend_bool in_static = (ce->ce_flags & ZEND_ACC_STATIC) != 0; zend_bool in_trait = ZEND_CE_IS_TRAIT(ce); + zend_bool is_abstract = (op_array->fn_flags & ZEND_ACC_ABSTRACT) != 0; zend_bool is_public = (op_array->fn_flags & ZEND_ACC_PUBLIC) != 0; zend_bool is_static = (op_array->fn_flags & ZEND_ACC_STATIC) != 0; zend_string *lcname; + if (in_static) { + if (is_abstract) { + zend_error_noreturn(E_COMPILE_ERROR, "Class %s contains abstract method (%s) and " + "therefore cannot be declared 'static'", ce->name->val, name->val); + } + + if (!is_static) { + zend_error_noreturn(E_COMPILE_ERROR, "Class %s contains non-static method (%s) and " + "therefore cannot be declared 'static'", ce->name->val, name->val); + } + } + if (in_interface) { if ((op_array->fn_flags & ZEND_ACC_PPP_MASK) != ZEND_ACC_PUBLIC) { zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface method " "%s::%s() must be omitted", ce->name->val, name->val); } + op_array->fn_flags |= ZEND_ACC_ABSTRACT; - } else if (is_static && (op_array->fn_flags & ZEND_ACC_ABSTRACT)) { + is_abstract = (op_array->fn_flags & ZEND_ACC_ABSTRACT) != 0; + } else if (is_static && is_abstract) { zend_error(E_STRICT, "Static function %s::%s() should not be abstract", ce->name->val, name->val); } - if (op_array->fn_flags & ZEND_ACC_ABSTRACT) { + if (is_abstract) { if (op_array->fn_flags & ZEND_ACC_PRIVATE) { zend_error_noreturn(E_COMPILE_ERROR, "%s function %s::%s() cannot be declared private", in_interface ? "Interface" : "Abstract", ce->name->val, name->val); @@ -4231,17 +4250,28 @@ void zend_compile_func_decl(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ void zend_compile_prop_decl(zend_ast *ast TSRMLS_DC) /* {{{ */ { + zend_class_entry *ce = CG(active_class_entry); zend_ast_list *list = zend_ast_get_list(ast); uint32_t flags = list->attr; - zend_class_entry *ce = CG(active_class_entry); uint32_t i, children = list->children; zend_string *doc_comment = NULL; - if (ce->ce_flags & ZEND_ACC_INTERFACE) { + zend_bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0; + zend_bool in_static = (ce->ce_flags & ZEND_ACC_STATIC) != 0; + zend_bool is_abstract = (flags & ZEND_ACC_ABSTRACT) != 0; + zend_bool is_final = (flags & ZEND_ACC_FINAL) != 0; + zend_bool is_static = (flags & ZEND_ACC_STATIC) != 0; + + if (in_interface) { zend_error_noreturn(E_COMPILE_ERROR, "Interfaces may not include member variables"); } - if (flags & ZEND_ACC_ABSTRACT) { + if (in_static && !is_static) { + zend_error_noreturn(E_COMPILE_ERROR, "Class %s contains non-static property declaration " + "and therefore cannot be declared 'static'", ce->name->val); + } + + if (is_abstract) { zend_error_noreturn(E_COMPILE_ERROR, "Properties cannot be declared abstract"); } @@ -4258,7 +4288,7 @@ void zend_compile_prop_decl(zend_ast *ast TSRMLS_DC) /* {{{ */ zend_string *name = zend_ast_get_str(name_ast); zval value_zv; - if (flags & ZEND_ACC_FINAL) { + if (is_final) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, " "the final modifier is allowed only for methods and classes", ce->name->val, name->val); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 1a8df0a0efbad..f12800688d2fc 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -445,6 +445,7 @@ class_modifiers: class_modifier: T_ABSTRACT { $$ = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } | T_FINAL { $$ = ZEND_ACC_FINAL; } + | T_STATIC { $$ = ZEND_ACC_STATIC; } ; trait_declaration_statement: diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b64eb6b3cbe41..821c9b00df96f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3691,9 +3691,11 @@ ZEND_VM_HANDLER(68, ZEND_NEW, CONST|VAR, ANY) } else { ce = Z_CE_P(EX_VAR(opline->op1.var)); } - if (UNEXPECTED((ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) != 0)) { + if (UNEXPECTED((ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_STATIC|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) != 0)) { if (ce->ce_flags & ZEND_ACC_INTERFACE) { zend_error_noreturn(E_ERROR, "Cannot instantiate interface %s", ce->name->val); + } else if (ce->ce_flags & ZEND_ACC_STATIC) { + zend_error_noreturn(E_ERROR, "Cannot instantiate static class %s", ce->name->val); } else if ((ce->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { zend_error_noreturn(E_ERROR, "Cannot instantiate trait %s", ce->name->val); } else { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index f42d1fa69d66c..54aec6e425986 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2742,9 +2742,11 @@ static int ZEND_FASTCALL ZEND_NEW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } else { ce = Z_CE_P(EX_VAR(opline->op1.var)); } - if (UNEXPECTED((ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) != 0)) { + if (UNEXPECTED((ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_STATIC|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) != 0)) { if (ce->ce_flags & ZEND_ACC_INTERFACE) { zend_error_noreturn(E_ERROR, "Cannot instantiate interface %s", ce->name->val); + } else if (ce->ce_flags & ZEND_ACC_STATIC) { + zend_error_noreturn(E_ERROR, "Cannot instantiate static class %s", ce->name->val); } else if ((ce->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { zend_error_noreturn(E_ERROR, "Cannot instantiate trait %s", ce->name->val); } else { @@ -16700,9 +16702,11 @@ static int ZEND_FASTCALL ZEND_NEW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } else { ce = Z_CE_P(EX_VAR(opline->op1.var)); } - if (UNEXPECTED((ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) != 0)) { + if (UNEXPECTED((ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_STATIC|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) != 0)) { if (ce->ce_flags & ZEND_ACC_INTERFACE) { zend_error_noreturn(E_ERROR, "Cannot instantiate interface %s", ce->name->val); + } else if (ce->ce_flags & ZEND_ACC_STATIC) { + zend_error_noreturn(E_ERROR, "Cannot instantiate static class %s", ce->name->val); } else if ((ce->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { zend_error_noreturn(E_ERROR, "Cannot instantiate trait %s", ce->name->val); } else { diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index b5a6682a67102..18a8c2db205f2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -398,6 +398,9 @@ static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *in if (ce->ce_flags & ZEND_ACC_FINAL) { string_printf(str, "final "); } + if (ce->ce_flags & ZEND_ACC_STATIC) { + string_printf(str, "static "); + } string_printf(str, "class "); } string_printf(str, "%s", ce->name->val); @@ -4052,7 +4055,7 @@ ZEND_METHOD(reflection_class, isInstantiable) return; } GET_REFLECTION_OBJECT_PTR(ce); - if (ce->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_IMPLICIT_ABSTRACT_CLASS)) { + if (ce->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_STATIC | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_IMPLICIT_ABSTRACT_CLASS)) { RETURN_FALSE; } @@ -4078,7 +4081,7 @@ ZEND_METHOD(reflection_class, isCloneable) return; } GET_REFLECTION_OBJECT_PTR(ce); - if (ce->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_IMPLICIT_ABSTRACT_CLASS)) { + if (ce->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_STATIC | ZEND_ACC_TRAIT | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_IMPLICIT_ABSTRACT_CLASS)) { RETURN_FALSE; } if (!Z_ISUNDEF(intern->obj)) { @@ -4123,6 +4126,14 @@ ZEND_METHOD(reflection_class, isFinal) } /* }}} */ +/* {{{ proto public bool ReflectionClass::isStatic() + Returns whether this class is final */ +ZEND_METHOD(reflection_class, isStatic) +{ + _class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_STATIC); +} +/* }}} */ + /* {{{ proto public bool ReflectionClass::isAbstract() Returns whether this class is abstract */ ZEND_METHOD(reflection_class, isAbstract) @@ -5897,6 +5908,7 @@ static const zend_function_entry reflection_class_functions[] = { ZEND_ME(reflection_class, isTrait, arginfo_reflection__void, 0) ZEND_ME(reflection_class, isAbstract, arginfo_reflection__void, 0) ZEND_ME(reflection_class, isFinal, arginfo_reflection__void, 0) + ZEND_ME(reflection_class, isStatic, arginfo_reflection__void, 0) ZEND_ME(reflection_class, getModifiers, arginfo_reflection__void, 0) ZEND_ME(reflection_class, isInstance, arginfo_reflection_class_isInstance, 0) ZEND_ME(reflection_class, newInstance, arginfo_reflection_class_newInstance, 0) @@ -6145,7 +6157,8 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_IMPLICIT_ABSTRACT", ZEND_ACC_IMPLICIT_ABSTRACT_CLASS); REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_EXPLICIT_ABSTRACT", ZEND_ACC_EXPLICIT_ABSTRACT_CLASS); REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_FINAL", ZEND_ACC_FINAL); - + REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_STATIC", ZEND_ACC_STATIC); + INIT_CLASS_ENTRY(_reflection_entry, "ReflectionObject", reflection_object_functions); _reflection_entry.create_object = reflection_objects_new; reflection_object_ptr = zend_register_internal_class_ex(&_reflection_entry, reflection_class_ptr TSRMLS_CC); diff --git a/ext/reflection/tests/ReflectionClass_isCloneable_001.phpt b/ext/reflection/tests/ReflectionClass_isCloneable_001.phpt index f1042cc130ffe..8cbb3581c1a07 100644 --- a/ext/reflection/tests/ReflectionClass_isCloneable_001.phpt +++ b/ext/reflection/tests/ReflectionClass_isCloneable_001.phpt @@ -28,6 +28,12 @@ $obj = new ReflectionObject($bar); var_dump($obj->isCloneable()); $h = clone $foo; +static class woo {} + +print "User static class\n"; +$obj = new ReflectionClass('woo'); +var_dump($obj->isCloneable()); + print "Closure\n"; $closure = function () { }; $obj = new ReflectionClass($closure); @@ -58,6 +64,8 @@ bool(true) User class - private __clone bool(false) bool(false) +User static class +bool(false) Closure bool(true) bool(true) diff --git a/ext/reflection/tests/ReflectionClass_isStatic_basic.phpt b/ext/reflection/tests/ReflectionClass_isStatic_basic.phpt new file mode 100644 index 0000000000000..d064296b43cda --- /dev/null +++ b/ext/reflection/tests/ReflectionClass_isStatic_basic.phpt @@ -0,0 +1,18 @@ +--TEST-- +ReflectionClass::isStatic() method +--FILE-- +isStatic()); +var_dump($staticClass->isStatic()); + +?> +--EXPECT-- +bool(false) +bool(true) diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index c47aba8b8d622..f3501da99f51f 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -11,10 +11,11 @@ echo $rc; --EXPECTF-- Class [ class ReflectionClass implements Reflector ] { - - Constants [3] { + - Constants [4] { Constant [ integer IS_IMPLICIT_ABSTRACT ] { 16 } Constant [ integer IS_EXPLICIT_ABSTRACT ] { 32 } Constant [ integer IS_FINAL ] { 4 } + Constant [ integer IS_STATIC ] { 1 } } - Static properties [0] { @@ -34,7 +35,7 @@ Class [ class ReflectionClass implements Reflector ] { Property [ public $name ] } - - Methods [49] { + - Methods [50] { Method [ final private method __clone ] { - Parameters [0] { @@ -230,6 +231,12 @@ Class [ class ReflectionClass implements Reflector ] { } } + Method [ public method isStatic ] { + + - Parameters [0] { + } + } + Method [ public method getModifiers ] { - Parameters [0] {