Skip to content

Fixed bug #48770: when call_user_func() fails to call parent from inheriting class #375

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

Closed
wants to merge 4 commits into from
Closed
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
2 changes: 0 additions & 2 deletions Zend/tests/bug48770.phpt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
--TEST--
Bug #48770 (call_user_func_array() fails to call parent from inheriting class)
--XFAIL--
See Bug #48770
--FILE--
<?php

Expand Down
2 changes: 0 additions & 2 deletions Zend/tests/bug48770_2.phpt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
--TEST--
Bug #48770 (call_user_func_array() fails to call parent from inheriting class)
--XFAIL--
See Bug #48770
--FILE--
<?php

Expand Down
2 changes: 0 additions & 2 deletions Zend/tests/bug48770_3.phpt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
--TEST--
Bug #48770 (call_user_func_array() fails to call parent from inheriting class)
--XFAIL--
See Bug #48770
--FILE--
<?php

Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/call_user_func_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
Testing call_user_func with self call in same class (not parent)
--FILE--
<?php

class A {
public static function func($str) {
var_dump(__METHOD__ .': '. $str);
}
public function run($str) {
call_user_func(array($this, 'self::func'), $str);
call_user_func('self::func', $str);
}
}

class B extends A {
public static function func($str) {
print "This method shouldn't be called when using self::func!\n";
}
}

$c = new B;
$c->run('This should work!');

?>
--EXPECTF--
%unicode|string%(26) "A::func: This should work!"
%unicode|string%(26) "A::func: This should work!"

7 changes: 4 additions & 3 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -3049,9 +3049,10 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, zval *object_ptr, uint ch
return 0;
}

fcc->calling_scope = Z_OBJCE_PP(obj); /* TBFixed: what if it's overloaded? */

fcc->object_ptr = *obj;
fcc->calling_scope = instanceof_function(Z_OBJCE_PP(obj), EG(scope) TSRMLS_CC)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EG(scope) could be empty here. Shouldn't we check for that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not find any case where EG(scope) was empty.
Should I check it anyway?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EG(scope) is going to be NULL in plain functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not completely sure in the patch. Currently the following script prints "C", but with your patch it prints "B".

test(); }

? EG(scope)
: Z_OBJCE_PP(obj);

if (callable_name) {
char *ptr;
Expand Down Expand Up @@ -3107,7 +3108,7 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, zval *object_ptr, uint ch
if (Z_OBJ_HANDLER_P(callable, get_closure) && Z_OBJ_HANDLER_P(callable, get_closure)(callable, &fcc->calling_scope, &fcc->function_handler, &fcc->object_ptr TSRMLS_CC) == SUCCESS) {
fcc->called_scope = fcc->calling_scope;
if (callable_name) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why delete the comment?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very old comment from 2001.
All overload logic was moved to zend_is_callable_check_class() by Dmitry Stogonov in 2008, when LSB was merget.
In fact, this comment very confusing, so I decided to remove it.

zend_class_entry *ce = Z_OBJCE_P(callable); /* TBFixed: what if it's overloaded? */
zend_class_entry *ce = Z_OBJCE_P(callable);

*callable_name_len = ce->name_length + sizeof("::__invoke") - 1;
*callable_name = emalloc(*callable_name_len + 1);
Expand Down
36 changes: 35 additions & 1 deletion ext/standard/tests/general_functions/callbacks_001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@ echo "===FOREIGN===\n";
$o = new P;
$o->test();

echo "===WITHOUT SCOPE===\n";
function call($cb) {
echo join('|', $cb) . "\n";
call_user_func($cb);
}
call(array($o, 'who'));
call(array($o, 'O::who'));
call(array($o, 'P::who'));
call(array($o, 'B::who'));
call(array($o, 'parent::who'));

call(array('parent', 'who'));
call(array('C', 'parent::who'));
call(array('C', 'who'));

?>
===DONE===
--EXPECTF--
Expand Down Expand Up @@ -105,4 +120,23 @@ O
$this|B::who

Warning: call_user_func() expects parameter 1 to be a valid callback, class 'P' is not a subclass of 'B' in %s on line %d
===DONE===
===WITHOUT SCOPE===
$this|who
P
$this|O::who
O
$this|P::who
P
$this|B::who

Warning: call_user_func() expects parameter 1 to be a valid callback, class 'P' is not a subclass of 'B' in %s on line %d
$this|parent::who
O
parent|who

Warning: call_user_func() expects parameter 1 to be a valid callback, cannot access parent:: when no class scope is active in %s on line %d
C|parent::who
B
C|who
B
===DONE===