Skip to content

Commit 2ef4135

Browse files
committed
Merge branch 'aliasing' of https://github.com/jasonvarga/tinker into jasonvarga-aliasing
2 parents ca586a7 + ac72016 commit 2ef4135

File tree

9 files changed

+203
-21
lines changed

9 files changed

+203
-21
lines changed

composer.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"symfony/var-dumper": "^4.0|^5.0"
1919
},
2020
"require-dev": {
21-
"phpunit/phpunit": "^8.0"
21+
"mockery/mockery": "^1.3.1",
22+
"phpunit/phpunit": "^8.0|^9.0"
2223
},
2324
"suggest": {
2425
"illuminate/database": "The Illuminate Database package (^6.0|^7.0)."
@@ -30,7 +31,9 @@
3031
},
3132
"autoload-dev": {
3233
"psr-4": {
33-
"Laravel\\Tinker\\Tests\\": "tests/"
34+
"Laravel\\Tinker\\Tests\\": "tests/",
35+
"App\\": "tests/fixtures/app",
36+
"One\\Two\\": "tests/fixtures/vendor/one/two"
3437
}
3538
},
3639
"extra": {

config/tinker.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,19 @@
3232
'App\Nova',
3333
],
3434

35+
/*
36+
|--------------------------------------------------------------------------
37+
| Alias Whitelist
38+
|--------------------------------------------------------------------------
39+
|
40+
| Tinker will not automatically alias classes in your vendor namespaces.
41+
| However, you may wish to allow this for certain classes, which you
42+
| may accomplish by listing the classes in the following array.
43+
|
44+
*/
45+
46+
'alias' => [
47+
//
48+
],
49+
3550
];

src/ClassAliasAutoloader.php

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,39 @@ class ClassAliasAutoloader
2121
*/
2222
protected $classes = [];
2323

24+
/**
25+
* Path to the vendor directory.
26+
*
27+
* @var string
28+
*/
29+
protected $vendorPath;
30+
31+
/**
32+
* Explicitly included namespaces/classes.
33+
*
34+
* @var \Illuminate\Support\Collection
35+
*/
36+
protected $includedAliases;
37+
38+
/**
39+
* Excluded namespaces/classes.
40+
*
41+
* @var \Illuminate\Support\Collection
42+
*/
43+
protected $excludedAliases;
44+
2445
/**
2546
* Register a new alias loader instance.
2647
*
2748
* @param \Psy\Shell $shell
2849
* @param string $classMapPath
50+
* @param array $includedAliases
51+
* @param array $excludedAliases
2952
* @return static
3053
*/
31-
public static function register(Shell $shell, $classMapPath)
54+
public static function register(Shell $shell, $classMapPath, array $includedAliases = [], array $excludedAliases = [])
3255
{
33-
return tap(new static($shell, $classMapPath), function ($loader) {
56+
return tap(new static($shell, $classMapPath, $includedAliases, $excludedAliases), function ($loader) {
3457
spl_autoload_register([$loader, 'aliasClass']);
3558
});
3659
}
@@ -40,26 +63,21 @@ public static function register(Shell $shell, $classMapPath)
4063
*
4164
* @param \Psy\Shell $shell
4265
* @param string $classMapPath
66+
* @param array $includedAliases
67+
* @param array $excludedAliases
4368
* @return void
4469
*/
45-
public function __construct(Shell $shell, $classMapPath)
70+
public function __construct(Shell $shell, $classMapPath, array $includedAliases = [], array $excludedAliases = [])
4671
{
4772
$this->shell = $shell;
48-
49-
$vendorPath = dirname(dirname($classMapPath));
73+
$this->vendorPath = dirname(dirname($classMapPath));
74+
$this->includedAliases = collect($includedAliases);
75+
$this->excludedAliases = collect($excludedAliases);
5076

5177
$classes = require $classMapPath;
5278

53-
$excludedAliases = collect(config('tinker.dont_alias', []));
54-
5579
foreach ($classes as $class => $path) {
56-
if (! Str::contains($class, '\\') || Str::startsWith($path, $vendorPath)) {
57-
continue;
58-
}
59-
60-
if (! $excludedAliases->filter(function ($alias) use ($class) {
61-
return Str::startsWith($class, $alias);
62-
})->isEmpty()) {
80+
if (! $this->isAliasable($class, $path)) {
6381
continue;
6482
}
6583

@@ -83,9 +101,7 @@ public function aliasClass($class)
83101
return;
84102
}
85103

86-
$fullName = isset($this->classes[$class])
87-
? $this->classes[$class]
88-
: false;
104+
$fullName = $this->classes[$class] ?? false;
89105

90106
if ($fullName) {
91107
$this->shell->writeStdout("[!] Aliasing '{$class}' to '{$fullName}' for this Tinker session.\n");
@@ -113,4 +129,35 @@ public function __destruct()
113129
{
114130
$this->unregister();
115131
}
132+
133+
/**
134+
* Whether a class may be aliased.
135+
*
136+
* @param string $class
137+
* @param string $path
138+
*/
139+
public function isAliasable($class, $path)
140+
{
141+
if (! Str::contains($class, '\\')) {
142+
return false;
143+
}
144+
145+
if (! $this->includedAliases->filter(function ($alias) use ($class) {
146+
return Str::startsWith($class, $alias);
147+
})->isEmpty()) {
148+
return true;
149+
}
150+
151+
if (Str::startsWith($path, $this->vendorPath)) {
152+
return false;
153+
}
154+
155+
if (! $this->excludedAliases->filter(function ($alias) use ($class) {
156+
return Str::startsWith($class, $alias);
157+
})->isEmpty()) {
158+
return false;
159+
}
160+
161+
return true;
162+
}
116163
}

src/Console/TinkerCommand.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ public function handle()
6363

6464
$path .= '/composer/autoload_classmap.php';
6565

66-
$loader = ClassAliasAutoloader::register($shell, $path);
66+
$config = $this->getLaravel()->make('config');
67+
68+
$loader = ClassAliasAutoloader::register(
69+
$shell, $path, $config->get('tinker.alias', []), $config->get('tinker.dont_alias', [])
70+
);
6771

6872
if ($code = $this->option('execute')) {
6973
$shell->execute($code);
@@ -97,7 +101,9 @@ protected function getCommands()
97101
}
98102
}
99103

100-
foreach (config('tinker.commands', []) as $command) {
104+
$config = $this->getLaravel()->make('config');
105+
106+
foreach ($config->get('tinker.commands', []) as $command) {
101107
$commands[] = $this->getApplication()->resolve($command);
102108
}
103109

tests/ClassAliasAutoloaderTest.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
namespace Laravel\Tinker\Tests;
4+
5+
use Laravel\Tinker\ClassAliasAutoloader;
6+
use Mockery;
7+
use PHPUnit\Framework\TestCase;
8+
use Psy\Shell;
9+
10+
class ClassAliasAutoloaderTest extends TestCase
11+
{
12+
public function setUp(): void
13+
{
14+
$this->classmapPath = __DIR__.'/fixtures/vendor/composer/autoload_classmap.php';
15+
}
16+
17+
public function tearDown(): void
18+
{
19+
$this->loader->unregister();
20+
}
21+
22+
public function testCanAliasClasses()
23+
{
24+
$this->loader = ClassAliasAutoloader::register(
25+
$shell = Mockery::mock(Shell::class),
26+
$this->classmapPath
27+
);
28+
29+
$shell->shouldReceive('writeStdout')
30+
->with("[!] Aliasing 'Bar' to 'App\Foo\Bar' for this Tinker session.\n")
31+
->once();
32+
33+
$this->assertTrue(class_exists('Bar'));
34+
$this->assertInstanceOf(\App\Foo\Bar::class, new \Bar);
35+
}
36+
37+
public function testCanExcludeNamespacesFromAliasing()
38+
{
39+
$this->loader = ClassAliasAutoloader::register(
40+
$shell = Mockery::mock(Shell::class),
41+
$this->classmapPath,
42+
[],
43+
['App\Baz']
44+
);
45+
46+
$shell->shouldNotReceive('writeStdout');
47+
48+
$this->assertFalse(class_exists('Qux'));
49+
}
50+
51+
public function testVendorClassesAreExcluded()
52+
{
53+
$this->loader = ClassAliasAutoloader::register(
54+
$shell = Mockery::mock(Shell::class),
55+
$this->classmapPath
56+
);
57+
58+
$shell->shouldNotReceive('writeStdout');
59+
60+
$this->assertFalse(class_exists('Three'));
61+
}
62+
63+
public function testVendorClassesCanBeWhitelisted()
64+
{
65+
$this->loader = ClassAliasAutoloader::register(
66+
$shell = Mockery::mock(Shell::class),
67+
$this->classmapPath,
68+
['One\Two']
69+
);
70+
71+
$shell->shouldReceive('writeStdout')
72+
->with("[!] Aliasing 'Three' to 'One\Two\Three' for this Tinker session.\n")
73+
->once();
74+
75+
$this->assertTrue(class_exists('Three'));
76+
$this->assertInstanceOf(\One\Two\Three::class, new \Three);
77+
}
78+
}

tests/fixtures/app/Baz/Qux.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace App\Baz;
4+
5+
class Qux
6+
{
7+
//
8+
}

tests/fixtures/app/Foo/Bar.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace App\Foo;
4+
5+
class Bar
6+
{
7+
//
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
$vendorDir = dirname(dirname(__FILE__));
3+
$baseDir = dirname($vendorDir);
4+
return array(
5+
'App\\Foo\\Bar' => $baseDir.'/app/Foo/Bar.php',
6+
'App\\Baz\\Qux' => $baseDir.'/app/Baz/Qux.php',
7+
'One\\Two\\Three' => $vendorDir.'/one/two/src/Three.php',
8+
'Four\\Five\\Six' => $vendorDir.'/four/five/src/Six.php',
9+
);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace One\Two;
4+
5+
class Three
6+
{
7+
//
8+
}

0 commit comments

Comments
 (0)