Skip to content

Commit

Permalink
Merge pull request #39 from laravel/fix/first-class-callables-namespaces
Browse files Browse the repository at this point in the history
[1.x] Fixes first class callables namespaces
  • Loading branch information
taylorotwell authored Feb 11, 2022
2 parents 6d6092c + c1311e0 commit 9e4b005
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 1 deletion.
19 changes: 18 additions & 1 deletion src/Support/ReflectionClosure.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public function getCode()
$builtin_types = self::getBuiltinTypes();
$class_keywords = ['self', 'static', 'parent'];

$ns = $this->getNamespaceName();
$ns = $this->getClosureNamespaceName();
$nsf = $ns == '' ? '' : ($ns[0] == '\\' ? $ns : '\\'.$ns);

$_file = var_export($fileName, true);
Expand Down Expand Up @@ -1133,6 +1133,23 @@ protected function fetchItems()
static::$structures[$key] = $structures;
}

/**
* Returns the namespace associated to the closure.
*
* @return string
*/
protected function getClosureNamespaceName()
{
$ns = $this->getNamespaceName();

// First class callables...
if ($this->getName() !== '{closure}' && empty($ns) && ! is_null($this->getClosureScopeClass())) {
$ns = $this->getClosureScopeClass()->getNamespaceName();
}

return $ns;
}

/**
* Parse the given token.
*
Expand Down
16 changes: 16 additions & 0 deletions tests/Fixtures/Model.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Tests\Fixtures;

class Model
{
public function make(Model $model): Model
{
return new Model();
}

public static function staticMake(Model $model): Model
{
return new Model();
}
}
23 changes: 23 additions & 0 deletions tests/ReflectionClosure5Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use Foo\Baz\Qux;
use Foo\Baz\Qux\Forest;
use Laravel\SerializableClosure\Support\ReflectionClosure;
use Tests\Fixtures\Model;

test('is short closure', function () {
$f1 = fn () => 1;
Expand Down Expand Up @@ -152,6 +153,28 @@
expect($f)->toBeCode($e);
});

test('from callable namespaces', function () {
$f = Closure::fromCallable([new Model, 'make']);

$e = 'function (\Tests\Fixtures\Model $model): \Tests\Fixtures\Model
{
return new \Tests\Fixtures\Model();
}';

expect($f)->toBeCode($e);
});

test('from static callable namespaces', function () {
$f = Closure::fromCallable([Model::class, 'staticMake']);

$e = 'static function (\Tests\Fixtures\Model $model): \Tests\Fixtures\Model
{
return new \Tests\Fixtures\Model();
}';

expect($f)->toBeCode($e);
});

// Helpers
function c(Closure $closure)
{
Expand Down
28 changes: 28 additions & 0 deletions tests/ReflectionClosurePhp81Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

use Foo\Baz\Qux\Forest;
use Some\ClassName as ClassAlias;
use Tests\Fixtures\Model;
use function Tests\Fixtures\{makeModel};

enum GlobalEnum {
case Admin;
Expand Down Expand Up @@ -256,6 +258,32 @@ enum ScopedBackedEnum: string {
expect($f)->toBeCode($e);
})->skip('Constants in anonymous classes is not supported.');

test('first-class callable namespaces', function () {
$model = new Model();

$f = $model->make(...);

$e = 'function (\Tests\Fixtures\Model $model): \Tests\Fixtures\Model
{
return new \Tests\Fixtures\Model();
}';

expect($f)->toBeCode($e);
});

test('static first-class callable namespaces', function () {
$model = new Model();

$f = $model->staticMake(...);

$e = 'static function (\Tests\Fixtures\Model $model): \Tests\Fixtures\Model
{
return new \Tests\Fixtures\Model();
}';

expect($f)->toBeCode($e);
});

class ReflectionClosurePhp81Service
{
}
Expand Down
22 changes: 22 additions & 0 deletions tests/SerializerPhp81Test.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

use Tests\Fixtures\Model;

enum SerializerGlobalEnum {
case Admin;
case Guest;
Expand Down Expand Up @@ -280,6 +282,26 @@ enum SerializerScopedBackedEnum: string {
expect($object->getPrivate())->toBe('private');
})->with('serializers');

test('first-class callable namespaces', function () {
$model = new Model();

$f = $model->make(...);

$f = s($f);

expect($f(new Model))->toBeInstanceOf(Model::class);
})->with('serializers');

test('static first-class callable namespaces', function () {
$model = new Model();

$f = $model->staticMake(...);

$f = s($f);

expect($f(new Model))->toBeInstanceOf(Model::class);
})->with('serializers');

interface SerializerPhp81HasId {}
interface SerializerPhp81HasName {}

Expand Down
17 changes: 17 additions & 0 deletions tests/SerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use Laravel\SerializableClosure\SerializableClosure;
use Laravel\SerializableClosure\Serializers\Signed;
use Laravel\SerializableClosure\Support\ReflectionClosure;
use Tests\Fixtures\Model;

test('closure use return value', function () {
$a = 100;
Expand Down Expand Up @@ -384,6 +385,22 @@ function () {
expect($r)->toEqual('Hi');
})->with('serializers');

test('from callable namespaces', function () {
$f = Closure::fromCallable([new Model, 'make']);

$f = s($f);

expect($f(new Model))->toBeInstanceOf(Model::class);
})->with('serializers');

test('from static callable namespaces', function () {
$f = Closure::fromCallable([new Model, 'staticMake']);

$f = s($f);

expect($f(new Model))->toBeInstanceOf(Model::class);
})->with('serializers');

class A
{
protected static function aStaticProtected()
Expand Down

0 comments on commit 9e4b005

Please sign in to comment.