Skip to content

Commit 23d731c

Browse files
Search for matching PSR-0 or PSR-4 namespaces, and default to component's autoloader
1 parent 4aff8ba commit 23d731c

File tree

3 files changed

+85
-24
lines changed

3 files changed

+85
-24
lines changed

src/Util/ComposerAutoloaderFinder.php

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,57 +19,87 @@
1919
*/
2020
class ComposerAutoloaderFinder
2121
{
22-
/**
23-
* @var string
24-
*/
2522
private $rootNamespace;
2623

2724
/**
2825
* @var ClassLoader|null
2926
*/
3027
private $classLoader = null;
3128

32-
public function __construct(string $rootNamespace = 'App\\')
29+
public function __construct(string $rootNamespace)
3330
{
34-
$this->rootNamespace = rtrim($rootNamespace, '\\').'\\';
31+
$this->rootNamespace = [
32+
'psr0' => rtrim($rootNamespace, '\\'),
33+
'psr4' => rtrim($rootNamespace, '\\').'\\',
34+
];
3535
}
3636

3737
public function getClassLoader(): ClassLoader
3838
{
3939
if (null === $this->classLoader) {
40-
$this->classLoader = $this->findComposerClassLoader();
40+
$this->findComposerClassLoader();
4141
}
4242

4343
if (null === $this->classLoader) {
44-
throw new \Exception('Composer ClassLoader not found!');
44+
throw new \Exception("Could not find a Composer autoloader that autoloads from '{$this->rootNamespace['psr4']}'");
4545
}
4646

4747
return $this->classLoader;
4848
}
4949

50-
/**
51-
* @return ClassLoader|null
52-
*/
5350
private function findComposerClassLoader()
5451
{
5552
$autoloadFunctions = spl_autoload_functions();
5653

5754
foreach ($autoloadFunctions as $autoloader) {
58-
if (\is_array($autoloader) && isset($autoloader[0]) && \is_object($autoloader[0])) {
59-
if ($autoloader[0] instanceof ClassLoader
60-
&& isset($autoloader[0]->getPrefixesPsr4()[$this->rootNamespace])) {
61-
return $autoloader[0];
62-
}
63-
64-
if ($autoloader[0] instanceof DebugClassLoader
65-
&& \is_array($autoloader[0]->getClassLoader())
66-
&& $autoloader[0]->getClassLoader()[0] instanceof ClassLoader
67-
&& isset($autoloader[0]->getClassLoader()[0]->getPrefixesPsr4()[$this->rootNamespace])) {
68-
return $autoloader[0]->getClassLoader()[0];
69-
}
55+
$classLoader = $this->extractComposerClassLoader($autoloader);
56+
if ($classLoader && $this->locateMatchingClassLoader($classLoader)) {
57+
return;
58+
}
59+
}
60+
}
61+
62+
/**
63+
* @return ClassLoader|null
64+
*/
65+
private function extractComposerClassLoader(array $autoloader)
66+
{
67+
if (isset($autoloader[0]) && \is_object($autoloader[0])) {
68+
if ($autoloader[0] instanceof ClassLoader) {
69+
return $autoloader[0];
70+
}
71+
if ($autoloader[0] instanceof DebugClassLoader
72+
&& \is_array($autoloader[0]->getClassLoader())
73+
&& $autoloader[0]->getClassLoader()[0] instanceof ClassLoader) {
74+
return $autoloader[0]->getClassLoader()[0];
7075
}
7176
}
7277

7378
return null;
7479
}
80+
81+
private function locateMatchingClassLoader(ClassLoader $classLoader): bool
82+
{
83+
foreach ($classLoader->getPrefixesPsr4() as $prefix => $paths) {
84+
// We can default to using the autoloader containing this component if none are matching.
85+
if ('Symfony\\Bundle\\MakerBundle\\' === $prefix) {
86+
$this->classLoader = $classLoader;
87+
}
88+
if (0 === strpos($this->rootNamespace['psr4'], $prefix)) {
89+
$this->classLoader = $classLoader;
90+
91+
return true;
92+
}
93+
}
94+
95+
foreach ($classLoader->getPrefixes() as $prefix => $paths) {
96+
if (0 === strpos($this->rootNamespace['psr0'], $prefix)) {
97+
$this->classLoader = $classLoader;
98+
99+
return true;
100+
}
101+
}
102+
103+
return false;
104+
}
75105
}

tests/Util/AutoloaderUtilTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ private function createComposerAutoloaderFinder(array $composerJsonParams = null
8787
/** @var \PHPUnit_Framework_MockObject_MockObject|ComposerAutoloaderFinder $finder */
8888
$finder = $this
8989
->getMockBuilder(ComposerAutoloaderFinder::class)
90+
->setConstructorArgs(['App\\'])
9091
->getMock();
9192

9293
$finder

tests/Util/ComposerAutoloaderFinderTest.php

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class ComposerAutoloaderFinderTest extends TestCase
1010
{
1111
public static $getSplAutoloadFunctions = 'spl_autoload_functions';
1212

13-
private static $rootNamespace = 'Symfony\\Bundle\\MakerBundle\\';
13+
private static $rootNamespace = 'Fake\\It\\Till\\You\\Make\\It\\';
1414

1515
/**
1616
* @after
@@ -20,8 +20,19 @@ public function resetAutoloadFunction()
2020
self::$getSplAutoloadFunctions = 'spl_autoload_functions';
2121
}
2222

23-
public function testGetClassLoader()
23+
public function providerNamespaces(): \Generator
2424
{
25+
yield 'Configured PSR-0' => [rtrim(static::$rootNamespace, '\\'), null];
26+
yield 'Configured PSR-4' => [null, static::$rootNamespace];
27+
yield 'Fallback default' => [null, 'Symfony\\Bundle\\MakerBundle\\'];
28+
}
29+
30+
/**
31+
* @dataProvider providerNamespaces
32+
*/
33+
public function testGetClassLoader($psr0, $psr4)
34+
{
35+
$this->setupAutoloadFunctions($psr0, $psr4);
2536
$loader = (new ComposerAutoloaderFinder(static::$rootNamespace))->getClassLoader();
2637

2738
$this->assertInstanceOf(ClassLoader::class, $loader, 'Wrong ClassLoader found');
@@ -39,6 +50,25 @@ public function testGetClassLoaderWhenItIsEmpty()
3950
// throws \Exception
4051
(new ComposerAutoloaderFinder(static::$rootNamespace))->getClassLoader();
4152
}
53+
54+
/**
55+
* @param string|null $psr0
56+
* @param string|null $psr4
57+
*/
58+
private function setupAutoloadFunctions($psr0, $psr4)
59+
{
60+
self::$getSplAutoloadFunctions = function () use ($psr0, $psr4) {
61+
$loader = new ClassLoader();
62+
if ($psr0) {
63+
$loader->add($psr0, __DIR__);
64+
}
65+
if ($psr4) {
66+
$loader->addPsr4($psr4, __DIR__);
67+
}
68+
69+
return [[$loader, 'loadClass']];
70+
};
71+
}
4272
}
4373

4474
namespace Symfony\Bundle\MakerBundle\Util;

0 commit comments

Comments
 (0)