Calling Closure Assigned to Object Property Directly

Calling closure assigned to object property directly

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.

Declaring closure to class attribute in PHP

PHP Currently doesn't allow directly calling a function stored as an object property.

It allows properties and methods of an object to have the same name actually.

One suggested solution to this issue is from another, almost identical question

class Totalizer
{
public $count;

public function __construct()
{
$this->count = function ($product) {
return $product;
};
}

public function __call($method, $args)
{
if (is_callable(array($this, $method))) {
return call_user_func_array($this->$method, $args);
} else {
// else throw exception
}
}
}

Calling closure assigned to object property directly

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.

PHP: Using globally defined closure in a class method

Because $this->globalfunction is a variable, not a method, you need to wrap it in parentheses to force PHP to evaluate $this->globalfunction before attempting to call it as a function i.e.

function testFunc(){
($this->globalFunction)('a');
}

Output:

works a

Demo on 3v4l.org

PHP closure function appended to stdObject and chained

Because this is currently a limitation of PHP. What you are doing is logical and should be possible. In fact, you can work around the limitation by writing:

function one(){
call_user_func($this->two()->func);
}

or

function one(){
$f = $this->two()->func;
$f();
}

Stupid, I know.

How to call a closure that is a class variable?

In PHP, methods and properties are in a separate namespace (you can have a method and a property with the same name), and whether you are accessing a property or a method depends of the syntax you are using to do so.

$expr->something() is a method call, so PHP will search something in the class' list of methods.

$expr->something is a property fetch, so PHP will search something in the class' list of properties.

$myInstance->lambda(); is parsed as a method call, so PHP searches for a method named lambda in your class, but there is no such method (hence the Call to undefined method error).

So you have to use the fetch property syntax to fetch the lambda, and then call it.

  • Since PHP 7.0, you can do this with ($obj->lambda)():

    ($obj->lambda)();

    The parentheses make sure that PHP parses ($obj->lambda) as fetch the property named lambda. Then, () calls the result of fetching the property.

  • or you can do this with ->lambda->__invoke():

    $myInstance = new MyClass();
    $myInstance->lambda->__invoke();

    __invoke is one of PHP's magic methods. When an object implements this method, it becomes invokable: it can be called using the $var() syntax. Anonymous functions are instances of Closure, which implements __invoke.

  • Or assign it to a local variable:

    $lambda = $myInstance->lambda;
    $lambda();
  • Or call it using call_user_func:

    call_user_func($myInstance->lambda);

    call_user_func can call any callable, including anonymous functions.

  • Alternatively, if this is a common pattern in your code, you can setup a __call method to forward calls to your lambda:

    class MyClass
    {
    private $lambda;

    public function __construct()
    {
    $this->lambda = function() {
    echo "Hello world!\n";
    };
    }

    public function __call($name, $args)
    {
    return call_user_func_array($this->$name, $args);
    }
    }

    Now this works:

    $myInstance = new MyClass();
    $myInstance->lambda();

    Since PHP 5.4 you can even do that in a trait:

    trait LambdasAsMethods
    {
    public function __call($name, $args)
    {
    return call_user_func_array($this->$name, $args);
    }
    }

    class MyClass
    {
    use LambdasAsMethods;

    private $lambda;

    public function __construct()
    {
    $this->lambda = function() {
    echo "Hello World!\n";
    };
    }
    }

    $myInstance = new MyClass();
    $myInstance->lambda();

Closure object cannot have properties in Laravel middleware

Your return statement is wrong, you have to return $next($request); instead of return $next;

I would personally change your code to this:

use Illuminate\Http\Response;

public function __construct()
{
parent::__construct();

// Return 404 if not AJAX request
$this->middleware(function ($request, $next) {
if (! $request->ajax()) {
abort(Response::HTTP_NOT_FOUND);
}

return $next($request);
})->only(['list', 'publish', 'unpublish', 'delete']);
}

PHP Call closure under stdClass

In general it's nicer to just make a class if you want to attach functions to objects
but in case you really want this then this should work:

$x = new \stdClass();
$x->cast = function($y){
return $y;
};

$y = $x->cast;

var_dump($y(5));

or

call_user_func($x->cast, 5);


Related Topics



Leave a reply



Submit