4

Case

I am playing around on a laravel project to see if i can use closures for my implementation of a sorting interface, and i noticed that when i dd() my closure, it also shows the class in which the closure was created as a property.

Minimised Code

// in my Order model class, i have a function that will return a closure
public static function defaultSortFunction(){
    $sortColumn = property_exists(self::class,'defaultSortingColumn') ? self::$defaultSortingColumn : 'created_at';

    return function($p,$n)use($sortColumn){
        return $p->$sortColumn <=> $n->$sortColumn;
    };
}
// in one of my controller I use for testing, I added these 2 methods for testing
public function index(){
    $sortFunction = Order::defaultSortFunction();
    $this->someOtherFunction($sortFunction);
    return 'done';
}

private function someOtherFunction($fn){
    dd($fn);

    // $scopeModel = get_class($fn); => Closure
    
    // example of how I can use this value later
    // $scopeModel::take(10)->get()->sort($fn);
}

The result of the dd() inside someOtherFunction():

^ Closure($p, $n) {#1308 ▼
  class: "App\Order"
  use: {▼
    $sortColumn: "created_at"
  }
}

Question

From the result of the dd() it shows that the closure has a property that shows that it was defined in the class App\Order. Is there any way to access this value?

I have tried get_class($fn) but as expected it gives "Closure", and if i did $fn->class it gives an error saying Closure object cannot have properties.

Helioarch
  • 1,158
  • 5
  • 18

2 Answers2

6

You may use Reflection API on your closure which is a much cleaner way than debug_backtrace

// in one of my controller I use for testing, I added these 2 methods for testing
public function index(){
    $sortFunction = Order::defaultSortFunction();
    $this->someOtherFunction($sortFunction);
    return 'done';
}

private function someOtherFunction($fn){
    $reflectionClosure = new \ReflectionFunction($fn);
    dd($reflectionClosure->getClosureScopeClass()->getName());
}

getClosureScopeClass returns a ReflectionClass instance based on the class you need to find and getName finishes the job.

Shizzen83
  • 3,325
  • 3
  • 12
  • 32
  • 3
    Good answer. `$reflectionClosure->getClosureThis()` can be also used to get the instance to which the closure is bound to (internal `$this`). – mvorisek Jul 04 '20 at 10:30
0

You can of course inject the class name in to the closure via a parameter in your defaultSortFunction, but that's obviously not so nice.

You should be able extract the calling class yourself from the call stack using: https://www.php.net/manual/en/function.debug-backtrace.php

If you use the limit parameter you should be able to restrict it to only returning the calling class and no further back.

I don't know for sure, but I suspect it isn't particularly performant.

Howard Tomlinson
  • 71
  • 1
  • 2
  • 5
  • I understand that i can just simply pass the class name via parameter but i'm trying to figure out if i can access that `class` property of the closure to avoid 2 parameters. The debug backtrace you suggested is interesting but i'd have to call it inside my closure not outside if i understand it correctly – Helioarch Jul 01 '20 at 09:36
  • Yes you'd have to call that from inside the closure, but it should provide what you need. – Howard Tomlinson Jul 01 '20 at 11:00
  • A bit of searching with different text gives this thread, which has some great suggestions of how to condense it and make it more optimal : https://stackoverflow.com/questions/2110732/how-to-get-name-of-calling-function-method-in-php – Howard Tomlinson Jul 01 '20 at 11:01
  • Thanks for the suggestion but accessing the property itself is still what I want to know, I can use it more flexibly for other cases too. – Helioarch Jul 01 '20 at 22:25
  • You're out of luck on this one then; while the calling class name and function are available, the actual instance of that class is not. If you need the value of that property within the closure, you're either going to have to pass it by parameter, or instantiate a temporary copy of the class within the closure and interrogate it. – Howard Tomlinson Jul 02 '20 at 09:24