Deprecate partially supported callables

This deprecates all callables that are accepted by
call_user_func($callable) but not by $callable(). In particular:

    "self::method"
    "parent::method"
    "static::method"
    ["self", "method"]
    ["parent", "method"]
    ["static", "method"]
    ["Foo", "Bar::method"]
    [new Foo, "Bar::method"]

RFC: https://wiki.php.net/rfc/deprecate_partially_supported_callables

Closes GH-7446.
This commit is contained in:
Nikita Popov 2021-09-01 16:54:17 +02:00
parent 1afe89f8c3
commit ee510eed68
27 changed files with 249 additions and 32 deletions

View file

@ -35,6 +35,26 @@ PHP 8.2 UPGRADE NOTES
4. Deprecated Functionality
========================================
- Core:
. Callables that are not accepted by the $callable() syntax (but are accepted
by call_user_func) are deprecated. In particular:
"self::method"
"parent::method"
"static::method"
["self", "method"]
["parent", "method"]
["static", "method"]
["Foo", "Bar::method"]
[new Foo, "Bar::method"]
This does not affect normal method callables like "A::method" or
["A", "method"]. A deprecation notice is only emitted on call. Both
is_callable() and the callable type will silently accept these callables
until support for them is removed entirely.
RFC: https://wiki.php.net/rfc/deprecate_partially_supported_callables
========================================
5. Changed Functions
========================================

View file

@ -14,7 +14,11 @@ st::e ();
st::e2 ();
stch::g ();
?>
--EXPECT--
--EXPECTF--
EHLO
Deprecated: Use of "self" in callables is deprecated in %s on line %d
EHLO
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
EHLO

View file

@ -20,6 +20,7 @@ try_class::main ();
echo "Done\n";
?>
--EXPECT--
--EXPECTF--
Deprecated: Use of "self" in callables is deprecated in %s on line %d
Done
CHECKPOINT

View file

@ -38,7 +38,7 @@ try {
}
?>
--EXPECT--
--EXPECTF--
__call:
string(3) "ABC"
__call:
@ -47,8 +47,12 @@ __call:
string(3) "xyz"
__call:
string(3) "www"
Deprecated: Use of "self" in callables is deprecated in %s on line %d
__call:
string(1) "y"
Deprecated: Use of "self" in callables is deprecated in %s on line %d
__call:
string(1) "y"
ok

View file

@ -38,7 +38,7 @@ try {
}
?>
--EXPECT--
--EXPECTF--
__call:
string(3) "ABC"
__call:
@ -47,8 +47,12 @@ __call:
string(3) "xyz"
__call:
string(3) "www"
Deprecated: Use of "self" in callables is deprecated in %s on line %d
__call:
string(1) "y"
Deprecated: Use of "self" in callables is deprecated in %s on line %d
__call:
string(1) "y"
ok

View file

@ -30,5 +30,6 @@ $c = new C;
$c->callFuncInParent('Which function will be called??');
?>
--EXPECT--
--EXPECTF--
Deprecated: Callables of the form ["C", "parent::func"] are deprecated in %s on line %d
B::func called

View file

@ -53,8 +53,15 @@ $c = new C;
$c->func('This should work!');
?>
--EXPECT--
--EXPECTF--
Deprecated: Callables of the form ["C", "parent::func2"] are deprecated in %s on line %d
string(27) "B::func2: This should work!"
Deprecated: Callables of the form ["C", "parent::func3"] are deprecated in %s on line %d
string(27) "B::func3: This should work!"
Deprecated: Callables of the form ["C", "parent::func22"] are deprecated in %s on line %d
call_user_func_array(): Argument #1 ($callback) must be a valid callback, cannot access private method B::func22()
Deprecated: Callables of the form ["C", "parent::inexistent"] are deprecated in %s on line %d
call_user_func_array(): Argument #1 ($callback) must be a valid callback, class B does not have a method "inexistent"

View file

@ -44,7 +44,12 @@ $c = new C;
$c->func('This should work!');
?>
--EXPECT--
--EXPECTF--
Deprecated: Callables of the form ["C", "self::func2"] are deprecated in %s on line %d
string(27) "B::func2: This should work!"
Deprecated: Callables of the form ["C", "self::func3"] are deprecated in %s on line %d
string(27) "B::func3: This should work!"
Deprecated: Callables of the form ["C", "self::inexistent"] are deprecated in %s on line %d
call_user_func_array(): Argument #1 ($callback) must be a valid callback, class C does not have a method "inexistent"

View file

@ -34,10 +34,14 @@ call_user_func(array(B::class, 'parent::who'));
C::test();
?>
--EXPECT--
--EXPECTF--
string(1) "B"
string(1) "A"
Deprecated: Callables of the form ["B", "parent::who"] are deprecated in %s on line %d
string(1) "A"
string(1) "B"
string(1) "A"
Deprecated: Callables of the form ["B", "parent::who"] are deprecated in %s on line %d
string(1) "A"

View file

@ -19,5 +19,6 @@ class Test {
?>
===DONE===
--EXPECT--
--EXPECTF--
Deprecated: Use of "self" in callables is deprecated in %s on line %d
===DONE===

View file

@ -30,5 +30,9 @@ $b = new B;
$b->x();
?>
--EXPECT--
aaa
--EXPECTF--
a
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
a
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
a

View file

@ -0,0 +1,69 @@
--TEST--
Deprecation of self/parent/static in callables
--FILE--
<?php
class A {
public function foo() {}
}
class B extends A {
public function test() {
// Different callables using self/parent/static
echo "Test different callables\n";
call_user_func("self::foo");
call_user_func("parent::foo");
call_user_func("static::foo");
call_user_func(["self", "foo"]);
call_user_func(["parent", "foo"]);
call_user_func(["static", "foo"]);
call_user_func(["B", "self::foo"]);
call_user_func(["B", "parent::foo"]);
call_user_func(["B", "static::foo"]);
call_user_func(["B", "A::foo"]);
// Also applies to other things performing calls
echo "Test array_map()\n";
array_map("self::foo", [1]);
echo "Test is_callable() -- should be silent\n";
var_dump(is_callable("self::foo"));
echo "Test callable type hint -- should be silent\n";
$this->callableTypeHint("self::foo");
}
public function callableTypeHint(callable $c) {}
}
$b = new B;
$b->test();
?>
--EXPECTF--
Test different callables
Deprecated: Use of "self" in callables is deprecated in %s on line %d
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
Deprecated: Use of "static" in callables is deprecated in %s on line %d
Deprecated: Use of "self" in callables is deprecated in %s on line %d
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
Deprecated: Use of "static" in callables is deprecated in %s on line %d
Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d
Test array_map()
Deprecated: Use of "self" in callables is deprecated in %s on line %d
Test is_callable() -- should be silent
bool(true)
Test callable type hint -- should be silent

View file

@ -96,7 +96,7 @@ echo $fn(" OK".PHP_EOL);
?>
--EXPECT--
--EXPECTF--
Access public static function OK
Access public static function with different case OK
Access public static function with colon scheme OK
@ -111,9 +111,17 @@ Instance return private static method as callable OK
Instance return protected static method as callable OK
Subclass closure over parent class protected method OK
Subclass closure over parent class static protected method OK
Access public instance method of parent object through "parent::" OK
Access public instance method of self object through "self::" OK
Access public instance method of parent object through "self::" to parent method OK
Access protected instance method of parent object through "self::" to parent method OK
Access public instance method of parent object through "parent::"
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
OK
Access public instance method of self object through "self::"
Deprecated: Use of "self" in callables is deprecated in %s on line %d
OK
Access public instance method of parent object through "self::" to parent method
Deprecated: Use of "self" in callables is deprecated in %s on line %d
OK
Access protected instance method of parent object through "self::" to parent method
Deprecated: Use of "self" in callables is deprecated in %s on line %d
OK
MagicCall __call instance method __call,nonExistentMethod, OK
MagicCall __callStatic static method __callStatic,nonExistentMethod, OK

View file

@ -193,7 +193,7 @@ catch (\Throwable $t) {
echo "OK\n";
?>
--EXPECT--
--EXPECTF--
Cannot access privateInstance method statically
Cannot access privateInstance method statically with colon scheme
Cannot access privateInstance method
@ -209,4 +209,6 @@ Subclass cannot closure over parant private static method
Function scope cannot closure over protected instance method
Function scope cannot closure over private instance method
Access private instance method of parent object through "self::" to parent method
Deprecated: Use of "self" in callables is deprecated in %s on line %d
OK

View file

@ -19,5 +19,6 @@ class Test2 extends Test1 {
}
Test2::test();
?>
--EXPECT--
--EXPECTF--
Deprecated: Use of "static" in callables is deprecated in %s on line %d
ok

View file

@ -19,5 +19,6 @@ class Test2 extends Test1 {
}
Test2::test();
?>
--EXPECT--
--EXPECTF--
Deprecated: Use of "static" in callables is deprecated in %s on line %d
ok

View file

@ -39,12 +39,20 @@ C::testForward();
C::testNoForward();
?>
--EXPECT--
C
C
--EXPECTF--
C
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
C
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
C
C
Deprecated: Use of "self" in callables is deprecated in %s on line %d
C
Deprecated: Use of "self" in callables is deprecated in %s on line %d
C
A
A

View file

@ -24,9 +24,24 @@ class B extends A {
}
B::foo();
?>
--EXPECT--
--EXPECTF--
Deprecated: Use of "static" in callables is deprecated in %s on line %d
B
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
Deprecated: Use of "static" in callables is deprecated in %s on line %d
B
Deprecated: Use of "static" in callables is deprecated in %s on line %d
B
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
Deprecated: Use of "static" in callables is deprecated in %s on line %d
B
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
Deprecated: Use of "static" in callables is deprecated in %s on line %d
B

View file

@ -3291,7 +3291,7 @@ static zend_always_inline zend_class_entry *get_scope(zend_execute_data *frame)
return frame && frame->func ? frame->func->common.scope : NULL;
}
static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *scope, zend_execute_data *frame, zend_fcall_info_cache *fcc, bool *strict_class, char **error) /* {{{ */
static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *scope, zend_execute_data *frame, zend_fcall_info_cache *fcc, bool *strict_class, char **error, bool suppress_deprecation) /* {{{ */
{
bool ret = 0;
zend_class_entry *ce;
@ -3307,6 +3307,9 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
if (!scope) {
if (error) *error = estrdup("cannot access \"self\" when no class scope is active");
} else {
if (error && !suppress_deprecation) {
zend_error(E_DEPRECATED, "Use of \"self\" in callables is deprecated");
}
fcc->called_scope = zend_get_called_scope(frame);
if (!fcc->called_scope || !instanceof_function(fcc->called_scope, scope)) {
fcc->called_scope = scope;
@ -3323,6 +3326,9 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
} else if (!scope->parent) {
if (error) *error = estrdup("cannot access \"parent\" when current class scope has no parent");
} else {
if (error && !suppress_deprecation) {
zend_error(E_DEPRECATED, "Use of \"parent\" in callables is deprecated");
}
fcc->called_scope = zend_get_called_scope(frame);
if (!fcc->called_scope || !instanceof_function(fcc->called_scope, scope->parent)) {
fcc->called_scope = scope->parent;
@ -3340,6 +3346,9 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
if (!called_scope) {
if (error) *error = estrdup("cannot access \"static\" when no class scope is active");
} else {
if (error && !suppress_deprecation) {
zend_error(E_DEPRECATED, "Use of \"static\" in callables is deprecated");
}
fcc->called_scope = called_scope;
fcc->calling_scope = called_scope;
if (!fcc->object) {
@ -3472,7 +3481,7 @@ static zend_always_inline bool zend_is_callable_check_func(int check_flags, zval
fcc->called_scope = fcc->object ? fcc->object->ce : fcc->calling_scope;
}
strict_class = 1;
} else if (!zend_is_callable_check_class(cname, scope, frame, fcc, &strict_class, error)) {
} else if (!zend_is_callable_check_class(cname, scope, frame, fcc, &strict_class, error, ce_org != NULL)) {
zend_string_release_ex(cname, 0);
return 0;
}
@ -3483,6 +3492,11 @@ static zend_always_inline bool zend_is_callable_check_func(int check_flags, zval
if (error) zend_spprintf(error, 0, "class %s is not a subclass of %s", ZSTR_VAL(ce_org->name), ZSTR_VAL(fcc->calling_scope->name));
return 0;
}
if (ce_org && error) {
zend_error(E_DEPRECATED,
"Callables of the form [\"%s\", \"%s\"] are deprecated",
ZSTR_VAL(ce_org->name), Z_STRVAL_P(callable));
}
mname = zend_string_init(Z_STRVAL_P(callable) + clen + 2, mlen, 0);
} else if (ce_org) {
/* Try to fetch find static method of given class. */
@ -3756,7 +3770,7 @@ check_func:
return 1;
}
if (!zend_is_callable_check_class(Z_STR_P(obj), get_scope(frame), frame, fcc, &strict_class, error)) {
if (!zend_is_callable_check_class(Z_STR_P(obj), get_scope(frame), frame, fcc, &strict_class, error, false)) {
return 0;
}
} else {

View file

@ -78,6 +78,8 @@ $db->exec('DROP TABLE IF EXISTS test');
?>
--EXPECTF--
__construct('%S', '%S', %s)
Deprecated: Callables of the form ["MyPDO", "parent::__construct"] are deprecated in %s on line %d
exec('DROP TABLE IF EXISTS test')
exec('CREATE TABLE test(id INT)')
exec('INSERT INTO test(id) VALUES (1), (2)')

View file

@ -126,6 +126,8 @@ function "" not found or invalid function name
PDOStatement::fetchAll(): Argument #2 must be a callable, null given
no array or string given
cannot access "self" when no class scope is active
Deprecated: Callables of the form ["bar", "parent::method"] are deprecated in %s on line %d
array(2) {
[0]=>
string(9) "--- 1 ---"

View file

@ -22,7 +22,10 @@ second::init();
var_dump(spl_autoload_functions());
?>
--EXPECT--
--EXPECTF--
Deprecated: Use of "self" in callables is deprecated in %s on line %d
Deprecated: Use of "self" in callables is deprecated in %s on line %d
array(2) {
[0]=>
array(2) {

View file

@ -57,14 +57,20 @@ echo "-\n";
C::test3();
?>
--EXPECT--
--EXPECTF--
A
-
B
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
B
-
B
Deprecated: Use of "self" in callables is deprecated in %s on line %d
B
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
B
-
B
@ -74,7 +80,11 @@ C
C
-
B
Deprecated: Use of "self" in callables is deprecated in %s on line %d
B
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
C
-
B

View file

@ -38,12 +38,16 @@ echo "-\n";
C::test();
?>
--EXPECT--
--EXPECTF--
A
-
B
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
B
-
C
B
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
B

View file

@ -73,10 +73,17 @@ new Derived_5('5');
new Derived_6('6');
?>
--EXPECT--
--EXPECTF--
Deprecated: Callables of the form ["Derived_1", "Base::__construct"] are deprecated in %s on line %d
Base::__construct(1)
Deprecated: Callables of the form ["Derived_2", "parent::__construct"] are deprecated in %s on line %d
Base::__construct(2)
Base::__construct(3)
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
Base::__construct(4)
Base::__construct(5)
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
Base::__construct(6)

View file

@ -83,14 +83,22 @@ $o = new P;
$o->test();
?>
--EXPECT--
--EXPECTF--
parent|who
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
B
C|parent::who
Deprecated: Callables of the form ["C", "parent::who"] are deprecated in %s on line %d
B
B|parent::who
Deprecated: Callables of the form ["B", "parent::who"] are deprecated in %s on line %d
A
E|parent::who
Deprecated: Callables of the form ["E", "parent::who"] are deprecated in %s on line %d
D
A|who
A
@ -100,10 +108,16 @@ B|who2
A
===FOREIGN===
parent|who
Deprecated: Use of "parent" in callables is deprecated in %s on line %d
O
P|parent::who
Deprecated: Callables of the form ["P", "parent::who"] are deprecated in %s on line %d
O
$this|O::who
Deprecated: Callables of the form ["P", "O::who"] are deprecated in %s on line %d
O
$this|B::who
call_user_func(): Argument #1 ($callback) must be a valid callback, class P is not a subclass of B

View file

@ -27,6 +27,8 @@ try {
}
?>
--EXPECT--
--EXPECTF--
test::func()
Deprecated: Callables of the form ["test", "test_base::func"] are deprecated in %s on line %d
call_user_func(): Argument #1 ($callback) must be a valid callback, cannot call abstract method test_base::func()