From f647b4a29d87a0c140a6f4c98c2650f8b0322007 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Mon, 4 Mar 2019 07:20:52 -0500 Subject: [PATCH] Fix DebugCommand when chain loader is involved --- .../Bridge/Twig/Command/DebugCommand.php | 92 ++++++++++++------- .../Twig/Tests/Command/DebugCommandTest.php | 16 +++- src/Symfony/Bridge/Twig/composer.json | 2 +- 3 files changed, 76 insertions(+), 34 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 5ff57e1cf49fc..5c22291ff0453 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -20,6 +20,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Finder\Finder; use Twig\Environment; +use Twig\Loader\ChainLoader; use Twig\Loader\FilesystemLoader; /** @@ -36,6 +37,7 @@ class DebugCommand extends Command private $bundlesMetadata; private $twigDefaultPath; private $rootDir; + private $filesystemLoaders; public function __construct(Environment $twig, string $projectDir = null, array $bundlesMetadata = [], string $twigDefaultPath = null, string $rootDir = null) { @@ -87,7 +89,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $name = $input->getArgument('name'); $filter = $input->getOption('filter'); - if (null !== $name && !$this->twig->getLoader() instanceof FilesystemLoader) { + if (null !== $name && [] === $this->getFilesystemLoaders()) { throw new InvalidArgumentException(sprintf('Argument "name" not supported, it requires the Twig loader "%s"', FilesystemLoader::class)); } @@ -150,9 +152,11 @@ private function displayPathsText(SymfonyStyle $io, string $name) $message = 'No template paths configured for your application'; } else { $message = sprintf('No template paths configured for "@%s" namespace', $namespace); - $namespaces = $this->twig->getLoader()->getNamespaces(); - foreach ($this->findAlternatives($namespace, $namespaces) as $namespace) { - $alternatives[] = '@'.$namespace; + foreach ($this->getFilesystemLoaders() as $loader) { + $namespaces = $loader->getNamespaces(); + foreach ($this->findAlternatives($namespace, $namespaces) as $namespace) { + $alternatives[] = '@'.$namespace; + } } } @@ -243,25 +247,25 @@ private function displayGeneralJson(SymfonyStyle $io, $filter) private function getLoaderPaths(string $name = null): array { - /** @var FilesystemLoader $loader */ - $loader = $this->twig->getLoader(); $loaderPaths = []; - $namespaces = $loader->getNamespaces(); - if (null !== $name) { - $namespace = $this->parseTemplateName($name)[0]; - $namespaces = array_intersect([$namespace], $namespaces); - } + foreach ($this->getFilesystemLoaders() as $loader) { + $namespaces = $loader->getNamespaces(); + if (null !== $name) { + $namespace = $this->parseTemplateName($name)[0]; + $namespaces = array_intersect([$namespace], $namespaces); + } - foreach ($namespaces as $namespace) { - $paths = array_map([$this, 'getRelativePath'], $loader->getPaths($namespace)); + foreach ($namespaces as $namespace) { + $paths = array_map([$this, 'getRelativePath'], $loader->getPaths($namespace)); - if (FilesystemLoader::MAIN_NAMESPACE === $namespace) { - $namespace = '(None)'; - } else { - $namespace = '@'.$namespace; - } + if (FilesystemLoader::MAIN_NAMESPACE === $namespace) { + $namespace = '(None)'; + } else { + $namespace = '@'.$namespace; + } - $loaderPaths[$namespace] = $paths; + $loaderPaths[$namespace] = $paths; + } } return $loaderPaths; @@ -437,22 +441,22 @@ private function error(SymfonyStyle $io, string $message, array $alternatives = private function findTemplateFiles(string $name): array { - /** @var FilesystemLoader $loader */ - $loader = $this->twig->getLoader(); - $files = []; list($namespace, $shortname) = $this->parseTemplateName($name); - foreach ($loader->getPaths($namespace) as $path) { - if (!$this->isAbsolutePath($path)) { - $path = $this->projectDir.'/'.$path; - } - $filename = $path.'/'.$shortname; + $files = []; + foreach ($this->getFilesystemLoaders() as $loader) { + foreach ($loader->getPaths($namespace) as $path) { + if (!$this->isAbsolutePath($path)) { + $path = $this->projectDir.'/'.$path; + } + $filename = $path.'/'.$shortname; - if (is_file($filename)) { - if (false !== $realpath = realpath($filename)) { - $files[] = $this->getRelativePath($realpath); - } else { - $files[] = $this->getRelativePath($filename); + if (is_file($filename)) { + if (false !== $realpath = realpath($filename)) { + $files[] = $this->getRelativePath($realpath); + } else { + $files[] = $this->getRelativePath($filename); + } } } } @@ -535,4 +539,28 @@ private function isAbsolutePath(string $file): bool { return strspn($file, '/\\', 0, 1) || (\strlen($file) > 3 && ctype_alpha($file[0]) && ':' === $file[1] && strspn($file, '/\\', 2, 1)) || null !== parse_url($file, PHP_URL_SCHEME); } + + /** + * @return FilesystemLoader[] + */ + private function getFilesystemLoaders(): array + { + if (null !== $this->filesystemLoaders) { + return $this->filesystemLoaders; + } + $this->filesystemLoaders = []; + + $loader = $this->twig->getLoader(); + if ($loader instanceof FilesystemLoader) { + $this->filesystemLoaders[] = $loader; + } elseif ($loader instanceof ChainLoader) { + foreach ($loader->getLoaders() as $l) { + if ($l instanceof FilesystemLoader) { + $this->filesystemLoaders[] = $l; + } + } + } + + return $this->filesystemLoaders; + } } diff --git a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php index 59c8ed045c8f8..76eb10884bbd0 100644 --- a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php @@ -16,6 +16,7 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; use Twig\Environment; +use Twig\Loader\ChainLoader; use Twig\Loader\FilesystemLoader; class DebugCommandTest extends TestCase @@ -279,7 +280,16 @@ public function getDebugTemplateNameTestData() ]; } - private function createCommandTester(array $paths = [], array $bundleMetadata = [], string $defaultPath = null, string $rootDir = null): CommandTester + public function testDebugTemplateNameWithChainLoader() + { + $tester = $this->createCommandTester(['templates/' => null], [], null, null, true); + $ret = $tester->execute(['name' => 'base.html.twig'], ['decorated' => false]); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('[OK]', $tester->getDisplay()); + } + + private function createCommandTester(array $paths = [], array $bundleMetadata = [], string $defaultPath = null, string $rootDir = null, bool $useChainLoader = false): CommandTester { $projectDir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; $loader = new FilesystemLoader([], $projectDir); @@ -291,6 +301,10 @@ private function createCommandTester(array $paths = [], array $bundleMetadata = } } + if ($useChainLoader) { + $loader = new ChainLoader([$loader]); + } + $application = new Application(); $application->add(new DebugCommand(new Environment($loader), $projectDir, $bundleMetadata, $defaultPath, $rootDir)); $command = $application->find('debug:twig'); diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 24034230f8f49..9405dc2c9c3f4 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^7.1.3", "symfony/contracts": "^1.0.2", - "twig/twig": "^1.37.1|^2.6.2" + "twig/twig": "^1.38.1|^2.7.1" }, "require-dev": { "symfony/asset": "~3.4|~4.0",