mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Add Closure::fromCallable().
Add the ability to create closures from callable as part of RFC: https://wiki.php.net/rfc/closurefromcallable
This commit is contained in:
parent
bc63879d46
commit
63ca65daef
5 changed files with 659 additions and 14 deletions
187
Zend/tests/closures/closureFunction.inc
Normal file
187
Zend/tests/closures/closureFunction.inc
Normal file
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
function bar($param1)
|
||||
{
|
||||
return $param1;
|
||||
}
|
||||
|
||||
|
||||
$closure = function($param1) {
|
||||
return $param1;
|
||||
};
|
||||
|
||||
function test($fn)
|
||||
{
|
||||
static $count = 0;
|
||||
$input = "foo".$count;
|
||||
$count++;
|
||||
|
||||
$output = $fn($input);
|
||||
return $input === $output;
|
||||
}
|
||||
|
||||
class Foo
|
||||
{
|
||||
public static function publicStaticFunction($param1)
|
||||
{
|
||||
return $param1;
|
||||
}
|
||||
|
||||
private static function privateStaticFunction($param1)
|
||||
{
|
||||
return $param1;
|
||||
}
|
||||
|
||||
protected static function protectedStaticFunction($param1)
|
||||
{
|
||||
return $param1;
|
||||
}
|
||||
|
||||
private function privateInstanceFunc($param1)
|
||||
{
|
||||
return $param1;
|
||||
}
|
||||
|
||||
protected function protectedInstanceFunc($param1)
|
||||
{
|
||||
return $param1;
|
||||
}
|
||||
|
||||
|
||||
public function publicInstanceFunc($param1)
|
||||
{
|
||||
return $param1;
|
||||
}
|
||||
|
||||
public function closePrivateValid()
|
||||
{
|
||||
return Closure::fromCallable([$this, 'privateInstanceFunc']);
|
||||
}
|
||||
|
||||
public function closePrivateStatic()
|
||||
{
|
||||
return Closure::fromCallable([__CLASS__, 'privateStaticFunction']);
|
||||
}
|
||||
|
||||
public function bar($param1)
|
||||
{
|
||||
echo "this is bar\n";
|
||||
}
|
||||
|
||||
public function getCallable()
|
||||
{
|
||||
return Closure::fromCallable([$this, 'publicInstanceFunc']);
|
||||
}
|
||||
|
||||
public function getSelfPublicInstance()
|
||||
{
|
||||
return Closure::fromCallable([$this, 'publicInstanceFunc']);
|
||||
}
|
||||
|
||||
public function getSelfColonPublicInstanceMethod()
|
||||
{
|
||||
return Closure::fromCallable('self::publicInstanceFunc');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SubFoo extends Foo {
|
||||
|
||||
public function closePrivateStaticInvalid()
|
||||
{
|
||||
return Closure::fromCallable([__CLASS__, 'privateStaticFunction']);
|
||||
}
|
||||
|
||||
|
||||
public function closePrivateInvalid()
|
||||
{
|
||||
return Closure::fromCallable([$this, 'privateInstanceFunc']);
|
||||
}
|
||||
|
||||
public function closeProtectedStaticMethod()
|
||||
{
|
||||
return Closure::fromCallable([__CLASS__, 'protectedStaticFunction']);
|
||||
}
|
||||
|
||||
public function closeProtectedValid()
|
||||
{
|
||||
return Closure::fromCallable([$this, 'protectedInstanceFunc']);
|
||||
}
|
||||
|
||||
public function getParentPublicInstanceMethod()
|
||||
{
|
||||
return Closure::fromCallable('parent::publicInstanceFunc');
|
||||
}
|
||||
|
||||
public function getSelfColonParentPublicInstanceMethod()
|
||||
{
|
||||
return Closure::fromCallable('self::publicInstanceFunc');
|
||||
}
|
||||
|
||||
|
||||
public function getSelfColonParentProtectedInstanceMethod()
|
||||
{
|
||||
return Closure::fromCallable('self::protectedInstanceFunc');
|
||||
}
|
||||
|
||||
public function getSelfColonParentPrivateInstanceMethod()
|
||||
{
|
||||
return Closure::fromCallable('self::privateInstanceFunc');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MagicCall
|
||||
{
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
$info = ['__call'];
|
||||
$info[] = $name;
|
||||
$info = array_merge($info, $arguments);
|
||||
return implode(',', $info);
|
||||
}
|
||||
|
||||
public static function __callStatic($name, $arguments)
|
||||
{
|
||||
$info = ['__callStatic'];
|
||||
$info[] = $name;
|
||||
$info = array_merge($info, $arguments);
|
||||
return implode(',', $info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class PublicInvokable
|
||||
{
|
||||
public function __invoke($param1)
|
||||
{
|
||||
return $param1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function functionAccessProtected()
|
||||
{
|
||||
$foo = new Foo;
|
||||
|
||||
return Closure::fromCallable([$foo, 'protectedStaticFunction']);
|
||||
}
|
||||
|
||||
function functionAccessPrivate()
|
||||
{
|
||||
$foo = new Foo;
|
||||
|
||||
return Closure::fromCallable([$foo, 'privateStaticFunction']);
|
||||
}
|
||||
|
||||
|
||||
function functionAccessMethodDoesntExist()
|
||||
{
|
||||
$foo = new Foo;
|
||||
|
||||
return Closure::fromCallable([$foo, 'thisDoesNotExist']);
|
||||
}
|
||||
|
||||
?>
|
122
Zend/tests/closures/closureFunction_basic.phpt
Normal file
122
Zend/tests/closures/closureFunction_basic.phpt
Normal file
|
@ -0,0 +1,122 @@
|
|||
--TEST--
|
||||
Testing closure() functionality
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
include('closureFunction.inc');
|
||||
|
||||
echo 'Access public static function';
|
||||
$fn = Closure::fromCallable(['Foo', 'publicStaticFunction']);
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Access public static function with different case';
|
||||
$fn = Closure::fromCallable(['fOo', 'publicStaticfUNCTION']);
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Access public static function with colon scheme';
|
||||
$fn = Closure::fromCallable('Foo::publicStaticFunction');
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Access public instance method of object';
|
||||
$fn = Closure::fromCallable([new Foo, 'publicInstanceFunc']);
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Access public instance method of parent object through parent:: ';
|
||||
$fn = Closure::fromCallable([new Foo, 'publicInstanceFunc']);
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Function that exists';
|
||||
$fn = Closure::fromCallable('bar');
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Function that exists with different spelling';
|
||||
$fn = Closure::fromCallable('BAR');
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Closure is already a closure';
|
||||
$fn = Closure::fromCallable($closure);
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Class with public invokable';
|
||||
$fn = Closure::fromCallable(new PublicInvokable);
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo "Instance return private method as callable";
|
||||
$foo = new Foo;
|
||||
$fn = $foo->closePrivateValid();
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo "Instance return private static method as callable";
|
||||
$foo = new Foo;
|
||||
$fn = $foo->closePrivateStatic();
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Instance return protected static method as callable';
|
||||
$subFoo = new SubFoo;
|
||||
$fn = $subFoo->closeProtectedStaticMethod();
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Subclass closure over parent class protected method';
|
||||
$subFoo = new SubFoo;
|
||||
$fn = $subFoo->closeProtectedValid();
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Subclass closure over parent class static protected method';
|
||||
$subFoo = new SubFoo;
|
||||
$fn = $subFoo->closeProtectedStaticMethod();
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Access public instance method of parent object through "parent::" ';
|
||||
$subFoo = new SubFoo;
|
||||
$fn = $subFoo->getParentPublicInstanceMethod();
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Access public instance method of self object through "self::" ';
|
||||
$foo = new Foo;
|
||||
$fn = $foo->getSelfColonPublicInstanceMethod();
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Access public instance method of parent object through "self::" to parent method';
|
||||
$foo = new SubFoo;
|
||||
$fn = $foo->getSelfColonParentPublicInstanceMethod();
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'Access proteced instance method of parent object through "self::" to parent method';
|
||||
$foo = new SubFoo;
|
||||
$fn = $foo->getSelfColonParentProtectedInstanceMethod();
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'MagicCall __call instance method ';
|
||||
$fn = Closure::fromCallable([new MagicCall, 'nonExistentMethod']);
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
echo 'MagicCall __callStatic static method ';
|
||||
$fn = Closure::fromCallable(['MagicCall', 'nonExistentMethod']);
|
||||
echo $fn(" OK".PHP_EOL);
|
||||
|
||||
|
||||
?>
|
||||
===DONE===
|
||||
--EXPECT--
|
||||
|
||||
Access public static function OK
|
||||
Access public static function with different case OK
|
||||
Access public static function with colon scheme OK
|
||||
Access public instance method of object OK
|
||||
Access public instance method of parent object through parent:: OK
|
||||
Function that exists OK
|
||||
Function that exists with different spelling OK
|
||||
Closure is already a closure OK
|
||||
Class with public invokable OK
|
||||
Instance return private method as callable OK
|
||||
Instance return private static method as callable OK
|
||||
Instance return protected static method as callable OK
|
||||
Subclass closure over parent class protected method OK
|
||||
Subclass closure over parent class static protected method OK
|
||||
Access public instance method of parent object through "parent::" OK
|
||||
Access public instance method of self object through "self::" OK
|
||||
Access public instance method of parent object through "self::" to parent method OK
|
||||
Access proteced instance method of parent object through "self::" to parent method OK
|
||||
MagicCall __call instance method __call,nonExistentMethod, OK
|
||||
MagicCall __callStatic static method __callStatic,nonExistentMethod, OK
|
||||
===DONE===
|
215
Zend/tests/closures/closureFunction_error.phpt
Normal file
215
Zend/tests/closures/closureFunction_error.phpt
Normal file
|
@ -0,0 +1,215 @@
|
|||
--TEST--
|
||||
Testing closure() functionality
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
include('closureFunction.inc');
|
||||
|
||||
echo 'Cannot access privateInstance method statically'."\n";
|
||||
try {
|
||||
$fn = Closure::fromCallable(['Foo', 'privateInstanceFunc']);
|
||||
echo "Test failed to fail and return was : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
|
||||
echo 'Cannot access privateInstance method statically with colon scheme'."\n";
|
||||
try {
|
||||
$fn = Closure::fromCallable('Foo::privateInstanceFunc');
|
||||
echo "Test failed to fail and return was : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Cannot access privateInstance method'."\n";
|
||||
try {
|
||||
$fn = Closure::fromCallable([new Foo, 'privateInstanceFunc']);
|
||||
echo "Test failed to fail and return was : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'SubClass cannot access private instance method'."\n";
|
||||
try {
|
||||
$fn = Closure::fromCallable([new SubFoo, 'privateInstanceFunc']);
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Cannot access private static function of instance'."\n";
|
||||
try {
|
||||
$fn = Closure::fromCallable([new Foo, 'privateStaticFunction']);
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Cannot access private static method statically'."\n";
|
||||
try {
|
||||
$fn = Closure::fromCallable(['Foo', 'privateStaticFunction']);
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Cannot access private static method statically with colon scheme'."\n";
|
||||
try {
|
||||
$fn = Closure::fromCallable('Foo::privateStaticFunction');
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Non-existent method should fail'."\n";
|
||||
try {
|
||||
$fn = Closure::fromCallable('Foo::nonExistentFunction');
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Non-existent class should fail'."\n";
|
||||
try {
|
||||
$fn = Closure::fromCallable(['NonExistentClass', 'foo']);
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Non-existent function should fail'."\n";
|
||||
try {
|
||||
$fn = Closure::fromCallable('thisDoesNotExist');
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
|
||||
echo 'Subclass cannot closure over parent private instance method'."\n";
|
||||
try {
|
||||
$subFoo = new SubFoo;
|
||||
$fn = $subFoo->closePrivateInvalid();
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Subclass cannot closure over parant private static method'."\n";
|
||||
try {
|
||||
$subFoo = new SubFoo;
|
||||
$fn = $subFoo->closePrivateStaticInvalid();
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Function scope cannot closure over protected instance method'."\n";
|
||||
try {
|
||||
$fn = functionAccessProtected();
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Function scope cannot closure over private instance method'."\n";
|
||||
try {
|
||||
$fn = functionAccessPrivate();
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo 'Access private instance method of parent object through "self::" to parent method'."\n";
|
||||
try {
|
||||
$foo = new SubFoo;
|
||||
$fn = $foo->getSelfColonParentPrivateInstanceMethod();
|
||||
echo "Test failed to fail, closure is : ".var_export($fn, true)."\n";
|
||||
}
|
||||
catch (\TypeError $te) {
|
||||
//This is the expected outcome.
|
||||
}
|
||||
catch (\Throwable $t) {
|
||||
echo "Wrong exception type thrown: ".get_class($t)." : ".$t->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo "OK\n";
|
||||
|
||||
?>
|
||||
===DONE===
|
||||
--EXPECT--
|
||||
|
||||
Cannot access privateInstance method statically
|
||||
Cannot access privateInstance method statically with colon scheme
|
||||
Cannot access privateInstance method
|
||||
SubClass cannot access private instance method
|
||||
Cannot access private static function of instance
|
||||
Cannot access private static method statically
|
||||
Cannot access private static method statically with colon scheme
|
||||
Non-existent method should fail
|
||||
Non-existent class should fail
|
||||
Non-existent function should fail
|
||||
Subclass cannot closure over parent private instance method
|
||||
Subclass cannot closure over parant private static method
|
||||
Function scope cannot closure over protected instance method
|
||||
Function scope cannot closure over private instance method
|
||||
Access private instance method of parent object through "self::" to parent method
|
||||
OK
|
||||
===DONE===
|
|
@ -235,6 +235,105 @@ ZEND_METHOD(Closure, bind)
|
|||
}
|
||||
/* }}} */
|
||||
|
||||
static void zend_closure_call_magic(INTERNAL_FUNCTION_PARAMETERS) {
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fcc;
|
||||
|
||||
memset(&fci, 0, sizeof(zend_fcall_info));
|
||||
memset(&fci, 0, sizeof(zend_fcall_info_cache));
|
||||
|
||||
fci.size = sizeof(zend_fcall_info);
|
||||
fci.retval = return_value;
|
||||
|
||||
fcc.initialized = 1;
|
||||
fcc.function_handler = (zend_function *) EX(func)->common.arg_info;
|
||||
fci.params = (zval*) emalloc(sizeof(zval) * 2);
|
||||
fci.param_count = 2;
|
||||
ZVAL_STR(&fci.params[0], EX(func)->common.function_name);
|
||||
array_init(&fci.params[1]);
|
||||
zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]);
|
||||
|
||||
fci.object = Z_OBJ(EX(This));
|
||||
fcc.object = Z_OBJ(EX(This));
|
||||
fcc.calling_scope = zend_get_executed_scope();
|
||||
|
||||
zend_call_function(&fci, &fcc);
|
||||
|
||||
zval_ptr_dtor(&fci.params[0]);
|
||||
zval_ptr_dtor(&fci.params[1]);
|
||||
efree(fci.params);
|
||||
}
|
||||
|
||||
|
||||
static int zend_create_closure_from_callable(zval *return_value, zval *callable, char **error) {
|
||||
zend_fcall_info_cache fcc;
|
||||
zend_function *mptr;
|
||||
zval instance;
|
||||
|
||||
if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, error)) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
mptr = fcc.function_handler;
|
||||
if (mptr == NULL) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
|
||||
zend_internal_function call;
|
||||
memset(&call, 0, sizeof(zend_internal_function));
|
||||
|
||||
call.type = ZEND_INTERNAL_FUNCTION;
|
||||
call.handler = zend_closure_call_magic;
|
||||
call.function_name = mptr->common.function_name;
|
||||
call.arg_info = (zend_internal_arg_info *) mptr->common.prototype;
|
||||
call.scope = mptr->common.scope;
|
||||
|
||||
zend_free_trampoline(mptr);
|
||||
mptr = (zend_function *) &call;
|
||||
}
|
||||
|
||||
ZVAL_OBJ(&instance, fcc.object);
|
||||
zend_create_closure(return_value, mptr, mptr->common.scope, fcc.object ? fcc.object->ce : NULL, fcc.object ? &instance : NULL);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* {{{ proto Closure Closure::fromCallable(callable callable)
|
||||
Create a closure from a callabl using the current scope. */
|
||||
ZEND_METHOD(Closure, fromCallable)
|
||||
{
|
||||
zval *callable;
|
||||
int success;
|
||||
char *error = NULL;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callable) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(callable) == IS_OBJECT && instanceof_function(Z_OBJCE_P(callable), zend_ce_closure)) {
|
||||
// It's already a closure
|
||||
RETURN_ZVAL(callable, 1, 0);
|
||||
}
|
||||
|
||||
// create closure as if it were called from parent scope
|
||||
EG(current_execute_data) = EX(prev_execute_data);
|
||||
success = zend_create_closure_from_callable(return_value, callable, &error);
|
||||
EG(current_execute_data) = execute_data;
|
||||
|
||||
if (success == FAILURE) {
|
||||
zend_clear_exception();
|
||||
if (error) {
|
||||
zend_throw_exception_ex(zend_ce_type_error, 0, "Failed to create closure from callable: %s", error);
|
||||
efree(error);
|
||||
} else {
|
||||
zend_throw_exception_ex(zend_ce_type_error, 0, "Failed to create closure from callable");
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */
|
||||
{
|
||||
zend_throw_error(NULL, "Instantiation of 'Closure' is not allowed");
|
||||
|
@ -489,11 +588,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_call, 0, 0, 1)
|
|||
ZEND_ARG_VARIADIC_INFO(0, parameters)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_fromcallable, 0, 0, 1)
|
||||
ZEND_ARG_INFO(0, newthis)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
static const zend_function_entry closure_functions[] = {
|
||||
ZEND_ME(Closure, __construct, NULL, ZEND_ACC_PRIVATE)
|
||||
ZEND_ME(Closure, bind, arginfo_closure_bind, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
|
||||
ZEND_MALIAS(Closure, bindTo, bind, arginfo_closure_bindto, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(Closure, call, arginfo_closure_call, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(Closure, fromCallable, arginfo_closure_fromcallable, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
|
|
|
@ -1633,6 +1633,30 @@ ZEND_METHOD(reflection_function, export)
|
|||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
static zend_function *_find_function_pointer(char *name_str, int name_len) {
|
||||
char *lcname, *nsname;
|
||||
zend_function *fptr;
|
||||
|
||||
lcname = zend_str_tolower_dup(name_str, name_len);
|
||||
|
||||
/* Ignore leading "\" */
|
||||
nsname = lcname;
|
||||
if (lcname[0] == '\\') {
|
||||
nsname = &lcname[1];
|
||||
name_len--;
|
||||
}
|
||||
|
||||
if ((fptr = zend_hash_str_find_ptr(EG(function_table), nsname, name_len)) == NULL) {
|
||||
efree(lcname);
|
||||
return NULL;
|
||||
}
|
||||
efree(lcname);
|
||||
|
||||
return fptr;
|
||||
}
|
||||
|
||||
|
||||
/* {{{ proto public void ReflectionFunction::__construct(string name)
|
||||
Constructor. Throws an Exception in case the given function does not exist */
|
||||
ZEND_METHOD(reflection_function, __construct)
|
||||
|
@ -1656,23 +1680,12 @@ ZEND_METHOD(reflection_function, __construct)
|
|||
if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "s", &name_str, &name_len) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
lcname = zend_str_tolower_dup(name_str, name_len);
|
||||
|
||||
/* Ignore leading "\" */
|
||||
nsname = lcname;
|
||||
if (lcname[0] == '\\') {
|
||||
nsname = &lcname[1];
|
||||
name_len--;
|
||||
}
|
||||
|
||||
if ((fptr = zend_hash_str_find_ptr(EG(function_table), nsname, name_len)) == NULL) {
|
||||
efree(lcname);
|
||||
fptr = _find_function_pointer(name_str, name_len);
|
||||
if (fptr == NULL) {
|
||||
zend_throw_exception_ex(reflection_exception_ptr, 0,
|
||||
"Function %s() does not exist", name_str);
|
||||
return;
|
||||
return;
|
||||
}
|
||||
efree(lcname);
|
||||
}
|
||||
|
||||
ZVAL_STR_COPY(&name, fptr->common.function_name);
|
||||
|
@ -6793,6 +6806,10 @@ static const zend_function_entry reflection_zend_extension_functions[] = {
|
|||
};
|
||||
/* }}} */
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_closure, 0, 0, 1)
|
||||
ZEND_ARG_INFO(0, callable)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
const zend_function_entry reflection_ext_functions[] = { /* {{{ */
|
||||
PHP_FE_END
|
||||
}; /* }}} */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue