A new version of Laravel is available from 24 January 2017 and, as usual, it comes with a lot of new features.

Among them, there is one that takes advantage of the dynamic nature of PHP. Some out of there will contempt this, but I find it awesome!

As you may already know Laravel provides the Collection class that is

a fluent, convenient wrapper for working with arrays of data.

If you never seen what they are here it is an example:

$collection = collect([collect([1, 2]), collect([2, 3, 4])])->map(function ($collection) {

return $collection->average();

});

Now instead you can do something like this:

collect([collect([1, 2]), collect([2, 3, 4])])->map->average();

You can already see some magic (methods) in action here. Do you want to try? Open up tinker and try it yourself! Please remember that from 5.4 you need to install tinker manually.

It’s just a terminal.

Deep Dive Into The Source Code

Everything begins with the Collection class. There is now defined this property:

/**

* The methods that can be proxied.

*

* @var array

*/

protected static $proxies = [

'contains', 'each', 'every', 'filter', 'first', 'map',

'partition', 'reject', 'sortBy', 'sortByDesc', 'sum',

];

Those are the methods that can be used with this HighOrder Collection feature.

If you look at the example, you can clearly see that we try to access to a property dynamically. That’s why there is the __get magic method in action:

/**

* Dynamically access collection proxies.

*

* @param string $key

* @return mixed

*

* @throws \Exception

*/

public function __get($key)

{

if (! in_array($key, static::$proxies)) {

throw new Exception("Property [{$key}] does not exist on this collection instance.");

}



return new HigherOrderCollectionProxy($this, $key);

}

So far, so simple. It just checks that the $key (which corresponds to the collection method) can be proxied. If true, it creates a new HigherOrderCollectionProxy passing the collection instance and the callee.

__get and __call

The HigherOrderCollectionProxy class contains two magic methods: __get and __call. So when do they are called?

I try to explain that with two examples. Consider this first example:

collect([collect([1, 2]), collect([2, 3, 4])])->map->average();

When we ->map we get an instance of HighOrderCollectionProxy.

In case you do not trust me.

So now we want to map the average method on each element of the collection and we do ->map->average(). Since HighOrderCollectionProxy class does not have any average() method defined, PHP will trigger the __call magic method that it is defined as follow:

/**

* Proxy a method call onto the collection items.

*

* @param string $method

* @param array $parameters

* @return mixed

*/

public function __call($method, $parameters)

{

return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {

return $value->{$method}(...$parameters);

});

}

So basically this method “writes” for us this code:

collect([collect([1, 2]), collect([2, 3, 4])])->map(function ($value){

return $value->average();

});

This works because the map method, and all proxied methods accept a callable as a parameter.

If we consider this example

$a = new class { public $prop = 3; };

collect([new $a, new $a, new $a])->sum->prop;

What do you think it will print? Before to show you the solution let’s parse this code.

We first define an anonymous class with a property that holds the integer 3. Then we sum each element based on this property.

So the actual code looks like.

collect([new $a, new $a, new $a])->sum(function ($value){

return $value->prop;

});

In this case, the PHP engine triggers the __get magic method on the HighOrderCollectionProxy class.

/**

* Proxy accessing an attribute onto the collection items.

*

* @param string $key

* @return mixed

*/

public function __get($key)

{

return $this->collection->{$this->method}(function ($value) use ($key) {

return is_array($value) ? $value[$key] : $value->{$key};

});

}

Note how it checks whether he needs to access to an array or to a property. So finally the result!

Boring stuff.

So now you can do some magic at work!