Skip to content

Commit 9e4b005

Browse files
authored
Merge pull request #39 from laravel/fix/first-class-callables-namespaces
[1.x] Fixes first class callables namespaces
2 parents 6d6092c + c1311e0 commit 9e4b005

File tree

6 files changed

+124
-1
lines changed

6 files changed

+124
-1
lines changed

src/Support/ReflectionClosure.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public function getCode()
9696
$builtin_types = self::getBuiltinTypes();
9797
$class_keywords = ['self', 'static', 'parent'];
9898

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

102102
$_file = var_export($fileName, true);
@@ -1133,6 +1133,23 @@ protected function fetchItems()
11331133
static::$structures[$key] = $structures;
11341134
}
11351135

1136+
/**
1137+
* Returns the namespace associated to the closure.
1138+
*
1139+
* @return string
1140+
*/
1141+
protected function getClosureNamespaceName()
1142+
{
1143+
$ns = $this->getNamespaceName();
1144+
1145+
// First class callables...
1146+
if ($this->getName() !== '{closure}' && empty($ns) && ! is_null($this->getClosureScopeClass())) {
1147+
$ns = $this->getClosureScopeClass()->getNamespaceName();
1148+
}
1149+
1150+
return $ns;
1151+
}
1152+
11361153
/**
11371154
* Parse the given token.
11381155
*

tests/Fixtures/Model.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Tests\Fixtures;
4+
5+
class Model
6+
{
7+
public function make(Model $model): Model
8+
{
9+
return new Model();
10+
}
11+
12+
public static function staticMake(Model $model): Model
13+
{
14+
return new Model();
15+
}
16+
}

tests/ReflectionClosure5Test.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use Foo\Baz\Qux;
55
use Foo\Baz\Qux\Forest;
66
use Laravel\SerializableClosure\Support\ReflectionClosure;
7+
use Tests\Fixtures\Model;
78

89
test('is short closure', function () {
910
$f1 = fn () => 1;
@@ -152,6 +153,28 @@
152153
expect($f)->toBeCode($e);
153154
});
154155

156+
test('from callable namespaces', function () {
157+
$f = Closure::fromCallable([new Model, 'make']);
158+
159+
$e = 'function (\Tests\Fixtures\Model $model): \Tests\Fixtures\Model
160+
{
161+
return new \Tests\Fixtures\Model();
162+
}';
163+
164+
expect($f)->toBeCode($e);
165+
});
166+
167+
test('from static callable namespaces', function () {
168+
$f = Closure::fromCallable([Model::class, 'staticMake']);
169+
170+
$e = 'static function (\Tests\Fixtures\Model $model): \Tests\Fixtures\Model
171+
{
172+
return new \Tests\Fixtures\Model();
173+
}';
174+
175+
expect($f)->toBeCode($e);
176+
});
177+
155178
// Helpers
156179
function c(Closure $closure)
157180
{

tests/ReflectionClosurePhp81Test.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
use Foo\Baz\Qux\Forest;
44
use Some\ClassName as ClassAlias;
5+
use Tests\Fixtures\Model;
6+
use function Tests\Fixtures\{makeModel};
57

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

261+
test('first-class callable namespaces', function () {
262+
$model = new Model();
263+
264+
$f = $model->make(...);
265+
266+
$e = 'function (\Tests\Fixtures\Model $model): \Tests\Fixtures\Model
267+
{
268+
return new \Tests\Fixtures\Model();
269+
}';
270+
271+
expect($f)->toBeCode($e);
272+
});
273+
274+
test('static first-class callable namespaces', function () {
275+
$model = new Model();
276+
277+
$f = $model->staticMake(...);
278+
279+
$e = 'static function (\Tests\Fixtures\Model $model): \Tests\Fixtures\Model
280+
{
281+
return new \Tests\Fixtures\Model();
282+
}';
283+
284+
expect($f)->toBeCode($e);
285+
});
286+
259287
class ReflectionClosurePhp81Service
260288
{
261289
}

tests/SerializerPhp81Test.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
use Tests\Fixtures\Model;
4+
35
enum SerializerGlobalEnum {
46
case Admin;
57
case Guest;
@@ -280,6 +282,26 @@ enum SerializerScopedBackedEnum: string {
280282
expect($object->getPrivate())->toBe('private');
281283
})->with('serializers');
282284

285+
test('first-class callable namespaces', function () {
286+
$model = new Model();
287+
288+
$f = $model->make(...);
289+
290+
$f = s($f);
291+
292+
expect($f(new Model))->toBeInstanceOf(Model::class);
293+
})->with('serializers');
294+
295+
test('static first-class callable namespaces', function () {
296+
$model = new Model();
297+
298+
$f = $model->staticMake(...);
299+
300+
$f = s($f);
301+
302+
expect($f(new Model))->toBeInstanceOf(Model::class);
303+
})->with('serializers');
304+
283305
interface SerializerPhp81HasId {}
284306
interface SerializerPhp81HasName {}
285307

tests/SerializerTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use Laravel\SerializableClosure\SerializableClosure;
44
use Laravel\SerializableClosure\Serializers\Signed;
55
use Laravel\SerializableClosure\Support\ReflectionClosure;
6+
use Tests\Fixtures\Model;
67

78
test('closure use return value', function () {
89
$a = 100;
@@ -384,6 +385,22 @@ function () {
384385
expect($r)->toEqual('Hi');
385386
})->with('serializers');
386387

388+
test('from callable namespaces', function () {
389+
$f = Closure::fromCallable([new Model, 'make']);
390+
391+
$f = s($f);
392+
393+
expect($f(new Model))->toBeInstanceOf(Model::class);
394+
})->with('serializers');
395+
396+
test('from static callable namespaces', function () {
397+
$f = Closure::fromCallable([new Model, 'staticMake']);
398+
399+
$f = s($f);
400+
401+
expect($f(new Model))->toBeInstanceOf(Model::class);
402+
})->with('serializers');
403+
387404
class A
388405
{
389406
protected static function aStaticProtected()

0 commit comments

Comments
 (0)