diff --git a/src/DependencyInjection/MakerExtension.php b/src/DependencyInjection/MakerExtension.php index 575408fa1..8051d8919 100644 --- a/src/DependencyInjection/MakerExtension.php +++ b/src/DependencyInjection/MakerExtension.php @@ -39,6 +39,9 @@ public function load(array $configs, ContainerBuilder $container) $rootNamespace = trim($config['root_namespace'], '\\'); + $autoloaderFinderDefinition = $container->getDefinition('maker.autoloader_finder'); + $autoloaderFinderDefinition->replaceArgument(0, $rootNamespace); + $makeCommandDefinition = $container->getDefinition('maker.generator'); $makeCommandDefinition->replaceArgument(1, $rootNamespace); diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 6440feb70..1031517e4 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -13,7 +13,9 @@ %kernel.project_dir% - + + + diff --git a/src/Util/ComposerAutoloaderFinder.php b/src/Util/ComposerAutoloaderFinder.php index 00d28d58e..db68dbe2d 100644 --- a/src/Util/ComposerAutoloaderFinder.php +++ b/src/Util/ComposerAutoloaderFinder.php @@ -19,11 +19,21 @@ */ class ComposerAutoloaderFinder { + private $rootNamespace; + /** * @var ClassLoader|null */ private $classLoader = null; + public function __construct(string $rootNamespace) + { + $this->rootNamespace = [ + 'psr0' => rtrim($rootNamespace, '\\'), + 'psr4' => rtrim($rootNamespace, '\\').'\\', + ]; + } + public function getClassLoader(): ClassLoader { if (null === $this->classLoader) { @@ -31,7 +41,7 @@ public function getClassLoader(): ClassLoader } if (null === $this->classLoader) { - throw new \Exception('Composer ClassLoader not found!'); + throw new \Exception("Could not find a Composer autoloader that autoloads from '{$this->rootNamespace['psr4']}'"); } return $this->classLoader; @@ -45,19 +55,61 @@ private function findComposerClassLoader() $autoloadFunctions = spl_autoload_functions(); foreach ($autoloadFunctions as $autoloader) { - if (\is_array($autoloader) && isset($autoloader[0]) && \is_object($autoloader[0])) { - if ($autoloader[0] instanceof ClassLoader) { - return $autoloader[0]; - } - - if ($autoloader[0] instanceof DebugClassLoader - && \is_array($autoloader[0]->getClassLoader()) - && $autoloader[0]->getClassLoader()[0] instanceof ClassLoader) { - return $autoloader[0]->getClassLoader()[0]; - } + $classLoader = $this->extractComposerClassLoader($autoloader); + if (null === $classLoader) { + continue; + } + + $finalClassLoader = $this->locateMatchingClassLoader($classLoader); + if (null !== $finalClassLoader) { + return $finalClassLoader; + } + } + + return null; + } + + /** + * @return ClassLoader|null + */ + private function extractComposerClassLoader(array $autoloader) + { + if (isset($autoloader[0]) && \is_object($autoloader[0])) { + if ($autoloader[0] instanceof ClassLoader) { + return $autoloader[0]; + } + if ($autoloader[0] instanceof DebugClassLoader + && \is_array($autoloader[0]->getClassLoader()) + && $autoloader[0]->getClassLoader()[0] instanceof ClassLoader) { + return $autoloader[0]->getClassLoader()[0]; } } return null; } + + /** + * @return ClassLoader|null + */ + private function locateMatchingClassLoader(ClassLoader $classLoader) + { + $makerClassLoader = null; + foreach ($classLoader->getPrefixesPsr4() as $prefix => $paths) { + if ('Symfony\\Bundle\\MakerBundle\\' === $prefix) { + $makerClassLoader = $classLoader; + } + if (0 === strpos($this->rootNamespace['psr4'], $prefix)) { + return $classLoader; + } + } + + foreach ($classLoader->getPrefixes() as $prefix => $paths) { + if (0 === strpos($this->rootNamespace['psr0'], $prefix)) { + return $classLoader; + } + } + + // We can default to using the autoloader containing this component if none are matching. + return $makerClassLoader ?: null; + } } diff --git a/tests/Maker/FunctionalTest.php b/tests/Maker/FunctionalTest.php index 64f765d7a..ff7e45312 100644 --- a/tests/Maker/FunctionalTest.php +++ b/tests/Maker/FunctionalTest.php @@ -205,7 +205,7 @@ public function getCommandTests() ]) ->addExtraDependencies('orm') ->setFixtureFilesPath(__DIR__.'/../fixtures/MakeFormForEntity') - ]; + ]; yield 'form_for_non_entity_dto' => [MakerTestDetails::createTest( $this->getMakerInstance(MakeForm::class), diff --git a/tests/Util/AutoloaderUtilTest.php b/tests/Util/AutoloaderUtilTest.php index f07e8c2c9..c95ba77a2 100644 --- a/tests/Util/AutoloaderUtilTest.php +++ b/tests/Util/AutoloaderUtilTest.php @@ -87,6 +87,7 @@ private function createComposerAutoloaderFinder(array $composerJsonParams = null /** @var \PHPUnit_Framework_MockObject_MockObject|ComposerAutoloaderFinder $finder */ $finder = $this ->getMockBuilder(ComposerAutoloaderFinder::class) + ->setConstructorArgs(['App\\']) ->getMock(); $finder diff --git a/tests/Util/ComposerAutoloaderFinderTest.php b/tests/Util/ComposerAutoloaderFinderTest.php index d76c473e3..5adc155cc 100644 --- a/tests/Util/ComposerAutoloaderFinderTest.php +++ b/tests/Util/ComposerAutoloaderFinderTest.php @@ -10,6 +10,8 @@ class ComposerAutoloaderFinderTest extends TestCase { public static $getSplAutoloadFunctions = 'spl_autoload_functions'; + private static $rootNamespace = 'Fake\\It\\Till\\You\\Make\\It\\'; + /** * @after */ @@ -18,9 +20,20 @@ public function resetAutoloadFunction() self::$getSplAutoloadFunctions = 'spl_autoload_functions'; } - public function testGetClassLoader() + public function providerNamespaces(): \Generator { - $loader = (new ComposerAutoloaderFinder())->getClassLoader(); + yield 'Configured PSR-0' => [rtrim(static::$rootNamespace, '\\'), null]; + yield 'Configured PSR-4' => [null, static::$rootNamespace]; + yield 'Fallback default' => [null, 'Symfony\\Bundle\\MakerBundle\\']; + } + + /** + * @dataProvider providerNamespaces + */ + public function testGetClassLoader($psr0, $psr4) + { + $this->setupAutoloadFunctions($psr0, $psr4); + $loader = (new ComposerAutoloaderFinder(static::$rootNamespace))->getClassLoader(); $this->assertInstanceOf(ClassLoader::class, $loader, 'Wrong ClassLoader found'); } @@ -35,7 +48,26 @@ public function testGetClassLoaderWhenItIsEmpty() }; // throws \Exception - (new ComposerAutoloaderFinder())->getClassLoader(); + (new ComposerAutoloaderFinder(static::$rootNamespace))->getClassLoader(); + } + + /** + * @param string|null $psr0 + * @param string|null $psr4 + */ + private function setupAutoloadFunctions($psr0, $psr4) + { + self::$getSplAutoloadFunctions = function () use ($psr0, $psr4) { + $loader = new ClassLoader(); + if ($psr0) { + $loader->add($psr0, __DIR__); + } + if ($psr4) { + $loader->addPsr4($psr4, __DIR__); + } + + return [[$loader, 'loadClass']]; + }; } }