PHP
Laravel
laravel5
PHP7

LaravelのHigherOrderCollectionProxyの仕組み

HigherOrderCollectionProxyとは


PHP

$collection = collect([collect([1, 2, 3, 4, 5]), collect([6, 7, 8, 9, 10])])->map(function ($collection) {

return $collection->average();
});

$collection = collect([collect([1, 2, 3, 4, 5]), collect([6, 7, 8, 9, 10])])->map->average();


上を下のようにメソッドチェーンで繋げられるようにしたものです。

collect([collect([1, 2, 3, 4, 5]), collect([6, 7, 8, 9, 10])])->map__get()メソッドを呼びHigherOrderCollectionProxyオブジェクトを返します。


/laravel/framework/src/Illuminate/Support/HigherOrderCollectionProxy.php

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);
}


phpではクラスに存在しないプロパティ(ここではmap)を読み込むとマジックメソッド__get()を呼び出します。

$keyはプロはティ名の文字列です。

そしてstatic::$proxiesの中にmapが入っているかをチェックしてから、HigherOrderCollectionProxyを返します。

第一引数にはcollect([collect([1, 2, 3, 4, 5]), collect([6, 7, 8, 9, 10])])

第二引数には'map'が入ります。


/laravel/framework/src/Illuminate/Support/Collection.php

/**

* The methods that can be proxied.
*
* @var array
*/

protected static $proxies = [
'average', 'avg', 'contains', 'each', 'every', 'filter', 'first',
'flatMap', 'groupBy', 'keyBy', 'map', 'max', 'min', 'partition',
'reject', 'some', 'sortBy', 'sortByDesc', 'sum', 'unique',
];


/laravel/framework/src/Illuminate/Support/HigherOrderCollectionProxy.php

public function __construct(Collection $collection, $method)

{
$this->method = $method;
$this->collection = $collection;
}

$this->method'map'$this->collectionCollectionオブジェクトが入ります。

そして->average()を呼び出します。

HigherOrderCollectionProxyにはaverage()メソッドはないので、マジックメソッド__call()が呼ばれます。


/laravel/framework/src/Illuminate/Support/HigherOrderCollectionProxy.php

/**

* 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);
});
}

$methodはメソッド名の文字列(ここでは'average')、$parametersは引数を格納した入れ配列(average()は引数がないので空の配列)になります。

つまり__call()の中身は


PHP

return $this->collection->map(function ($value) use ('average', []) {

return $value->average(...[]);
});

ということになります。


/laravel/framework/src/Illuminate/Support/Collection.php

/**

* Run a map over each of the items.
*
* @param callable $callback
* @return static
*/

public function map(callable $callback)
{
$keys = array_keys($this->items);

$items = array_map($callback, $this->items, $keys);

return new static(array_combine($keys, $items));
}


$callbackfunction ($value) use ('average', []) { return $value->average(...[]); }

$this->items[collect([1, 2, 3, 4, 5]), collect([6, 7, 8, 9, 10])]

$keys[0, 1]です。


array_map() は、array1 の各要素に callback 関数を適用した後、 その全ての要素を含む配列を返します。


array_map()

つまり__call()$valueにはそれぞれ、collect([1, 2, 3, 4, 5]とcollect([6, 7, 8, 9, 10])が入ります。

そしてaverage()で平均が計算され


PHP

var_dump($collection->all());

/*
array(2) {
[0]=>
int(3)
[1]=>
int(8)
}
*/


となります。