|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2010-04-10 16:07 UTC] weierophinney@php.net
Description:
------------
is_callable() is returning a false-positive for callbacks that reference non-
static methods. As an example, if I have defined a class 'Foo' with an instance
(i.e., non-static) method 'bar', and define the callback "array('Foo', 'bar')",
is_callable() will falsely return true.
Additionally, if you then pass this callback to call_user_func(), this latter
function will actually try to call the method statically -- and raise an
E_STRICT about the callback being invalid. If the method has any references to
$this, it then fails with an E_FATAL, but otherwise it will run the method as if
it were static.
This behavior is unexpected, and unintuitive. Calling non-static methods as if
they were static, even when they do not reference $this, violates visibility. I
would expect is_callable() to return false in these instances, and for
call_user_func() to immediately raise an E_FATAL if the method is not defined as
static.
Test script:
---------------
class Foo
{
public function bar()
{
return 'foo bar';
}
}
$callback = array('Foo', 'bar');
if (is_callable($callback)) {
echo call_user_func($callback);
}
Expected result:
----------------
No output.
Actual result:
--------------
PHP Strict Standards: call_user_func() expects parameter 1 to be a valid
callback, non-static method Foo::bar() should not be called statically
Strict Standards: call_user_func() expects parameter 1 to be a valid callback,
non-static method Test\Foo::bar() should not be called statically
foo bar
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Fri Oct 24 21:00:01 2025 UTC |
@johannes Perhaps an optional "strict" flag to is_callable() would address this? That would keep BC, while allowing for better checking for >= PHP-5 apps. Usage would be: $callback = array('Foo', 'bar'); if (is_callable($callback, true)) { // Passed strict check } else { // failed strict check } Thoughts?Another variation that actually (unexpectedly) works: Calling is_callable and call_user_func from inside an instance (non-static) method using any of: 'self::otherInstanceMethod', array('self','otherInstanceMethod'), array(self,'otherInstanceMethod') succeed. It appears that (the context for) '$this' is carried over from the original method, even though the calls are being done statically. Calling self::otherInstanceMethod() directly also succeeds. It appears that methods called from an instance method 'inherit' the context for $this. A bit unexpected, but *reasonable*. Win XP SP3 PHP 5.3.1 (xampp)<?php /** * @author zhouw * @copyright 2010 */ class Foo { public function bar() { return 'foo bar'; } } $callback = array('Foo', 'bar'); if (is_callable($callback)) { echo call_user_func($callback); } ?> 我测试可以用的。Let's first make important note that it is not a bug - it is an intended functionality, is_callable is supposed to return true on this data and as far as I can see, has always done so at least since 5.2 (maybe earlier, just don't have binary to check). Secondly, Foo::Bar is not always a static call. Consider this code: class Foo { private $number = 42; public function bar() { var_dump($this->number); return __METHOD__; } } class Bar extends Foo { public function callme($callback) { echo call_user_func($callback); } } $callback = array('Foo', 'Bar'); var_dump(is_callable($callback)); $bar = new Bar(); $bar->callme($callback); This is a bit convoluted, but everything works just fine. Changing is_callable and the engine to prohibit this case would cause massive code breakage, and as a lot of code uses this pattern to call parent ctors, it's probably not feasible. There's a difference between "true static call" and "parent method call that looks like static call" and unfortunately, this difference exists only in runtime when the actual call is made, is_callable would not be able to predict it.