diff --git a/Zend/tests/class_modifiers_001.phpt b/Zend/tests/class_modifiers_001.phpt index 27ef428154b7d..106ab600ef39b 100644 --- a/Zend/tests/class_modifiers_001.phpt +++ b/Zend/tests/class_modifiers_001.phpt @@ -7,18 +7,22 @@ Check if final abstract classes are supported final abstract class A { + public static $c = "foo\n"; + public static function display($name) { echo "Hello $name\n"; } } +echo A::$c; A::display('Foo'); $a = new A; ?> --EXPECTF-- +foo Hello Foo Fatal error: Cannot instantiate abstract class %s in %s on line %d \ No newline at end of file diff --git a/Zend/tests/class_modifiers_002.phpt b/Zend/tests/class_modifiers_002.phpt index 4de36eef2da4d..d1c1cfb5edcf8 100644 --- a/Zend/tests/class_modifiers_002.phpt +++ b/Zend/tests/class_modifiers_002.phpt @@ -1,5 +1,5 @@ --TEST-- -Check if final abstract classes are final indeed +Check if final abstract classes are indeed final --SKIPIF-- --FILE-- @@ -7,6 +7,8 @@ Check if final abstract classes are final indeed final abstract class A { + public static $c = "foo\n"; + public static function display($name) { echo "Hello $name\n"; diff --git a/Zend/tests/class_modifiers_003.phpt b/Zend/tests/class_modifiers_003.phpt new file mode 100644 index 0000000000000..7e83f80e10a85 --- /dev/null +++ b/Zend/tests/class_modifiers_003.phpt @@ -0,0 +1,18 @@ +--TEST-- +Check if final abstract classes enforces static methods +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Class %s contains non-static method (%s) and therefore cannot be declared 'abstract final' 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..f00e2d14f9aea --- /dev/null +++ b/Zend/tests/class_modifiers_004.phpt @@ -0,0 +1,15 @@ +--TEST-- +Check if final abstract classes enforces static properties +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Class %s contains non-static property declaration and therefore cannot be declared 'abstract final' 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..e9d7f62dba8c9 --- /dev/null +++ b/Zend/tests/class_modifiers_005.phpt @@ -0,0 +1,15 @@ +--TEST-- +Check if final abstract classes prevents abstract static methods +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Class %s contains abstract static method (%s) and therefore cannot be declared 'abstract final' in %s on line %d \ No newline at end of file diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index aa4b6950705b1..a6d7f9691a4fe 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3935,24 +3935,41 @@ 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_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0; + zend_bool in_final = (ce->ce_flags & ZEND_ACC_FINAL) != 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_abstract && in_final) { + if (is_abstract) { + zend_error_noreturn(E_COMPILE_ERROR, "Class %s contains abstract static method (%s) and " + "therefore cannot be declared 'abstract final'", 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 'abstract final'", 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); @@ -4228,17 +4245,30 @@ 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_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0; + zend_bool in_final = (ce->ce_flags & ZEND_ACC_FINAL) != 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; + + // Fail if abstract final class' properties are not static + if (in_abstract && in_final && !is_static) { + zend_error_noreturn(E_COMPILE_ERROR, "Class %s contains non-static property declaration " + "and therefore cannot be declared 'abstract final'", ce->name->val); + } + + if (in_interface) { zend_error_noreturn(E_COMPILE_ERROR, "Interfaces may not include member variables"); } - if (flags & ZEND_ACC_ABSTRACT) { + if (is_abstract) { zend_error_noreturn(E_COMPILE_ERROR, "Properties cannot be declared abstract"); } @@ -4255,7 +4285,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);