Skip to content

Commit

Permalink
Extend deprecation notices to is_callable($foo) and callable $foo
Browse files Browse the repository at this point in the history
Implements https://wiki.php.net/rfc/partially-supported-callables-expand-deprecation-notices
so that uses of "self" and "parent" in is_callable() and callable
type constraints now raise a deprecation notice, independent of the
one raised when and if the callable is actually invoked.

A new flag is added to the existing check_flags parameter of
zend_is_callable / zend_is_callable_ex, for use in internal calls
that would otherwise repeat the notice multiple times. In particular,
arguments to internal function calls are checked first based on
arginfo, and then again during ZPP, so the former suppresses the
deprecation notice.

Some existing tests which raised this deprecation have been updated
to avoid the syntax, but the existing version retained for maximum
regression coverage until it is made an error.

With thanks to Juliette Reinders Folmer for the RFC and initial
investigation.

Closes GH-8823.
  • Loading branch information
IMSoP authored and cmb69 committed Jul 14, 2022
1 parent 37e214b commit af15923
Show file tree
Hide file tree
Showing 22 changed files with 409 additions and 49 deletions.
5 changes: 2 additions & 3 deletions UPGRADING
Expand Up @@ -134,11 +134,10 @@ PHP 8.2 UPGRADE NOTES
[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.
["A", "method"].

RFC: https://wiki.php.net/rfc/deprecate_partially_supported_callables
RFC: https://wiki.php.net/rfc/partially-supported-callables-expand-deprecation-notices

. The "${var}" and "${expr}" style string interpolations are deprecated and
will be removed in PHP 9. Use "$var"/"{$var}" or "{${expr}}", respectively.
Expand Down
5 changes: 5 additions & 0 deletions UPGRADING.INTERNALS
Expand Up @@ -46,6 +46,11 @@ PHP 8.2 INTERNALS UPGRADE NOTES
smart_str_trim_to_size() before returning the string.
* It is recommended to use smart_str_extract() or smart_str_trim_to_size() when
using the smart_str API.
* zend_is_callable_ex, and functions which call it such as zend_is_callable and
zend_fcall_info_init, will issue deprecation notices if passed values which
are deprecated (see main UPGRADING notes). To suppress the notice, e.g. to
avoid duplicates when processing the same value multiple times, pass or add
IS_CALLABLE_SUPPRESS_DEPRECATIONS to the check_flags parameter.

========================
2. Build system changes
Expand Down
27 changes: 27 additions & 0 deletions Zend/tests/bug48899-deprecated.phpt
@@ -0,0 +1,27 @@
--TEST--
Bug #48899 (is_callable returns true even if method does not exist in parent class) [original test with deprecated syntax]
--FILE--
<?php

class ParentClass { }

class ChildClass extends ParentClass {
public function testIsCallable() {
var_dump(is_callable(array($this, 'parent::testIsCallable')));
}
public function testIsCallable2() {
var_dump(is_callable(array($this, 'static::testIsCallable2')));
}
}

$child = new ChildClass();
$child->testIsCallable();
$child->testIsCallable2();

?>
--EXPECTF--
Deprecated: Callables of the form ["ChildClass", "parent::testIsCallable"] are deprecated in %s on line %d
bool(false)

Deprecated: Callables of the form ["ChildClass", "static::testIsCallable2"] are deprecated in %s on line %d
bool(true)
4 changes: 2 additions & 2 deletions Zend/tests/bug48899.phpt
Expand Up @@ -7,10 +7,10 @@ class ParentClass { }

class ChildClass extends ParentClass {
public function testIsCallable() {
var_dump(is_callable(array($this, 'parent::testIsCallable')));
var_dump(is_callable(array('ParentClass', 'testIsCallable')));
}
public function testIsCallable2() {
var_dump(is_callable(array($this, 'static::testIsCallable2')));
var_dump(is_callable(array('ChildClass', 'testIsCallable2')));
}
}

Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/bug71622.phpt
Expand Up @@ -17,7 +17,7 @@ class Abc {
public static function run() {
$method = "foobar";
getMethodName($method);
var_dump(is_callable("self::$method"));
var_dump(is_callable("Abc::$method"));
self::$method();
}
}
Expand Down
273 changes: 252 additions & 21 deletions Zend/tests/callable_self_parent_static_deprecation.phpt
Expand Up @@ -9,27 +9,52 @@ class A {
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"]);
$variants = [
'"self::foo"' => "self::foo",
'"parent::foo"' => "parent::foo",
'"static::foo"' => "static::foo",
'["self", "foo"]' => ["self", "foo"],
'["parent", "foo"]' => ["parent", "foo"],
'["static", "foo"]' => ["static", "foo"],
'["B", "self::foo"]' => ["B", "self::foo"],
'["B", "parent::foo"]' => ["B", "parent::foo"],
'["B", "static::foo"]' => ["B", "static::foo"],
'["B", "A::foo"]' => ["B", "A::foo"],
'[$this, "self::foo"]' => [$this, "self::foo"],
'[$this, "parent::foo"]' => [$this, "parent::foo"],
'[$this, "static::foo"]' => [$this, "static::foo"],
'[$this, "A::foo"]' => [$this, "A::foo"],
];

echo "==> Test call_user_func\n";
foreach ($variants as $description => $callable) {
echo "$description\n";
call_user_func($callable);
}
echo "\n==> Test call_user_func_array\n";
foreach ($variants as $description => $callable) {
echo "$description\n";
call_user_func_array($callable, []);
}

// Also applies to other things performing calls
echo "Test array_map()\n";
array_map("self::foo", [1]);
echo "\n==> Test array_map\n";
foreach ($variants as $description => $callable) {
echo "$description\n";
array_map($callable, [1]);
}

echo "Test is_callable() -- should be silent\n";
var_dump(is_callable("self::foo"));
echo "\n==> Test is_callable()\n";
foreach ($variants as $description => $callable) {
echo "$description\n";
var_dump(is_callable($callable));
}

echo "Test callable type hint -- should be silent\n";
$this->callableTypeHint("self::foo");
echo "\n==> Test callable type hint\n";
foreach ($variants as $description => $callable) {
echo "$description\n";
$this->callableTypeHint($callable);
}
}

public function callableTypeHint(callable $c) {}
Expand All @@ -40,30 +65,236 @@ $b->test();

?>
--EXPECTF--
Test different callables
==> Test call_user_func
"self::foo"

Deprecated: Use of "self" in callables is deprecated in %s on line %d
"parent::foo"

Deprecated: Use of "parent" in callables is deprecated in %s on line %d
"static::foo"

Deprecated: Use of "static" in callables is deprecated in %s on line %d
["self", "foo"]

Deprecated: Use of "self" in callables is deprecated in %s on line %d
["parent", "foo"]

Deprecated: Use of "parent" in callables is deprecated in %s on line %d
["static", "foo"]

Deprecated: Use of "static" in callables is deprecated in %s on line %d
["B", "self::foo"]

Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
["B", "parent::foo"]

Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
["B", "static::foo"]

Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
["B", "A::foo"]

Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d
[$this, "self::foo"]

Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
[$this, "parent::foo"]

Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
[$this, "static::foo"]

Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
[$this, "A::foo"]

Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d

==> Test call_user_func_array
"self::foo"

Deprecated: Use of "self" in callables is deprecated in %s on line %d
"parent::foo"

Deprecated: Use of "parent" in callables is deprecated in %s on line %d
"static::foo"

Deprecated: Use of "static" in callables is deprecated in %s on line %d
["self", "foo"]

Deprecated: Use of "self" in callables is deprecated in %s on line %d
["parent", "foo"]

Deprecated: Use of "parent" in callables is deprecated in %s on line %d
["static", "foo"]

Deprecated: Use of "static" in callables is deprecated in %s on line %d
["B", "self::foo"]

Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
["B", "parent::foo"]

Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
["B", "static::foo"]

Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
["B", "A::foo"]

Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d
[$this, "self::foo"]

Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
[$this, "parent::foo"]

Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
[$this, "static::foo"]

Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
[$this, "A::foo"]

Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d

==> Test array_map
"self::foo"

Deprecated: Use of "self" in callables is deprecated in %s on line %d
"parent::foo"

Deprecated: Use of "parent" in callables is deprecated in %s on line %d
"static::foo"

Deprecated: Use of "static" in callables is deprecated in %s on line %d
["self", "foo"]

Deprecated: Use of "self" in callables is deprecated in %s on line %d
["parent", "foo"]

Deprecated: Use of "parent" in callables is deprecated in %s on line %d
["static", "foo"]

Deprecated: Use of "static" in callables is deprecated in %s on line %d
["B", "self::foo"]

Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
["B", "parent::foo"]

Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
["B", "static::foo"]

Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
["B", "A::foo"]

Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d
Test array_map()
[$this, "self::foo"]

Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
[$this, "parent::foo"]

Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
[$this, "static::foo"]

Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
[$this, "A::foo"]

Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d

==> Test is_callable()
"self::foo"

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
"parent::foo"

Deprecated: Use of "parent" in callables is deprecated in %s on line %d
bool(true)
"static::foo"

Deprecated: Use of "static" in callables is deprecated in %s on line %d
bool(true)
["self", "foo"]

Deprecated: Use of "self" in callables is deprecated in %s on line %d
bool(true)
["parent", "foo"]

Deprecated: Use of "parent" in callables is deprecated in %s on line %d
bool(true)
["static", "foo"]

Deprecated: Use of "static" in callables is deprecated in %s on line %d
bool(true)
["B", "self::foo"]

Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
bool(true)
["B", "parent::foo"]

Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
bool(true)
["B", "static::foo"]

Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
bool(true)
["B", "A::foo"]

Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d
bool(true)
[$this, "self::foo"]

Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
bool(true)
[$this, "parent::foo"]

Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
bool(true)
[$this, "static::foo"]

Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
bool(true)
[$this, "A::foo"]

Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d
bool(true)

==> Test callable type hint
"self::foo"

Deprecated: Use of "self" in callables is deprecated in %s on line %d
"parent::foo"

Deprecated: Use of "parent" in callables is deprecated in %s on line %d
"static::foo"

Deprecated: Use of "static" in callables is deprecated in %s on line %d
["self", "foo"]

Deprecated: Use of "self" in callables is deprecated in %s on line %d
["parent", "foo"]

Deprecated: Use of "parent" in callables is deprecated in %s on line %d
["static", "foo"]

Deprecated: Use of "static" in callables is deprecated in %s on line %d
["B", "self::foo"]

Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
["B", "parent::foo"]

Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
["B", "static::foo"]

Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
["B", "A::foo"]

Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d
[$this, "self::foo"]

Deprecated: Callables of the form ["B", "self::foo"] are deprecated in %s on line %d
[$this, "parent::foo"]

Deprecated: Callables of the form ["B", "parent::foo"] are deprecated in %s on line %d
[$this, "static::foo"]

Deprecated: Callables of the form ["B", "static::foo"] are deprecated in %s on line %d
[$this, "A::foo"]

Deprecated: Callables of the form ["B", "A::foo"] are deprecated in %s on line %d

0 comments on commit af15923

Please sign in to comment.