How to Convert a Function into a Closure in PHP? - php

I have function that uploads files to server:
function upload() {
$files = Input::file(self::$filename);
foreach ($files as $key => $file) {
// Validation
// Uploading
}
}
So, how to make this function as closure? That after loading it returns error or success information to output variable:
$results = function upload() {
}
In results should be result array of uploading files. It can be error or success state.

Since you're using self:: I assume you expect to run this function from inside a class. But you can't really do this - the function should be defined in the class to be able to use self::.
But really, you can just pass it as a parameter.
function upload($filename) {
$files = Input::file($filename); // note, self:: removed
foreach ($files as $key => $file) {
// Validation
// Uploading
}
return $result;
}
And when you need to use it inside your class, call it this way:
$result = upload(self::$filename);

For Converting a Function into a Closure in PHP, you can :
Since PHP 7 >= 7.1.0, you can use Closure::fromCallable()
$cl = Closure::fromCallable("upload");
$result = $cl();
Before (PHP >= 5.4.0), you can use ReflectionFunction::getClosure()
$reflexion = new ReflectionFunction('upload');
$cl = $reflexion->getClosure();
$result = $cl();
Another idea is to construct a new function that call your main function :
$cl = function (...$args) {
return upload(...$args);
};

Updated and Expanded
This answer addresses the OP's primary question of "How to Convert a Function into a Closure in PHP?" One way involves writing a nameless function and then assigning it to a variable. Since the OP's upload code is problematic, i.e. a method without a class, I therefore borrow the corrected code of #astax, modifying it for the purpose of illustrating the mechanics of how to create a closure in PHP, as follows:
<?php
$f = function( $filename ) {
$files = Input::file( $filename );
foreach ($files as $key => $file) {
// Validation
// Uploading
}
return $arrResults;
};
Note the trailing semicolon after the final brace. Also, the closure definition consists of an anonymous function. You may get the result as follows:
<?php
$arrResults = $f( $filename );
// or in a class:
$arrResults = $f( self::$filename );
Note: PHP does not have named closures, so the function needs to be anonymous. Usually it is assigned to a variable but PHP 7 provides more flexibility such that you may avoid setting a variable as the following two basic examples indicate:
<?php
echo (function() {
return 2 * 2;
})(),"\n",
(function(){
return function(){
return 3 * 3;
};
})()();
See live code.
This syntax is possible thanks to the support for function call chaining in PHP 7.
Note: if your code includes a return statement, when the closure executes, you may assign the return value to a variable.
Incidentally, two useful articles on PHP's closures: see this issue as well in this issue.
Lastly, note while other languages recognize a bound variable with a function as a closure and a nameless function as an anonymous function, in PHP support for each is done vis a vis the Closure class.
Caveat:
Care should be taken when creating a closure from the Closure object's fromCallable method since it evidently yields a more restricted closure as the following code demonstrates:
<?php
class A {
private $name;
public function __construct($name)
{
$this->name = $name;
}
}
$person = new A("Tim");
function test() {
return $this->name;
}
$f = function() {
return $this->name;
};
$cl = Closure::fromCallable( $f );
echo $cl->bindTo($person, 'A')();
echo "\n\nBut, Closure::fromCallable problematic:\n";
try {
$cl = Closure::fromCallable( "test" );
echo $cl->bindTo($person, 'A')();
} catch (Error $e) {
echo "Unable to bind closure from callable because: \n";
echo $e->getMessage();
}
see live code
The error message that results is as follows:
Warning: Cannot rebind scope of closure created by
ReflectionFunctionAbstract::getClosure() in /in/qso7b on line 26
Unable to bind closure from callable because: Function name must be a
string

Related

Check if callable can receive class as parameter in php

I have a callable $f and I would like to know if it can receive an instance of a certain class Foo as input.
At the moment I'm doing something like
try {
$f($foo);
} catch (\TypeError $e) {
throw new \InvalidArgumentException('The provided function can not evaluate inputs of this type');
}
Is there a way to check this WITHOUT actually invoking the callable? Maybe with reflection or some other dark magic?
If you want to be able to reflect any kind of callable, you'll need to wrap up the logic in a small function. Depending on whether you've got an array, a function name or an anonymous function, you need to create either a ReflectionFunction or ReflectionMethod. Fortunately, these both extend ReflectionFunctionAbstract, so we can type-hint the return value.
function reflectCallable($arg): ReflectionFunctionAbstract {
if (is_array($arg)) {
$ref = new ReflectionMethod(...$arg);
} elseif (is_callable($arg)) {
$ref = new ReflectionFunction($arg);
}
return $ref;
}
This will return you the appropriate object for your callable value, which you can then use to fetch the parameters and act accordingly:
function definedFunc(Foo $foo) {}
$callable = function(Foo $foo) {};
class Bar { public function baz(Foo $foo) {} }
foreach (['definedFunc', $callable, ['Bar', 'baz']] as $callable) {
$reflected = reflectCallable($callable);
if ((string) $reflected->getParameters()[0]->getType() === 'Foo') {
echo 'Callable takes Foo', PHP_EOL;
}
}
See https://3v4l.org/c5vmM
Note that this doesn't do any error handling - you'll probably get warnings/notices if the callable doesn't take any parameters or the first parameter doesn't have a type. It also requires PHP 7+, but hopefully that's not an issue.
It doesn't currently support objects that implement __invoke or static calls defined as "Foo::bar", but they wouldn't be too hard to add if necessary. I've just found something very similar in the source of Twig, which does a more thorough job: https://github.com/twigphp/Twig/blob/v2.8.0/src/Node/Expression/CallExpression.php#L280
You can with ReflectionParameter::getType:
$f = function(Foo $foo) {};
$reflectionFunc = new ReflectionFunction($f);
$reflectionParams = $reflectionFunc->getParameters();
$reflectionType1 = $reflectionParams[0]->getType();
echo $reflectionType1;
output:
Foo

Is there any cleaner way to call a clouse property in PHP [duplicate]

I would like to be able to call a closure that I assign to an object's property directly without reassigning the closure to a variable and then calling it. Is this possible?
The code below doesn't work and causes Fatal error: Call to undefined method stdClass::callback().
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
$obj->callback();
As of PHP7, you can do
$obj = new StdClass;
$obj->fn = function($arg) { return "Hello $arg"; };
echo ($obj->fn)('World');
or use Closure::call(), though that doesn't work on a StdClass.
Before PHP7, you'd have to implement the magic __call method to intercept the call and invoke the callback (which is not possible for StdClass of course, because you cannot add the __call method)
class Foo
{
public function __call($method, $args)
{
if(is_callable(array($this, $method))) {
return call_user_func_array($this->$method, $args);
}
// else throw exception
}
}
$foo = new Foo;
$foo->cb = function($who) { return "Hello $who"; };
echo $foo->cb('World');
Note that you cannot do
return call_user_func_array(array($this, $method), $args);
in the __call body, because this would trigger __call in an infinite loop.
You can do this by calling __invoke on the closure, since that's the magic method that objects use to behave like functions:
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
$obj->callback->__invoke();
Of course that won't work if the callback is an array or a string (which can also be valid callbacks in PHP) - just for closures and other objects with __invoke behavior.
As of PHP 7 you can do the following:
($obj->callback)();
Since PHP 7 a closure can be called using the call() method:
$obj->callback->call($obj);
Since PHP 7 is possible to execute operations on arbitrary (...) expressions too (as explained by Korikulum):
($obj->callback)();
Other common PHP 5 approaches are:
using the magic method __invoke() (as explained by Brilliand)
$obj->callback->__invoke();
using the call_user_func() function
call_user_func($obj->callback);
using an intermediate variable in an expression
($_ = $obj->callback) && $_();
Each way has its own pros and cons, but the most radical and definitive solution still remains the one presented by Gordon.
class stdKlass
{
public function __call($method, $arguments)
{
// is_callable([$this, $method])
// returns always true when __call() is defined.
// is_callable($this->$method)
// triggers a "PHP Notice: Undefined property" in case of missing property.
if (isset($this->$method) && is_callable($this->$method)) {
return call_user_func($this->$method, ...$arguments);
}
// throw exception
}
}
$obj = new stdKlass();
$obj->callback = function() { print "HelloWorld!"; };
$obj->callback();
It seems to be possible using call_user_func().
call_user_func($obj->callback);
not elegant, though.... What #Gordon says is probably the only way to go.
Well, if you really insist. Another workaround would be:
$obj = new ArrayObject(array(),2);
$obj->callback = function() {
print "HelloWorld!";
};
$obj['callback']();
But that's not the nicest syntax.
However, the PHP parser always treats T_OBJECT_OPERATOR, IDENTIFIER, ( as method call. There seems to be no workaround for making -> bypass the method table and access the attributes instead.
I know this is old, but I think Traits nicely handle this problem if you are using PHP 5.4+
First, create a trait that makes properties callable:
trait CallableProperty {
public function __call($method, $args) {
if (property_exists($this, $method) && is_callable($this->$method)) {
return call_user_func_array($this->$method, $args);
}
}
}
Then, you can use that trait in your classes:
class CallableStdClass extends stdClass {
use CallableProperty;
}
Now, you can define properties via anonymous functions and call them directly:
$foo = new CallableStdClass();
$foo->add = function ($a, $b) { return $a + $b; };
$foo->add(2, 2); // 4
well, it should be emphisized that storing the closure in a variable, and call the varible is actually (wierdly) faster, depending on the call amount, it becomes quite a lot, with xdebug (so very precise measuring), we are talking about 1,5 (the factor, by using a varible, instead of directly calling the __invoke. so instead , just store the closure in a varible and call it.
Here's another alternative based on the accepted answer but extending stdClass directly:
class stdClassExt extends stdClass {
public function __call($method, $args)
{
if (isset($this->$method)) {
$func = $this->$method;
return call_user_func_array($func, $args);
}
}
}
Usage example:
$foo = new stdClassExt;
$foo->blub = 42;
$foo->whooho = function () { return 1; };
echo $foo->whooho();
You are probably better off using call_user_func or __invoke though.
Updated:
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
PHP >= 7 :
($obj->callback)();
PHP >= 5.4 :
$callback = $obj->callback;
$callback();
If you're using PHP 5.4 or above you could bind a callable to the scope of your object to invoke custom behavior. So for example if you were to have the following set up..
function run_method($object, Closure $method)
{
$prop = uniqid();
$object->$prop = \Closure::bind($method, $object, $object);
$object->$prop->__invoke();
unset($object->$prop);
}
And you were operating on a class like so..
class Foo
{
private $value;
public function getValue()
{
return $this->value;
}
}
You could run your own logic as if you were operating from within the scope of your object
$foo = new Foo();
run_method($foo, function(){
$this->value = 'something else';
});
echo $foo->getValue(); // prints "something else"
I note that this works in PHP5.5
$a = array();
$a['callback'] = function() {
print "HelloWorld!";
};
$a['callback']();
Allows one to create a psuedo-object collection of closures.

Determining if a closure is static in PHP

A closure defined in PHP can also carry the static modifier.
$f = function () { };
$g = static function () { };
The static closure cannot be bound via Closure::bind or Closure::bindTo, and will issue a warning.
$g = Closure::bind(static function () { }, new stdClass());
// Warning: Cannot bind an instance to a static closure in ...
This is also the case of closures created by reflecting a static method with ReflectionMethod::getClosure.
class MyClass
{
public static function myStaticMethod() { }
}
// reflect MyClass::myStaticMethod, create an unbound closure, and try to bind it
$f = (new ReflectionMethod(MyClass::class, 'myStaticMethod'))
->getClosure()
->bindTo(new stdClass());
// Warning: Cannot bind an instance to a static closure in ...
While annoying, this is acceptable; however, how is one to test between a static and non-static closure?
ReflectionMethod::isStatic seemed like it might work, but sensibly doesn't as Closure::__invoke is instance-level, not static.
$f = static function () { };
// reflect Closure::__invoke because I think I'm tricky
$r = new ReflectionMethod($f, '__invoke');
// and it's not static anyway
var_dump($r->isStatic()); // bool(false)
Further, checking ReflectionMethod::getClosureThis can generally work, as a static method must be unbound, however that doesn't cover closures defined outside an instance method, or the corner-case of instance methods that have been unbound.
class MyClass
{
public function myInstanceMethod() { }
}
$o = new MyClass();
// reflect MyClass::myInstanceMethod, create a bound closure, and then unbind it
$f = (new ReflectionMethod($o, 'myInstanceMethod'))
->getClosure($o)
->bindTo(null);
// then reflect the closure
$r = new ReflectionFunction($f);
// and see it's bound to nothing, as would be the case of a static closure
var_dump($r->getClosureThis()); // NULL
So, to restate, how do you determine whether a closure is static (or more specifically, bindable) or not?
It really seems as though we should have a ReflectionFunctionAbstract::isBindable, or that ReflectionMethod::isStatic be moved up to ReflectionFunctionAbstract.
If binding works, the Closure will have a $this bound to it. So, just bind it and then check for the $this. If it's null, well, then it's a static Closure.
function isBindable(\Closure $closure) {
$boundClosure = #\Closure::bind($closure, new stdClass);
return $boundClosure && (new ReflectionFunction($boundClosure))->getClosureThis() != null;
}
It seems impossible now.
You can find some debates here: https://bugs.php.net/bug.php?id=64761
The only real workaround I use for myself now is adding ->isBindable property manually.
Here's some code I found here https://github.com/atoum/atoum/blob/master/classes/test/adapter/invoker.php
Maybe will give you a few ideas
protected static function isBindable(\closure $closure)
{
$isBindable = (version_compare(PHP_VERSION, '5.4.0') >= 0);
if ($isBindable === true)
{
$reflectedClosure = new \reflectionFunction($closure);
$isBindable = ($reflectedClosure->getClosureThis() !== null || $reflectedClosure->getClosureScopeClass() === null);
}
return $isBindable;
}
Very old question but still shows up in google search. The mentioned isStatic nowadays exists in ReflectionFunction,as of php 8.1 (I tested it out)
$fn = static fn() => true;
$refl = new ReflectionFunction($fn);
$refl->isStatic(); // returns true
$fn = fn() => true;
$refl = new ReflectionFunction($fn);
$refl->isStatic(); // returns false
It does not show up in documentation as of writing.
See: https://www.php.net/manual/en/class.reflectionfunction.php
I also created a missing doc report here: https://github.com/php/doc-en/issues/2136

invoking runtime created functions

I'm trying to dynamically create the base for a DB entity generalization for a project I'm working on. I basically want to dynamically create a set of standard methods and tools for the properties in any class that extends this. Much like the tools you get for free with Python/Django.
I got the idea from this guy: http://www.stubbles.org/archives/65-Extending-objects-with-new-methods-at-runtime.html
So I've implemented the __call function as described in the post above,
public function __call($method, $args) {
echo "<br>Calling ".$method;
if (isset($this->$method) === true) {
$func = $this->$method;
$func();
}
}
I have a function which gives me the objects public/protected properties through get_object_vars,
public function getJsonData() {
$var = get_object_vars($this);
foreach($var as &$value) {
if (is_object($value) && method_exists($value, 'getJsonData')) {
$value = $value->getJsonData;
}
}
return $var;
}
and now I want to create some methods for them:
public function __construct() {
foreach($this->getJsonData() as $name => $value) {
// Create standard getter
$methodName = "get".$name;
$me = $this;
$this->$methodName = function() use ($me, $methodName, $name) {
echo "<br>".$methodName." is called";
return $me->$name;
};
}
}
Thanks to Louis H. which pointed out the "use" keyword for this down below.
This basically creates an anonymous function on the fly. The function is callable, but it is no longer within the context of it's object. It produces a "Fatal error: Cannot access protected property"
Unfortunately I'm bound to PHP version 5.3, which rules out Closure::bind. The suggested solution in Lazy loading class methods in PHP will therefore not work here.
I'm rather stumped here... Any other suggestions?
Update
Edited for brevity.
Try it like this (you have to make the variables you'll need available to the method)
$this->$methodName = function() use ($this, $methodName, $name){
echo "<br>".$methodName." is called";
return $this->$$name;
};
You should have access to the object context through $this.
Instead of updating the original question above, I include the complete solution here for anybody struggling with the same issues:
First of all, since the closure cannot have real object access, I needed to include the actual value with the "use" declaration when creating the closure function (see original __construct function above):
$value =& $this->$name;
$this->$methodName = function() use ($me, $methodName, &$value) {
return $value;
};
Secondly the __call magic method did not just need to call the closure function, it needed also to return any output from it. So instead of just calling $func(), I return $func();
This did the trick! :-)

Passing objects/references as function parameters

EDIT: Language is PHP.
So, I'm implementing a listener/observer pattern. One of the objects I implemented was an object for including files. Which works in a way like this:
class IncludeEvent extends Event {
protected $File = '';
public $Contents;
....
public function Begin() {
....
// this is the ONLY time $Contents is modified:
$this->Contents = ob_get_contents();
ob_end_clean();
....
}
....
if ($this->Notify(new IncludeObject($this->File,'OBOutput', $this)) !== false) {
print $this->Contents;
}
}
Now, whenever an object wants to signal its listeners that an event happened, they need to use the Notify function.
So at one point I had to send a reference to the object to its listeners, so they could have access to the included content ($Contents).
So I used Notify() passing $this as the parameter. When I try to access the $Content from the other object, it simply returns as an empty string.
Note: The object functions as it should in a normal context, only when I pass it as a function parameter I can't access its $Content.
The constructor for IncludeObject():
public function __construct($F= '', $M= -1, &$IV = null) {
$this->File = $F;
$this->Func = $M;
$this->IncEv = $IV;
}
The listener accesses the IncludeEvent object like this:
public function OBOutput($IncObj){
print $IncObj->IncEv->Contents;
return false;
}
So yeah, how do I access long strings from object references? It works fine with short strings, but something like a long included file just returns empty.
Notify() is defined as:
final public function Notify($EvObj = null) {
foreach ($this->listen_list as $z) {
$d = call_user_func(array($z, $EvObj->Func), $EvObj);
if ($d == true || is_null($d)) {
return true;
}
}
return false;
}
how do I access long strings from object references? It works fine with short strings, but something like a long included file just returns empty.
you're barking up the wrong tree. and btw, you don't need that & at all. objects are passed by reference without it (not in PHP 4, but you shouldn't be using that nowadays).

Categories