Skip to content
Merged
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
20 changes: 9 additions & 11 deletions Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -1130,22 +1130,20 @@ ZEND_API zend_result zend_call_method_if_exists(
zend_object *object, zend_string *method_name, zval *retval,
uint32_t param_count, zval *params)
{
zend_fcall_info fci;
fci.size = sizeof(zend_fcall_info);
fci.object = object;
ZVAL_STR(&fci.function_name, method_name);
fci.retval = retval;
fci.param_count = param_count;
fci.params = params;
fci.named_params = NULL;

zval zval_method;
zend_fcall_info_cache fcc;
if (!zend_is_callable_ex(&fci.function_name, fci.object, IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL, &fcc, NULL)) {

ZVAL_STR(&zval_method, method_name);

if (UNEXPECTED(!zend_is_callable_ex(&zval_method, object, IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL, &fcc, NULL))) {
ZVAL_UNDEF(retval);
return FAILURE;
}

return zend_call_function(&fci, &fcc);
zend_call_known_fcc(&fcc, retval, param_count, params, NULL);
/* Need to free potential trampoline (__call/__callStatic) copied function handler before releasing the closure */
zend_release_fcall_info_cache(&fcc);
return SUCCESS;
}

/* 0-9 a-z A-Z _ \ 0x80-0xff */
Expand Down
22 changes: 22 additions & 0 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,28 @@ static ZEND_FUNCTION(zend_object_init_with_constructor)
ZVAL_COPY_VALUE(return_value, &obj);
}

static ZEND_FUNCTION(zend_call_method_if_exists)
{
zend_object *obj = NULL;
zend_string *method_name;
uint32_t num_args = 0;
zval *args = NULL;
ZEND_PARSE_PARAMETERS_START(2, -1)
Z_PARAM_OBJ(obj)
Z_PARAM_STR(method_name)
Z_PARAM_VARIADIC('*', args, num_args)
ZEND_PARSE_PARAMETERS_END();

zend_result status = zend_call_method_if_exists(obj, method_name, return_value, num_args, args);
if (status == FAILURE) {
ZEND_ASSERT(Z_ISUNDEF_P(return_value));
if (EG(exception)) {
RETURN_THROWS();
}
RETURN_NULL();
}
}

static ZEND_FUNCTION(zend_get_unit_enum)
{
ZEND_PARSE_PARAMETERS_NONE();
Expand Down
2 changes: 2 additions & 0 deletions ext/zend_test/test.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ function zend_call_method(object|string $obj_or_class, string $method, mixed $ar

function zend_object_init_with_constructor(string $class, mixed ...$args): mixed {}

function zend_call_method_if_exists(object $obj, string $method, mixed ...$args): mixed {}

function zend_test_zend_ini_parse_quantity(string $str): int {}
function zend_test_zend_ini_parse_uquantity(string $str): int {}

Expand Down
10 changes: 9 additions & 1 deletion ext/zend_test/test_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
zend_call_method_if_exists() with existing non public methods
--EXTENSIONS--
zend_test
--FILE--
<?php

class A {
protected function foo() {
return __METHOD__;
}
private function bar() {
return __METHOD__;
}
}

$a = new A();

$r = zend_call_method_if_exists($a, 'foo');
var_dump($r);

$r = zend_call_method_if_exists($a, 'bar');
var_dump($r);
?>
--EXPECT--
NULL
NULL
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
zend_call_method_if_exists() with existing public method
--EXTENSIONS--
zend_test
--FILE--
<?php

class C {
private function priv() {
var_dump(__METHOD__);
}
public function test() {
// this should call $c->priv()
zend_call_method_if_exists($this, 'priv');
}
}

$c = new C();
$c->test();

?>
--EXPECT--
string(7) "C::priv"
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
zend_call_method_if_exists() with existing public method
--EXTENSIONS--
zend_test
--FILE--
<?php

class A {
public function foo() {
return __METHOD__;
}
}

$a = new A();

$r = zend_call_method_if_exists($a, 'foo');
var_dump($r);

?>
--EXPECT--
string(6) "A::foo"
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
zend_call_method_if_exists() with existing non public static methods
--EXTENSIONS--
zend_test
--FILE--
<?php

class A {
protected static function foo() {
return __METHOD__;
}
private static function bar() {
return __METHOD__;
}
}

$a = new A();

$r = zend_call_method_if_exists($a, 'foo');
var_dump($r);

$r = zend_call_method_if_exists($a, 'bar');
var_dump($r);
?>
--EXPECT--
NULL
NULL
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
zend_call_method_if_exists() with existing static public method
--EXTENSIONS--
zend_test
--FILE--
<?php

class A {
public static function foo() {
return __METHOD__;
}
}

$a = new A();

$r = zend_call_method_if_exists($a, 'foo');
var_dump($r);

?>
--EXPECT--
string(6) "A::foo"
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
zend_call_method_if_exists() with non existing method on extended class with a trampoline
--EXTENSIONS--
zend_test
--FILE--
<?php

class A {
public function __call(string $name, array $arguments): string {
return "In A trampoline for $name!";
}
}

class B extends A {
public function __call(string $name, array $arguments): string {
return "In B trampoline for $name!";
}
}

$b = new B();

$r = zend_call_method_if_exists($b, 'bar');
var_dump($r);

?>
--EXPECT--
string(24) "In B trampoline for bar!"
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
zend_call_method_if_exists() with non existing method on extended class with a trampoline
--EXTENSIONS--
zend_test
--FILE--
<?php

class A {
public function __call(string $name, array $arguments): string {
return "In A trampoline for $name!";
}
}

class B extends A {}

$b = new B();

$r = zend_call_method_if_exists($b, 'bar');
var_dump($r);

?>
--EXPECT--
string(24) "In A trampoline for bar!"
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
zend_call_method_if_exists() with throwing method
--EXTENSIONS--
zend_test
--FILE--
<?php

class A {
public function foo() {
throw new Exception("Error");
}
}

$a = new A();

try {
$r = zend_call_method_if_exists($a, 'foo');
var_dump($r);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

?>
--EXPECT--
Exception: Error
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
--TEST--
zend_call_method_if_exists() with non existing method on class with a trampoline and static trampoline
--EXTENSIONS--
zend_test
--FILE--
<?php

class A {
public function foo() {
return __METHOD__;
}
public function __call(string $name, array $arguments): string {
return "In A trampoline for $name!";
}
public static function __callStatic(string $name, array $arguments): string {
return "In A static trampoline for $name!";
}
}

class B {
public function foo() {
return __METHOD__;
}
public static function __callStatic(string $name, array $arguments): string {
return "In B static trampoline for $name!";
}
public function __call(string $name, array $arguments): string {
return "In B trampoline for $name!";
}
}

$a = new A();
$r = zend_call_method_if_exists($a, 'bar');
var_dump($r);

$b = new B();
$r = zend_call_method_if_exists($b, 'bar');
var_dump($r);


?>
--EXPECT--
string(24) "In A trampoline for bar!"
string(24) "In B trampoline for bar!"
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
zend_call_method_if_exists() with non existing method on class with a static trampoline
--EXTENSIONS--
zend_test
--FILE--
<?php

class A {
public function foo() {
return __METHOD__;
}
public static function __callStatic(string $name, array $arguments): string {
return "In static trampoline for $name!";
}
}

$a = new A();

$r = zend_call_method_if_exists($a, 'bar');
var_dump($r);

?>
--EXPECT--
NULL
Loading