Pass callback wrapper parameters by reference (#2485)

Because invokable class objects are not directly called and instead it's the callback wrapper that calls these objects, it's currently not possible to receive arguments by reference on an invokable class.

To fix this we pass the arguments by reference by default when calling the object in the callback wrapper.
This commit is contained in:
Sami Mazouz 2020-12-06 20:58:45 +01:00 committed by GitHub
parent 4389d3b1a0
commit 0754de8d1b
2 changed files with 142 additions and 2 deletions

View File

@ -24,10 +24,10 @@ class ContainerUtil
public static function wrapCallback($callback, Container $container)
{
if (is_string($callback)) {
$callback = function () use ($container, $callback) {
$callback = function (&...$args) use ($container, $callback) {
$callback = $container->make($callback);
return call_user_func_array($callback, func_get_args());
return $callback(...$args);
};
}

View File

@ -0,0 +1,140 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Tests\unit\Foundation;
use Flarum\Foundation\ContainerUtil;
use Flarum\Tests\unit\TestCase;
use Illuminate\Container\Container;
class ContainerUtilTest extends TestCase
{
private $container;
protected function setUp()
{
parent::setUp();
$this->container = new Container();
}
/** @test */
public function it_works_with_closures()
{
$callback = ContainerUtil::wrapCallback(function ($array) {
$array['key'] = 'newValue';
return 'return';
}, $this->container);
$array = ['key' => 'value'];
$return = $callback($array);
$this->assertEquals('value', $array['key']);
$this->assertEquals('return', $return);
}
/** @test */
public function it_works_with_invokable_classes()
{
$callback = ContainerUtil::wrapCallback(CustomInvokableClass::class, $this->container);
$array = ['key' => 'value2'];
$return = $callback($array);
$this->assertEquals('value2', $array['key']);
$this->assertEquals('return2', $return);
}
/** @test */
public function it_works_with_invokable_objects()
{
$callback = ContainerUtil::wrapCallback(new class {
public function __invoke($array)
{
$array['key'] = 'newValue5';
return 'return5';
}
}, $this->container);
$array = ['key' => 'value5'];
$return = $callback($array);
$this->assertEquals('value5', $array['key']);
$this->assertEquals('return5', $return);
}
/** @test */
public function it_allows_passing_args_by_reference_on_closures()
{
$callback = ContainerUtil::wrapCallback(function (&$array) {
$array['key'] = 'newValue3';
return 'return3';
}, $this->container);
$array = ['key' => 'value3'];
$return = $callback($array);
$this->assertEquals('newValue3', $array['key']);
$this->assertEquals('return3', $return);
}
/** @test */
public function it_allows_passing_args_by_reference_on_invokable_classes()
{
$callback = ContainerUtil::wrapCallback(SecondCustomInvokableClass::class, $this->container);
$array = ['key' => 'value4'];
$return = $callback($array);
$this->assertEquals('newValue4', $array['key']);
$this->assertEquals('return4', $return);
}
/** @test */
public function it_allows_passing_args_by_reference_on_invokable_objects()
{
$callback = ContainerUtil::wrapCallback(new class {
public function __invoke(&$array)
{
$array['key'] = 'newValue6';
return 'return6';
}
}, $this->container);
$array = ['key' => 'value6'];
$return = $callback($array);
$this->assertEquals('newValue6', $array['key']);
$this->assertEquals('return6', $return);
}
}
class CustomInvokableClass
{
public function __invoke($array)
{
$array['key'] = 'newValue2';
return 'return2';
}
}
class SecondCustomInvokableClass
{
public function __invoke(&$array)
{
$array['key'] = 'newValue4';
return 'return4';
}
}