Skip to content

Commit 0b94cf6

Browse files
phpGH-16067: prevent invalid abstract during compilation of methods (phpGH-16069)
For classes that are not declared `abstract`, produce a compiler error for any `abstract` methods. For anonymous classes, since they cannot be made abstract, the error message is slightly different. Co-authored-by: Ilija Tovilo <[email protected]>
1 parent e64e531 commit 0b94cf6

10 files changed

+64
-10
lines changed

Zend/tests/abstract_implicit.phpt

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Abstract methods not allowed in classes that are not abstract (GH-16067)
3+
--FILE--
4+
<?php
5+
6+
// Still allowed via trait
7+
trait TraitWithAbstract {
8+
abstract public function foo();
9+
}
10+
class TraitWorks {
11+
use TraitWithAbstract;
12+
}
13+
14+
class NotAbstract {
15+
abstract public function bar();
16+
}
17+
?>
18+
--EXPECTF--
19+
Fatal error: Class NotAbstract declares abstract method bar() and must therefore be declared abstract in %s on line %d

Zend/tests/anon/gh16067.phpt

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Compiler prevents explicit `abstract` methods on anonymous classes
3+
--FILE--
4+
<?php
5+
6+
$c = new class {
7+
abstract public function f();
8+
}
9+
?>
10+
--EXPECTF--
11+
Fatal error: Anonymous class method f() must not be abstract in %s on line 4

Zend/tests/enum/no-abstract.phpt

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Compiler prevents `abstract` methods on enums classes (GH-16067)
3+
--FILE--
4+
<?php
5+
6+
enum Example {
7+
abstract public function foo();
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Enum method Example::foo() must not be abstract in %s on line 4

Zend/tests/errmsg/errmsg_018.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ class test {
1010
echo "Done\n";
1111
?>
1212
--EXPECTF--
13-
Fatal error: Class test contains 1 abstract method and must therefore be declared abstract or implement the remaining method (test::foo) in %s on line %d
13+
Fatal error: Class test declares abstract method foo() and must therefore be declared abstract in %s on line %d

Zend/zend_compile.c

+16-4
Original file line numberDiff line numberDiff line change
@@ -8067,6 +8067,22 @@ static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string
80678067
zend_error(E_COMPILE_WARNING, "Private methods cannot be final as they are never overridden by other classes");
80688068
}
80698069

8070+
if ((fn_flags & ZEND_ACC_ABSTRACT)
8071+
&& !(ce->ce_flags & (ZEND_ACC_EXPLICIT_ABSTRACT_CLASS|ZEND_ACC_TRAIT))) {
8072+
// Don't say that the class should be declared abstract if it is
8073+
// anonymous or an enum and can't be abstract
8074+
if (ce->ce_flags & ZEND_ACC_ANON_CLASS) {
8075+
zend_error_noreturn(E_COMPILE_ERROR, "Anonymous class method %s() must not be abstract",
8076+
ZSTR_VAL(name));
8077+
} else if (ce->ce_flags & (ZEND_ACC_ENUM|ZEND_ACC_INTERFACE)) {
8078+
zend_error_noreturn(E_COMPILE_ERROR, "%s method %s::%s() must not be abstract",
8079+
zend_get_object_type_case(ce, true), ZSTR_VAL(ce->name), ZSTR_VAL(name));
8080+
} else {
8081+
zend_error_noreturn(E_COMPILE_ERROR, "Class %s declares abstract method %s() and must therefore be declared abstract",
8082+
ZSTR_VAL(ce->name), ZSTR_VAL(name));
8083+
}
8084+
}
8085+
80708086
if (in_interface) {
80718087
if (!(fn_flags & ZEND_ACC_PUBLIC)) {
80728088
zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface method "
@@ -8076,10 +8092,6 @@ static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string
80768092
zend_error_noreturn(E_COMPILE_ERROR, "Interface method "
80778093
"%s::%s() must not be final", ZSTR_VAL(ce->name), ZSTR_VAL(name));
80788094
}
8079-
if (fn_flags & ZEND_ACC_ABSTRACT) {
8080-
zend_error_noreturn(E_COMPILE_ERROR, "Interface method "
8081-
"%s::%s() must not be abstract", ZSTR_VAL(ce->name), ZSTR_VAL(name));
8082-
}
80838095
op_array->fn_flags |= ZEND_ACC_ABSTRACT;
80848096
}
80858097

tests/classes/abstract_derived.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ class derived extends base {
1313
?>
1414
===DONE===
1515
--EXPECTF--
16-
Fatal error: Class derived contains 1 abstract method and must therefore be declared abstract or implement the remaining method (derived::show) in %sabstract_derived.php on line %d
16+
Fatal error: Class derived declares abstract method show() and must therefore be declared abstract in %sabstract_derived.php on line %d

tests/classes/abstract_not_declared.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ class fail {
1010
echo "Done\n"; // shouldn't be displayed
1111
?>
1212
--EXPECTF--
13-
Fatal error: Class fail contains 1 abstract method and must therefore be declared abstract or implement the remaining method (fail::show) in %s on line %d
13+
Fatal error: Class fail declares abstract method show() and must therefore be declared abstract in %s on line %d

tests/classes/abstract_redeclare.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ class fail extends pass {
1616
echo "Done\n"; // Shouldn't be displayed
1717
?>
1818
--EXPECTF--
19-
Fatal error: Class fail contains 1 abstract method and must therefore be declared abstract or implement the remaining method (fail::show) in %sabstract_redeclare.php on line %d
19+
Fatal error: Class fail declares abstract method show() and must therefore be declared abstract in %sabstract_redeclare.php on line %d

tests/classes/abstract_static.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ echo "Done\n"; // shouldn't be displayed
3131
--EXPECTF--
3232
Call to function show()
3333

34-
Fatal error: Class fail contains 1 abstract method and must therefore be declared abstract or implement the remaining method (fail::func) in %sabstract_static.php(%d) : eval()'d code on line %d
34+
Fatal error: Class fail declares abstract method func() and must therefore be declared abstract in %sabstract_static.php(%d) : eval()'d code on line %d

tests/classes/interface_method_private.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ ZE2 An interface method cannot be private
44
<?php
55

66
interface if_a {
7-
abstract private function err();
7+
private function err();
88
}
99

1010
?>

0 commit comments

Comments
 (0)