From 8598b49c0425070e19330894950032060fadfae2 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Fri, 14 Mar 2025 11:55:41 +0100 Subject: [PATCH 1/6] move ide namespace lookup file generation into a service and its own mandatory cache warmer --- core-bundle/config/services.yaml | 14 ++- .../Twig/IDE/NamespaceLookupFileGenerator.php | 56 +++++++++++ .../Twig/IDE/NamespaceLookupFileWarmer.php | 41 ++++++++ .../Loader/ContaoFilesystemLoaderWarmer.php | 50 ---------- .../IDE/NamespaceLookupFileGeneratorTest.php | 75 +++++++++++++++ .../IDE/NamespaceLookupFileWarmerTest.php | 62 +++++++++++++ .../ContaoFilesystemLoaderWarmerTest.php | 93 +------------------ 7 files changed, 247 insertions(+), 144 deletions(-) create mode 100644 core-bundle/src/Twig/IDE/NamespaceLookupFileGenerator.php create mode 100644 core-bundle/src/Twig/IDE/NamespaceLookupFileWarmer.php create mode 100644 core-bundle/tests/Twig/IDE/NamespaceLookupFileGeneratorTest.php create mode 100644 core-bundle/tests/Twig/IDE/NamespaceLookupFileWarmerTest.php diff --git a/core-bundle/config/services.yaml b/core-bundle/config/services.yaml index b356c790c0a..1291b7948c6 100644 --- a/core-bundle/config/services.yaml +++ b/core-bundle/config/services.yaml @@ -1291,8 +1291,6 @@ services: class: Contao\CoreBundle\Twig\Loader\ContaoFilesystemLoaderWarmer arguments: - '@contao.twig.filesystem_loader' - - '%kernel.cache_dir%' - - '%kernel.environment%' contao.twig.finder_factory: class: Contao\CoreBundle\Twig\Finder\FinderFactory @@ -1321,6 +1319,18 @@ services: contao.twig.highlighter_runtime: class: Contao\CoreBundle\Twig\Runtime\HighlighterRuntime + contao.twig.ide.namespace_lookup_file_generator: + class: Contao\CoreBundle\Twig\IDE\NamespaceLookupFileGenerator + arguments: + - '@contao.twig.filesystem_loader' + + contao.twig.ide.namespace_lookup_file_warmer: + class: Contao\CoreBundle\Twig\IDE\NamespaceLookupFileWarmer + arguments: + - '@contao.twig.ide.namespace_lookup_file_generator' + - '%kernel.environment%' + - '%kernel.project_dir%' + contao.twig.insert_tag_runtime: class: Contao\CoreBundle\Twig\Runtime\InsertTagRuntime arguments: diff --git a/core-bundle/src/Twig/IDE/NamespaceLookupFileGenerator.php b/core-bundle/src/Twig/IDE/NamespaceLookupFileGenerator.php new file mode 100644 index 00000000000..e2cfb4f11f8 --- /dev/null +++ b/core-bundle/src/Twig/IDE/NamespaceLookupFileGenerator.php @@ -0,0 +1,56 @@ +loader->getInheritanceChains() as $chain) { + foreach ($chain as $path => $name) { + [$namespace, $file] = ContaoTwigUtil::parseContaoName($name); + $templateDir = preg_replace('%(.*)/'.preg_quote($file, '%').'%', '$1', $path); + + $mappings[Path::makeRelative($templateDir, $targetDir)] = $namespace; + } + } + + $data = []; + + foreach ($mappings as $path => $namespace) { + $data['namespaces'][] = ['namespace' => 'Contao', 'path' => $path]; + $data['namespaces'][] = ['namespace' => $namespace, 'path' => $path]; + } + + if (!$this->filesystem) { + $this->filesystem = new Filesystem(); + } + + $this->filesystem->dumpFile( + Path::join($targetDir, self::FILE_NAME), + json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES), + ); + } +} diff --git a/core-bundle/src/Twig/IDE/NamespaceLookupFileWarmer.php b/core-bundle/src/Twig/IDE/NamespaceLookupFileWarmer.php new file mode 100644 index 00000000000..5ac9fe0ebfe --- /dev/null +++ b/core-bundle/src/Twig/IDE/NamespaceLookupFileWarmer.php @@ -0,0 +1,41 @@ +environment) { + return []; + } + + try { + $this->namespaceLookupFileGenerator->write(Path::join($this->projectDir, self::TARGET_DIR)); + } catch (IOException) { + // ignore + } + + return []; + } +} diff --git a/core-bundle/src/Twig/Loader/ContaoFilesystemLoaderWarmer.php b/core-bundle/src/Twig/Loader/ContaoFilesystemLoaderWarmer.php index 957b27887d1..47e03b39e89 100644 --- a/core-bundle/src/Twig/Loader/ContaoFilesystemLoaderWarmer.php +++ b/core-bundle/src/Twig/Loader/ContaoFilesystemLoaderWarmer.php @@ -12,10 +12,6 @@ namespace Contao\CoreBundle\Twig\Loader; -use Contao\CoreBundle\Twig\ContaoTwigUtil; -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Filesystem\Path; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; /** @@ -25,9 +21,6 @@ class ContaoFilesystemLoaderWarmer implements CacheWarmerInterface { public function __construct( private readonly ContaoFilesystemLoader $loader, - private readonly string $cacheDir, - private readonly string $environment, - private Filesystem|null $filesystem = null, ) { } @@ -35,10 +28,6 @@ public function warmUp(string|null $cacheDir = null, string|null $buildDir = nul { $this->loader->warmUp(); - if ('dev' === $this->environment) { - $this->writeIdeAutoCompletionMapping($cacheDir ?? $this->cacheDir); - } - return []; } @@ -46,43 +35,4 @@ public function isOptional(): bool { return true; } - - /** - * Writes an "ide-twig.json" file with path mapping information that enables IDE - * auto-completion for all our dynamic namespaces. - */ - private function writeIdeAutoCompletionMapping(string $cacheDir): void - { - $mappings = []; - $targetDir = Path::join($cacheDir, 'contao'); - - foreach ($this->loader->getInheritanceChains() as $chain) { - foreach ($chain as $path => $name) { - [$namespace, $file] = ContaoTwigUtil::parseContaoName($name); - $templateDir = preg_replace('%(.*)/'.preg_quote($file, '%').'%', '$1', $path); - - $mappings[Path::makeRelative($templateDir, $targetDir)] = $namespace; - } - } - - $data = []; - - foreach ($mappings as $path => $namespace) { - $data['namespaces'][] = ['namespace' => 'Contao', 'path' => $path]; - $data['namespaces'][] = ['namespace' => $namespace, 'path' => $path]; - } - - if (!$this->filesystem) { - $this->filesystem = new Filesystem(); - } - - try { - $this->filesystem->dumpFile( - Path::join($targetDir, 'ide-twig.json'), - json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES), - ); - } catch (IOException) { - // ignore - } - } } diff --git a/core-bundle/tests/Twig/IDE/NamespaceLookupFileGeneratorTest.php b/core-bundle/tests/Twig/IDE/NamespaceLookupFileGeneratorTest.php new file mode 100644 index 00000000000..1b1f9dd6652 --- /dev/null +++ b/core-bundle/tests/Twig/IDE/NamespaceLookupFileGeneratorTest.php @@ -0,0 +1,75 @@ +createMock(ContaoFilesystemLoader::class); + $loader + ->method('getInheritanceChains') + ->willReturn([ + 'a' => [ + '/project/templates/a.html.twig' => '@Contao_Global/a.html.twig', + '/project/contao/templates/a.html.twig' => '@Contao_App/a.html.twig', + ], + 'b' => [ + '/project/templates/b.html.twig' => '@Contao_Global/b.html.twig', + ], + 'foo/c' => [ + '/project/templates/foo/c.html.twig' => '@Contao_Global/foo/c.html.twig', + ], + 'bar/d' => [ + '/project/vendor/demo/bar/d.html.twig' => '@Contao_DemoBundle/bar/d.html.twig', + ], + ]) + ; + + $filesystem = $this->createMock(Filesystem::class); + $filesystem + ->expects($this->once()) + ->method('dumpFile') + ->with( + '/project/foo/bar/ide-twig.json', + $this->callback( + function (string $json): bool { + $expectedData = [ + 'namespaces' => [ + ['namespace' => 'Contao', 'path' => '../../templates'], + ['namespace' => 'Contao_Global', 'path' => '../../templates'], + ['namespace' => 'Contao', 'path' => '../../contao/templates'], + ['namespace' => 'Contao_App', 'path' => '../../contao/templates'], + ['namespace' => 'Contao', 'path' => '../../vendor/demo'], + ['namespace' => 'Contao_DemoBundle', 'path' => '../../vendor/demo'], + ], + ]; + + $this->assertJson($json); + $this->assertSame($expectedData, json_decode($json, true, 512, JSON_THROW_ON_ERROR)); + + return true; + }, + ), + ) + ; + + $namespaceLookupFileGenerator = new NamespaceLookupFileGenerator($loader, $filesystem); + $namespaceLookupFileGenerator->write('/project/foo/bar'); + } +} diff --git a/core-bundle/tests/Twig/IDE/NamespaceLookupFileWarmerTest.php b/core-bundle/tests/Twig/IDE/NamespaceLookupFileWarmerTest.php new file mode 100644 index 00000000000..c25e32136aa --- /dev/null +++ b/core-bundle/tests/Twig/IDE/NamespaceLookupFileWarmerTest.php @@ -0,0 +1,62 @@ +assertFalse($this->getNamespaceLookupFileWarmer()->isOptional()); + } + + public function testWritesFileOnWarmUp(): void + { + $namespaceLookupFileGenerator = $this->createMock(NamespaceLookupFileGenerator::class); + $namespaceLookupFileGenerator + ->expects($this->once()) + ->method('write') + ->with('/project/var/contao-twig') + ; + + $this->getNamespaceLookupFileWarmer($namespaceLookupFileGenerator)->warmUp(''); + } + + public function testDoesNotWriteFileInProd(): void + { + $namespaceLookupFileGenerator = $this->createMock(NamespaceLookupFileGenerator::class); + $namespaceLookupFileGenerator + ->expects($this->never()) + ->method('write') + ; + + $this->getNamespaceLookupFileWarmer($namespaceLookupFileGenerator, 'prod')->warmUp(''); + } + + public function testToleratesFailingWrites(): void + { + $namespaceLookupFileGenerator = $this->createMock(NamespaceLookupFileGenerator::class); + $namespaceLookupFileGenerator + ->expects($this->once()) + ->method('write') + ->willThrowException(new IOException('Unable to write')) + ; + + $this->getNamespaceLookupFileWarmer($namespaceLookupFileGenerator)->warmUp(''); + } + + private function getNamespaceLookupFileWarmer(NamespaceLookupFileGenerator|null $namespaceLookupFileGenerator = null, string $environment = 'dev'): NamespaceLookupFileWarmer + { + return new NamespaceLookupFileWarmer( + $namespaceLookupFileGenerator ?? $this->createMock(NamespaceLookupFileGenerator::class), + $environment, + '/project', + ); + } +} diff --git a/core-bundle/tests/Twig/Loader/ContaoFilesystemLoaderWarmerTest.php b/core-bundle/tests/Twig/Loader/ContaoFilesystemLoaderWarmerTest.php index 0c24aedaf14..46bfd9d6d0f 100644 --- a/core-bundle/tests/Twig/Loader/ContaoFilesystemLoaderWarmerTest.php +++ b/core-bundle/tests/Twig/Loader/ContaoFilesystemLoaderWarmerTest.php @@ -15,8 +15,6 @@ use Contao\CoreBundle\Tests\TestCase; use Contao\CoreBundle\Twig\Loader\ContaoFilesystemLoader; use Contao\CoreBundle\Twig\Loader\ContaoFilesystemLoaderWarmer; -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; class ContaoFilesystemLoaderWarmerTest extends TestCase { @@ -40,99 +38,10 @@ public function testWarmsUpContaoFilesystemLoader(): void $filesystemLoaderWarmer->warmUp(); } - public function testWritesIdeAutoCompletionFile(): void - { - $loader = $this->createMock(ContaoFilesystemLoader::class); - $loader - ->method('getInheritanceChains') - ->willReturn([ - 'a' => [ - '/templates/a.html.twig' => '@Contao_Global/a.html.twig', - '/contao/templates/a.html.twig' => '@Contao_App/a.html.twig', - ], - 'b' => [ - '/templates/b.html.twig' => '@Contao_Global/b.html.twig', - ], - 'foo/c' => [ - '/templates/foo/c.html.twig' => '@Contao_Global/foo/c.html.twig', - ], - 'bar/d' => [ - '/vendor/demo/bar/d.html.twig' => '@Contao_DemoBundle/bar/d.html.twig', - ], - ]) - ; - - $filesystem = $this->createMock(Filesystem::class); - $filesystem - ->expects($this->once()) - ->method('dumpFile') - ->with( - '/cache/contao/ide-twig.json', - $this->callback( - function (string $json): bool { - $expectedData = [ - 'namespaces' => [ - ['namespace' => 'Contao', 'path' => '../../templates'], - ['namespace' => 'Contao_Global', 'path' => '../../templates'], - ['namespace' => 'Contao', 'path' => '../../contao/templates'], - ['namespace' => 'Contao_App', 'path' => '../../contao/templates'], - ['namespace' => 'Contao', 'path' => '../../vendor/demo'], - ['namespace' => 'Contao_DemoBundle', 'path' => '../../vendor/demo'], - ], - ]; - - $this->assertJson($json); - $this->assertSame($expectedData, json_decode($json, true, 512, JSON_THROW_ON_ERROR)); - - return true; - }, - ), - ) - ; - - $warmer = $this->getContaoFilesystemLoaderWarmer($loader, 'dev', $filesystem); - $warmer->warmUp(); - } - - public function testDoesNotWriteAutoCompletionFileInProd(): void - { - $loader = $this->createMock(ContaoFilesystemLoader::class); - $loader - ->expects($this->never()) - ->method('getInheritanceChains') - ; - - $filesystem = $this->createMock(Filesystem::class); - $filesystem - ->expects($this->never()) - ->method('dumpFile') - ; - - $warmer = $this->getContaoFilesystemLoaderWarmer($loader, 'prod', $filesystem); - $warmer->warmUp(); - } - - public function testToleratesFailingWritesWhenWritingIdeAutoCompletionFile(): void - { - $filesystem = $this->createMock(Filesystem::class); - $filesystem - ->expects($this->once()) - ->method('dumpFile') - ->with('/cache/contao/ide-twig.json', $this->anything()) - ->willThrowException(new IOException('write fail')) - ; - - $warmer = $this->getContaoFilesystemLoaderWarmer(null, 'dev', $filesystem); - $warmer->warmUp(); - } - - private function getContaoFilesystemLoaderWarmer(ContaoFilesystemLoader|null $filesystemLoader = null, string|null $environment = null, Filesystem|null $filesystem = null): ContaoFilesystemLoaderWarmer + private function getContaoFilesystemLoaderWarmer(ContaoFilesystemLoader|null $filesystemLoader = null): ContaoFilesystemLoaderWarmer { return new ContaoFilesystemLoaderWarmer( $filesystemLoader ?? $this->createMock(ContaoFilesystemLoader::class), - '/cache', - $environment ?? 'prod', - $filesystem ?? $this->createMock(Filesystem::class), ); } } From e4b9abd084930a48247ed7bd3f00495d5f67cc6e Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Fri, 14 Mar 2025 11:56:17 +0100 Subject: [PATCH 2/6] add a command to manually dump the ide namespace lookup file --- core-bundle/config/commands.yaml | 6 ++ .../src/Command/DumpTwigIDEFileCommand.php | 67 +++++++++++++++++++ .../Command/DumpTwigIDEFileCommandTest.php | 40 +++++++++++ 3 files changed, 113 insertions(+) create mode 100644 core-bundle/src/Command/DumpTwigIDEFileCommand.php create mode 100644 core-bundle/tests/Command/DumpTwigIDEFileCommandTest.php diff --git a/core-bundle/config/commands.yaml b/core-bundle/config/commands.yaml index 43a0d96d2e0..243e536731a 100644 --- a/core-bundle/config/commands.yaml +++ b/core-bundle/config/commands.yaml @@ -73,6 +73,12 @@ services: - '%kernel.project_dir%' - '@contao.twig.inspector' + contao.command.dump_twig_ide_file: + class: Contao\CoreBundle\Command\DumpTwigIDEFileCommand + arguments: + - '@contao.twig.ide.namespace_lookup_file_generator' + - '%kernel.project_dir%' + contao.command.filesync: class: Contao\CoreBundle\Command\FilesyncCommand arguments: diff --git a/core-bundle/src/Command/DumpTwigIDEFileCommand.php b/core-bundle/src/Command/DumpTwigIDEFileCommand.php new file mode 100644 index 00000000000..57a9026513d --- /dev/null +++ b/core-bundle/src/Command/DumpTwigIDEFileCommand.php @@ -0,0 +1,67 @@ +addArgument('dir', InputArgument::OPTIONAL, 'Target path relative to the project directory.', NamespaceLookupFileWarmer::TARGET_DIR) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $targetDir = Path::canonicalize($input->getArgument('dir')); + $io = new SymfonyStyle($input, $output); + + try { + $this->namespaceLookupFileGenerator->write(Path::makeAbsolute($targetDir, $this->projectDir)); + } catch (IOException) { + $io->error(\sprintf('Unable to write the "%s" namespace lookup file to "%s".', $targetDir, NamespaceLookupFileGenerator::FILE_NAME)); + + return Command::FAILURE; + } + + $io->success(\sprintf('The namespace lookup file was written to "%s/%s". Make sure the file is not ignored by your IDE.', $targetDir, NamespaceLookupFileGenerator::FILE_NAME)); + + if (NamespaceLookupFileWarmer::TARGET_DIR !== $input->getArgument('dir')) { + $io->info('Re-run this command after installing extensions or introducing new @Contao namespace locations.'); + } + + return Command::SUCCESS; + } +} diff --git a/core-bundle/tests/Command/DumpTwigIDEFileCommandTest.php b/core-bundle/tests/Command/DumpTwigIDEFileCommandTest.php new file mode 100644 index 00000000000..b56b6e581b8 --- /dev/null +++ b/core-bundle/tests/Command/DumpTwigIDEFileCommandTest.php @@ -0,0 +1,40 @@ +createMock(NamespaceLookupFileGenerator::class); + $namespaceLookupFileGenerator + ->expects($this->once()) + ->method('write') + ->with('/project/dir/foo') + ; + + $command = new DumpTwigIDEFileCommand($namespaceLookupFileGenerator, '/project/dir'); + + $tester = new CommandTester($command); + $tester->execute(['dir' => 'foo']); + + $this->assertSame(Command::SUCCESS, $tester->getStatusCode()); + $this->assertStringContainsString('The namespace lookup file was written to "foo/ide-twig.json".', $tester->getDisplay()); + } +} From 11b62e2bcf553216627178d8337d8223c65d1bfd Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Fri, 4 Apr 2025 14:17:37 +0200 Subject: [PATCH 3/6] write to ""%kernel.build_dir%/contao-ide" by default --- core-bundle/config/commands.yaml | 1 + core-bundle/config/services.yaml | 1 - .../src/Command/DumpTwigIDEFileCommand.php | 10 ++++- .../Twig/IDE/NamespaceLookupFileWarmer.php | 7 ++-- .../Command/DumpTwigIDEFileCommandTest.php | 38 ++++++++++++++----- .../IDE/NamespaceLookupFileWarmerTest.php | 32 +++++++++++++--- 6 files changed, 68 insertions(+), 21 deletions(-) diff --git a/core-bundle/config/commands.yaml b/core-bundle/config/commands.yaml index 243e536731a..8844b5f7b71 100644 --- a/core-bundle/config/commands.yaml +++ b/core-bundle/config/commands.yaml @@ -77,6 +77,7 @@ services: class: Contao\CoreBundle\Command\DumpTwigIDEFileCommand arguments: - '@contao.twig.ide.namespace_lookup_file_generator' + - '%kernel.build_dir%' - '%kernel.project_dir%' contao.command.filesync: diff --git a/core-bundle/config/services.yaml b/core-bundle/config/services.yaml index 1291b7948c6..8d6ee400247 100644 --- a/core-bundle/config/services.yaml +++ b/core-bundle/config/services.yaml @@ -1329,7 +1329,6 @@ services: arguments: - '@contao.twig.ide.namespace_lookup_file_generator' - '%kernel.environment%' - - '%kernel.project_dir%' contao.twig.insert_tag_runtime: class: Contao\CoreBundle\Twig\Runtime\InsertTagRuntime diff --git a/core-bundle/src/Command/DumpTwigIDEFileCommand.php b/core-bundle/src/Command/DumpTwigIDEFileCommand.php index 57a9026513d..5f31e2eba77 100644 --- a/core-bundle/src/Command/DumpTwigIDEFileCommand.php +++ b/core-bundle/src/Command/DumpTwigIDEFileCommand.php @@ -31,6 +31,7 @@ class DumpTwigIDEFileCommand extends Command { public function __construct( private readonly NamespaceLookupFileGenerator $namespaceLookupFileGenerator, + private readonly string $buildDir, private readonly string $projectDir, ) { parent::__construct(); @@ -38,8 +39,13 @@ public function __construct( protected function configure(): void { + $defaultDir = Path::makeRelative( + Path::join($this->buildDir, NamespaceLookupFileWarmer::CONTAO_IDE_DIR), + $this->projectDir, + ); + $this - ->addArgument('dir', InputArgument::OPTIONAL, 'Target path relative to the project directory.', NamespaceLookupFileWarmer::TARGET_DIR) + ->addArgument('dir', InputArgument::OPTIONAL, 'Target path relative to the project directory.', $defaultDir) ; } @@ -58,7 +64,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->success(\sprintf('The namespace lookup file was written to "%s/%s". Make sure the file is not ignored by your IDE.', $targetDir, NamespaceLookupFileGenerator::FILE_NAME)); - if (NamespaceLookupFileWarmer::TARGET_DIR !== $input->getArgument('dir')) { + if (NamespaceLookupFileWarmer::CONTAO_IDE_DIR !== $input->getArgument('dir')) { $io->info('Re-run this command after installing extensions or introducing new @Contao namespace locations.'); } diff --git a/core-bundle/src/Twig/IDE/NamespaceLookupFileWarmer.php b/core-bundle/src/Twig/IDE/NamespaceLookupFileWarmer.php index 5ac9fe0ebfe..cd143d2fb79 100644 --- a/core-bundle/src/Twig/IDE/NamespaceLookupFileWarmer.php +++ b/core-bundle/src/Twig/IDE/NamespaceLookupFileWarmer.php @@ -10,12 +10,11 @@ class NamespaceLookupFileWarmer implements CacheWarmerInterface { - public const TARGET_DIR = 'var/contao-twig'; + public const CONTAO_IDE_DIR = 'contao-ide'; public function __construct( private readonly NamespaceLookupFileGenerator $namespaceLookupFileGenerator, private readonly string $environment, - private readonly string $projectDir, ) { } @@ -26,12 +25,12 @@ public function isOptional(): bool public function warmUp(string $cacheDir, string|null $buildDir = null): array { - if ('dev' !== $this->environment) { + if ('dev' !== $this->environment || null === $buildDir) { return []; } try { - $this->namespaceLookupFileGenerator->write(Path::join($this->projectDir, self::TARGET_DIR)); + $this->namespaceLookupFileGenerator->write(Path::join($buildDir, self::CONTAO_IDE_DIR)); } catch (IOException) { // ignore } diff --git a/core-bundle/tests/Command/DumpTwigIDEFileCommandTest.php b/core-bundle/tests/Command/DumpTwigIDEFileCommandTest.php index b56b6e581b8..6333ebc8f40 100644 --- a/core-bundle/tests/Command/DumpTwigIDEFileCommandTest.php +++ b/core-bundle/tests/Command/DumpTwigIDEFileCommandTest.php @@ -10,7 +10,7 @@ * @license LGPL-3.0-or-later */ -namespace Command; +namespace Contao\CoreBundle\Tests\Command; use Contao\CoreBundle\Command\DumpTwigIDEFileCommand; use Contao\CoreBundle\Tests\TestCase; @@ -20,16 +20,20 @@ class DumpTwigIDEFileCommandTest extends TestCase { - public function testWritesFile(): void + public function testWritesFileAtDefaultLocation(): void { - $namespaceLookupFileGenerator = $this->createMock(NamespaceLookupFileGenerator::class); - $namespaceLookupFileGenerator - ->expects($this->once()) - ->method('write') - ->with('/project/dir/foo') - ; + $command = $this->getCommand('/project/var/build/contao-ide'); + + $tester = new CommandTester($command); + $tester->execute([]); + + $this->assertSame(Command::SUCCESS, $tester->getStatusCode()); + $this->assertStringContainsString('The namespace lookup file was written to "var/build/contao-ide/ide-twig.json".', $tester->getDisplay()); + } - $command = new DumpTwigIDEFileCommand($namespaceLookupFileGenerator, '/project/dir'); + public function testWritesFileToCustomLocation(): void + { + $command = $this->getCommand('/project/foo'); $tester = new CommandTester($command); $tester->execute(['dir' => 'foo']); @@ -37,4 +41,20 @@ public function testWritesFile(): void $this->assertSame(Command::SUCCESS, $tester->getStatusCode()); $this->assertStringContainsString('The namespace lookup file was written to "foo/ide-twig.json".', $tester->getDisplay()); } + + protected function getCommand(string $expectedWriteDir): DumpTwigIDEFileCommand + { + $namespaceLookupFileGenerator = $this->createMock(NamespaceLookupFileGenerator::class); + $namespaceLookupFileGenerator + ->expects($this->once()) + ->method('write') + ->with($expectedWriteDir) + ; + + return new DumpTwigIDEFileCommand( + $namespaceLookupFileGenerator, + '/project/var/build', + '/project', + ); + } } diff --git a/core-bundle/tests/Twig/IDE/NamespaceLookupFileWarmerTest.php b/core-bundle/tests/Twig/IDE/NamespaceLookupFileWarmerTest.php index c25e32136aa..4b09f3e7bf9 100644 --- a/core-bundle/tests/Twig/IDE/NamespaceLookupFileWarmerTest.php +++ b/core-bundle/tests/Twig/IDE/NamespaceLookupFileWarmerTest.php @@ -22,10 +22,13 @@ public function testWritesFileOnWarmUp(): void $namespaceLookupFileGenerator ->expects($this->once()) ->method('write') - ->with('/project/var/contao-twig') + ->with('/var/build/contao-ide') ; - $this->getNamespaceLookupFileWarmer($namespaceLookupFileGenerator)->warmUp(''); + $this + ->getNamespaceLookupFileWarmer($namespaceLookupFileGenerator) + ->warmUp('/var/cache', '/var/build') + ; } public function testDoesNotWriteFileInProd(): void @@ -36,7 +39,24 @@ public function testDoesNotWriteFileInProd(): void ->method('write') ; - $this->getNamespaceLookupFileWarmer($namespaceLookupFileGenerator, 'prod')->warmUp(''); + $this + ->getNamespaceLookupFileWarmer($namespaceLookupFileGenerator, 'prod') + ->warmUp('/var/cache', '/var/build') + ; + } + + public function testDoesNotWriteFileIfNoBuildDirIsSpecified(): void + { + $namespaceLookupFileGenerator = $this->createMock(NamespaceLookupFileGenerator::class); + $namespaceLookupFileGenerator + ->expects($this->never()) + ->method('write') + ; + + $this + ->getNamespaceLookupFileWarmer($namespaceLookupFileGenerator, 'prod') + ->warmUp('/var/cache', null) + ; } public function testToleratesFailingWrites(): void @@ -48,7 +68,10 @@ public function testToleratesFailingWrites(): void ->willThrowException(new IOException('Unable to write')) ; - $this->getNamespaceLookupFileWarmer($namespaceLookupFileGenerator)->warmUp(''); + $this + ->getNamespaceLookupFileWarmer($namespaceLookupFileGenerator) + ->warmUp('/var/cache', '/var/build') + ; } private function getNamespaceLookupFileWarmer(NamespaceLookupFileGenerator|null $namespaceLookupFileGenerator = null, string $environment = 'dev'): NamespaceLookupFileWarmer @@ -56,7 +79,6 @@ private function getNamespaceLookupFileWarmer(NamespaceLookupFileGenerator|null return new NamespaceLookupFileWarmer( $namespaceLookupFileGenerator ?? $this->createMock(NamespaceLookupFileGenerator::class), $environment, - '/project', ); } } From 1fc02a5bc7016c9d470215d3fdef5175588b85ab Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Fri, 4 Apr 2025 14:18:25 +0200 Subject: [PATCH 4/6] also pass build dir, when manually warming up in the Automator --- core-bundle/contao/library/Contao/Automator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-bundle/contao/library/Contao/Automator.php b/core-bundle/contao/library/Contao/Automator.php index 85f75926eab..c4e6f336cb1 100644 --- a/core-bundle/contao/library/Contao/Automator.php +++ b/core-bundle/contao/library/Contao/Automator.php @@ -375,7 +375,7 @@ public function generateInternalCache() $container = System::getContainer(); $warmer = $container->get('contao.cache.warmer'); - $warmer->warmUp($container->getParameter('kernel.cache_dir')); + $warmer->warmUp($container->getParameter('kernel.cache_dir'), $container->getParameter('kernel.build_dir')); System::getContainer()->get('monolog.logger.contao.cron')->info('Generated the internal cache'); } From 546ec9ab3058f2bd27ce6f21d685f85d344d3423 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Fri, 4 Apr 2025 14:21:54 +0200 Subject: [PATCH 5/6] fix default value --- core-bundle/src/Command/DumpTwigIDEFileCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-bundle/src/Command/DumpTwigIDEFileCommand.php b/core-bundle/src/Command/DumpTwigIDEFileCommand.php index 5f31e2eba77..0d4b0a5556d 100644 --- a/core-bundle/src/Command/DumpTwigIDEFileCommand.php +++ b/core-bundle/src/Command/DumpTwigIDEFileCommand.php @@ -64,7 +64,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->success(\sprintf('The namespace lookup file was written to "%s/%s". Make sure the file is not ignored by your IDE.', $targetDir, NamespaceLookupFileGenerator::FILE_NAME)); - if (NamespaceLookupFileWarmer::CONTAO_IDE_DIR !== $input->getArgument('dir')) { + if ($this->getDefinition()->getArgument('dir')->getDefault() !== $input->getArgument('dir')) { $io->info('Re-run this command after installing extensions or introducing new @Contao namespace locations.'); } From f764f604113732d33465fc913ea85bcf31099a90 Mon Sep 17 00:00:00 2001 From: Leo Feyer <1192057+leofeyer@users.noreply.github.com> Date: Mon, 2 Jun 2025 17:25:06 +0200 Subject: [PATCH 6/6] Update and run the tools --- .../src/Controller/SitemapController.php | 1 + .../DataContainerOperationsBuilder.php | 1 + .../Runtime/PictureConfigurationRuntime.php | 4 +- core-bundle/tests/Event/SitemapEventTest.php | 1 + .../SubrequestCacheSubscriberTest.php | 1 + .../TransportSecurityHeaderListenerTest.php | 1 + vendor-bin/ecs/composer.lock | 32 ++++++++------ vendor-bin/rector/composer.lock | 44 +++++++++---------- 8 files changed, 48 insertions(+), 37 deletions(-) diff --git a/core-bundle/src/Controller/SitemapController.php b/core-bundle/src/Controller/SitemapController.php index 6abd7e116a1..883d5f21a3d 100644 --- a/core-bundle/src/Controller/SitemapController.php +++ b/core-bundle/src/Controller/SitemapController.php @@ -62,6 +62,7 @@ public function __invoke(Request $request): Response $sitemap = new \DOMDocument('1.0', 'UTF-8'); $sitemap->formatOutput = true; + $urlSet = $sitemap->createElementNS('https://www.sitemaps.org/schemas/sitemap/0.9', 'urlset'); foreach ($urls as $url) { diff --git a/core-bundle/src/DataContainer/DataContainerOperationsBuilder.php b/core-bundle/src/DataContainer/DataContainerOperationsBuilder.php index d04e57be740..3ef38d9a779 100644 --- a/core-bundle/src/DataContainer/DataContainerOperationsBuilder.php +++ b/core-bundle/src/DataContainer/DataContainerOperationsBuilder.php @@ -183,6 +183,7 @@ private function parseOperationsHtml(array $operation): array $xml = new \DOMDocument(); $xml->preserveWhiteSpace = false; $xml->loadHTML(''.$operation['html']); + $body = $xml->getElementsByTagName('body')[0]; if ($body->childNodes->length < 2) { diff --git a/core-bundle/src/Twig/Runtime/PictureConfigurationRuntime.php b/core-bundle/src/Twig/Runtime/PictureConfigurationRuntime.php index 7abc56f2138..78f2cd6774c 100644 --- a/core-bundle/src/Twig/Runtime/PictureConfigurationRuntime.php +++ b/core-bundle/src/Twig/Runtime/PictureConfigurationRuntime.php @@ -46,7 +46,7 @@ public function fromArray(array $config): PictureConfiguration function (array $itemConfig): PictureConfigurationItem { $sizeItem = $this->createPictureConfigurationItem($itemConfig); - if (!empty($itemConfig)) { + if ([] !== $itemConfig) { $this->throwInvalidArgumentException($itemConfig, 'items'); } @@ -60,7 +60,7 @@ function (array $itemConfig): PictureConfigurationItem { // Apply remaining data to root config $this->applyConfiguration($pictureConfiguration, $config); - if (!empty($config)) { + if ([] !== $config) { $this->throwInvalidArgumentException($config); } diff --git a/core-bundle/tests/Event/SitemapEventTest.php b/core-bundle/tests/Event/SitemapEventTest.php index ee2574f764c..c7c3b816548 100644 --- a/core-bundle/tests/Event/SitemapEventTest.php +++ b/core-bundle/tests/Event/SitemapEventTest.php @@ -34,6 +34,7 @@ public function testAddingUrlsToExistingUrlSetDoesNotFailIfThereIsNoUrlSet(): vo { $sitemap = new \DOMDocument('1.0', 'UTF-8'); $sitemap->preserveWhiteSpace = false; + $event = new SitemapEvent($sitemap, new Request(), []); $event->addUrlToDefaultUrlSet('https://contao.org'); diff --git a/core-bundle/tests/EventListener/SubrequestCacheSubscriberTest.php b/core-bundle/tests/EventListener/SubrequestCacheSubscriberTest.php index 8b06b574d9e..2d08206d637 100644 --- a/core-bundle/tests/EventListener/SubrequestCacheSubscriberTest.php +++ b/core-bundle/tests/EventListener/SubrequestCacheSubscriberTest.php @@ -76,6 +76,7 @@ public function testMakeMainResponsePrivateIfSubrequestIsPrivate(): void $mainResponse = new Response(); $mainResponse->headers->set(SubrequestCacheSubscriber::MERGE_CACHE_HEADER, '1'); + $subResponse->setPublic(); $mainResponse->setMaxAge(60); diff --git a/core-bundle/tests/EventListener/TransportSecurityHeaderListenerTest.php b/core-bundle/tests/EventListener/TransportSecurityHeaderListenerTest.php index 73844c9a6e0..ad6d77bb5c7 100644 --- a/core-bundle/tests/EventListener/TransportSecurityHeaderListenerTest.php +++ b/core-bundle/tests/EventListener/TransportSecurityHeaderListenerTest.php @@ -49,6 +49,7 @@ public function testIgnoresIfTheResponseAlreadyHasAnStsHeaderPresent(): void { $response = new Response(); $response->headers->set('Strict-Transport-Security', 'max-age=500; includeSubDomains; preload'); + $request = Request::create('https://contao.org'); $listener = new TransportSecurityHeaderListener($this->createScopeMatcher(true), 31536000); diff --git a/vendor-bin/ecs/composer.lock b/vendor-bin/ecs/composer.lock index b4cf5094cc7..7e3d7b1d755 100644 --- a/vendor-bin/ecs/composer.lock +++ b/vendor-bin/ecs/composer.lock @@ -298,16 +298,16 @@ }, { "name": "contao/easy-coding-standard", - "version": "6.13.2", + "version": "6.13.3", "source": { "type": "git", "url": "https://github.com/contao/easy-coding-standard.git", - "reference": "ce25e5f73ec10894f8ae3842e363ade441ae2043" + "reference": "f251af7e5e34917f5ed9ab912ad18ce2f10b1799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/contao/easy-coding-standard/zipball/ce25e5f73ec10894f8ae3842e363ade441ae2043", - "reference": "ce25e5f73ec10894f8ae3842e363ade441ae2043", + "url": "https://api.github.com/repos/contao/easy-coding-standard/zipball/f251af7e5e34917f5ed9ab912ad18ce2f10b1799", + "reference": "f251af7e5e34917f5ed9ab912ad18ce2f10b1799", "shasum": "" }, "require": { @@ -318,7 +318,7 @@ }, "require-dev": { "contao/rector": "^1.2", - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^11.5" }, "type": "library", "autoload": { @@ -339,7 +339,7 @@ "description": "EasyCodingStandard configurations for Contao", "support": { "issues": "https://github.com/contao/easy-coding-standard/issues", - "source": "https://github.com/contao/easy-coding-standard/tree/6.13.2" + "source": "https://github.com/contao/easy-coding-standard/tree/6.13.3" }, "funding": [ { @@ -347,7 +347,7 @@ "type": "custom" } ], - "time": "2025-02-13T10:21:52+00:00" + "time": "2025-06-02T15:17:08+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -641,16 +641,16 @@ }, { "name": "kubawerlos/php-cs-fixer-custom-fixers", - "version": "v3.25.0", + "version": "v3.26.0", "source": { "type": "git", "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", - "reference": "50e070e5457ca4570e032207d201b3874aaff942" + "reference": "67f253320e0db784ab0d1248af6fc42e5f2215e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/50e070e5457ca4570e032207d201b3874aaff942", - "reference": "50e070e5457ca4570e032207d201b3874aaff942", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/67f253320e0db784ab0d1248af6fc42e5f2215e2", + "reference": "67f253320e0db784ab0d1248af6fc42e5f2215e2", "shasum": "" }, "require": { @@ -681,9 +681,15 @@ "description": "A set of custom fixers for PHP CS Fixer", "support": { "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", - "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.25.0" + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.26.0" }, - "time": "2025-05-06T20:12:43+00:00" + "funding": [ + { + "url": "https://github.com/kubawerlos", + "type": "github" + } + ], + "time": "2025-06-01T11:49:27+00:00" }, { "name": "phpstan/phpdoc-parser", diff --git a/vendor-bin/rector/composer.lock b/vendor-bin/rector/composer.lock index 7347c3a1b98..bf4aabaa1e5 100644 --- a/vendor-bin/rector/composer.lock +++ b/vendor-bin/rector/composer.lock @@ -8,24 +8,24 @@ "packages": [ { "name": "contao/rector", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/contao/rector.git", - "reference": "9d7008206abaef006c4d56bb3092370361382388" + "reference": "66b327ac98ef291b134b0a1c3a573fccb6cbd2d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/contao/rector/zipball/9d7008206abaef006c4d56bb3092370361382388", - "reference": "9d7008206abaef006c4d56bb3092370361382388", + "url": "https://api.github.com/repos/contao/rector/zipball/66b327ac98ef291b134b0a1c3a573fccb6cbd2d8", + "reference": "66b327ac98ef291b134b0a1c3a573fccb6cbd2d8", "shasum": "" }, "require": { "php": "^8.1", - "rector/rector": "^1.0" + "rector/rector": "^2.0" }, "require-dev": { - "contao/easy-coding-standard": "^6.0" + "contao/easy-coding-standard": "^6.12" }, "type": "library", "autoload": { @@ -46,7 +46,7 @@ "description": "Rector configurations for Contao", "support": { "issues": "https://github.com/contao/rector/issues", - "source": "https://github.com/contao/rector/tree/1.2.0" + "source": "https://github.com/contao/rector/tree/1.3.0" }, "funding": [ { @@ -54,24 +54,24 @@ "type": "custom" } ], - "time": "2024-04-04T10:28:57+00:00" + "time": "2025-06-02T15:11:54+00:00" }, { "name": "phpstan/phpstan", - "version": "1.12.27", + "version": "2.1.17", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "3a6e423c076ab39dfedc307e2ac627ef579db162" + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3a6e423c076ab39dfedc307e2ac627ef579db162", - "reference": "3a6e423c076ab39dfedc307e2ac627ef579db162", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053", + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -112,25 +112,25 @@ "type": "github" } ], - "time": "2025-05-21T20:51:45+00:00" + "time": "2025-05-21T20:55:28+00:00" }, { "name": "rector/rector", - "version": "1.2.10", + "version": "2.0.17", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "40f9cf38c05296bd32f444121336a521a293fa61" + "reference": "caa4ffda1d48bde44434e6ba95d132ec32e7fd40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/40f9cf38c05296bd32f444121336a521a293fa61", - "reference": "40f9cf38c05296bd32f444121336a521a293fa61", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/caa4ffda1d48bde44434e6ba95d132ec32e7fd40", + "reference": "caa4ffda1d48bde44434e6ba95d132ec32e7fd40", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "phpstan/phpstan": "^1.12.5" + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.17" }, "conflict": { "rector/rector-doctrine": "*", @@ -163,7 +163,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/1.2.10" + "source": "https://github.com/rectorphp/rector/tree/2.0.17" }, "funding": [ { @@ -171,7 +171,7 @@ "type": "github" } ], - "time": "2024-11-08T13:59:10+00:00" + "time": "2025-05-30T10:59:08+00:00" } ], "packages-dev": [],