Skip to content

GH-16067: prevent invalid abstract during compilation of methods #16069

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Zend/tests/abstract_implicit.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
Abstract methods not allowed in classes that are not abstract (GH-16067)
--FILE--
<?php

// Still allowed via trait
trait TraitWithAbstract {
abstract public function foo();
}
class TraitWorks {
use TraitWithAbstract;
}

class NotAbstract {
abstract public function bar();
}
?>
--EXPECTF--
Fatal error: Class NotAbstract declares abstract method bar() and must therefore be declared abstract in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/anon/gh16067.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Compiler prevents explicit `abstract` methods on anonymous classes
--FILE--
<?php

$c = new class {
abstract public function f();
}
?>
--EXPECTF--
Fatal error: Anonymous class method f() must not be abstract in %s on line 4
12 changes: 12 additions & 0 deletions Zend/tests/enum/no-abstract.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Compiler prevents `abstract` methods on enums classes (GH-16067)
--FILE--
<?php

enum Example {
abstract public function foo();
}

?>
--EXPECTF--
Fatal error: Enum method Example::foo() must not be abstract in %s on line 4
2 changes: 1 addition & 1 deletion Zend/tests/errmsg/errmsg_018.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ class test {
echo "Done\n";
?>
--EXPECTF--
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
Fatal error: Class test declares abstract method foo() and must therefore be declared abstract in %s on line %d
20 changes: 16 additions & 4 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -8067,6 +8067,22 @@ static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string
zend_error(E_COMPILE_WARNING, "Private methods cannot be final as they are never overridden by other classes");
}

if ((fn_flags & ZEND_ACC_ABSTRACT)
&& !(ce->ce_flags & (ZEND_ACC_EXPLICIT_ABSTRACT_CLASS|ZEND_ACC_TRAIT))) {
// Don't say that the class should be declared abstract if it is
// anonymous or an enum and can't be abstract
if (ce->ce_flags & ZEND_ACC_ANON_CLASS) {
zend_error_noreturn(E_COMPILE_ERROR, "Anonymous class method %s() must not be abstract",
ZSTR_VAL(name));
} else if (ce->ce_flags & (ZEND_ACC_ENUM|ZEND_ACC_INTERFACE)) {
zend_error_noreturn(E_COMPILE_ERROR, "%s method %s::%s() must not be abstract",
zend_get_object_type_case(ce, true), ZSTR_VAL(ce->name), ZSTR_VAL(name));
} else {
zend_error_noreturn(E_COMPILE_ERROR, "Class %s declares abstract method %s() and must therefore be declared abstract",
ZSTR_VAL(ce->name), ZSTR_VAL(name));
}
}

if (in_interface) {
if (!(fn_flags & ZEND_ACC_PUBLIC)) {
zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface method "
Expand All @@ -8076,10 +8092,6 @@ static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string
zend_error_noreturn(E_COMPILE_ERROR, "Interface method "
"%s::%s() must not be final", ZSTR_VAL(ce->name), ZSTR_VAL(name));
}
if (fn_flags & ZEND_ACC_ABSTRACT) {
zend_error_noreturn(E_COMPILE_ERROR, "Interface method "
"%s::%s() must not be abstract", ZSTR_VAL(ce->name), ZSTR_VAL(name));
}
op_array->fn_flags |= ZEND_ACC_ABSTRACT;
}

Expand Down
2 changes: 1 addition & 1 deletion tests/classes/abstract_derived.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ class derived extends base {
?>
===DONE===
--EXPECTF--
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
Fatal error: Class derived declares abstract method show() and must therefore be declared abstract in %sabstract_derived.php on line %d
2 changes: 1 addition & 1 deletion tests/classes/abstract_not_declared.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ class fail {
echo "Done\n"; // shouldn't be displayed
?>
--EXPECTF--
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
Fatal error: Class fail declares abstract method show() and must therefore be declared abstract in %s on line %d
2 changes: 1 addition & 1 deletion tests/classes/abstract_redeclare.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ class fail extends pass {
echo "Done\n"; // Shouldn't be displayed
?>
--EXPECTF--
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
Fatal error: Class fail declares abstract method show() and must therefore be declared abstract in %sabstract_redeclare.php on line %d
2 changes: 1 addition & 1 deletion tests/classes/abstract_static.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ echo "Done\n"; // shouldn't be displayed
--EXPECTF--
Call to function show()

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
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
2 changes: 1 addition & 1 deletion tests/classes/interface_method_private.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ZE2 An interface method cannot be private
<?php

interface if_a {
abstract private function err();
private function err();
}

?>
Expand Down
Loading