Skip to content

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

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 5 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!"

42 changes: 42 additions & 0 deletions Zend/tests/call_user_func_007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
Testing scope overwriting with call_user_fun() in dynamic context
This test was written by "smalyshev" in this tread https://github.com/php/php-src/pull/375#issuecomment-20945580
--FILE--
<?php

class A {
function foo() {
echo "I am A\n";
}
}

class B extends A {
function foo() {
echo "I am B\n";
}
function who() {
var_dump(get_class($this));
call_user_func(array($this, "foo"));
}
}

class C extends B {
function foo() {
echo "I am C\n";
}

function who() {
var_dump(get_class($this));
call_user_func(array($this, "foo"));
parent::who();
}
}

$c = new C;
$c->who();
?>
--EXPECTF--
string(1) "C"
I am C
string(1) "C"
I am C
29 changes: 18 additions & 11 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -2612,6 +2612,7 @@ static int zend_is_callable_check_class(const char *name, int name_len, zend_fca
{
int ret = 0;
zend_class_entry **pce;
zend_class_entry *scope = EG(active_op_array) ? EG(active_op_array)->scope : NULL;
char *lcname = zend_str_tolower_dup(name, name_len);

*strict_class = 0;
Expand All @@ -2621,7 +2622,9 @@ static int zend_is_callable_check_class(const char *name, int name_len, zend_fca
if (error) *error = estrdup("cannot access self:: when no class scope is active");
} else {
fcc->called_scope = EG(called_scope);
fcc->calling_scope = EG(scope);
fcc->calling_scope = scope && instanceof_function(EG(scope), scope TSRMLS_CC)
? scope
: EG(scope);
if (!fcc->object_ptr) {
fcc->object_ptr = EG(This);
}
Expand All @@ -2631,16 +2634,22 @@ static int zend_is_callable_check_class(const char *name, int name_len, zend_fca
!memcmp(lcname, "parent", sizeof("parent") - 1)) {
if (!EG(scope)) {
if (error) *error = estrdup("cannot access parent:: when no class scope is active");
} else if (!EG(scope)->parent) {
if (error) *error = estrdup("cannot access parent:: when current class scope has no parent");
} else {
fcc->called_scope = EG(called_scope);
fcc->calling_scope = EG(scope)->parent;
if (!fcc->object_ptr) {
fcc->object_ptr = EG(This);
if (!(scope && instanceof_function(EG(scope), scope TSRMLS_CC))) {
scope = EG(scope);
}

if (!scope->parent) {
if (error) *error = estrdup("cannot access parent:: when current class scope has no parent");
} else {
fcc->calling_scope = scope->parent;
fcc->called_scope = EG(called_scope);
if (!fcc->object_ptr) {
fcc->object_ptr = EG(This);
}
*strict_class = 1;
ret = 1;
}
*strict_class = 1;
ret = 1;
}
} else if (name_len == sizeof("static") - 1 &&
!memcmp(lcname, "static", sizeof("static") - 1)) {
Expand All @@ -2656,8 +2665,6 @@ static int zend_is_callable_check_class(const char *name, int name_len, zend_fca
ret = 1;
}
} else if (zend_lookup_class_ex(name, name_len, NULL, 1, &pce TSRMLS_CC) == SUCCESS) {
zend_class_entry *scope = EG(active_op_array) ? EG(active_op_array)->scope : NULL;

fcc->calling_scope = *pce;
if (scope && !fcc->object_ptr && EG(This) &&
instanceof_function(Z_OBJCE_P(EG(This)), scope TSRMLS_CC) &&
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===