diff --git a/UPGRADE-4.4.md b/UPGRADE-4.4.md index c3b9ad8688..de27e3c1cb 100644 --- a/UPGRADE-4.4.md +++ b/UPGRADE-4.4.md @@ -108,9 +108,33 @@ HttpFoundation HttpKernel ---------- - * Implementing the `BundleInterface` without implementing the `getPublicDir()` method is deprecated. - This method will be added to the interface in 5.0. * The `DebugHandlersListener` class has been marked as `final` + * Added new Bundle directory convention consistent with standard skeletons: + + ``` + └── MyBundle/ + ├── config/ + ├── public/ + ├── src/ + │ └── MyBundle.php + ├── templates/ + └── translations/ + ``` + + To make this work properly, it is necessary to change the root path of the bundle: + + ```php + class MyBundle extends Bundle + { + public function getPath(): string + { + return \dirname(__DIR__); + } + } + ``` + + As many bundles must be compatible with a range of Symfony versions, the current + directory convention is not deprecated yet, but it will be in the future. Lock ---- diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index 284e942c9a..9dbdca5993 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -286,7 +286,6 @@ HttpFoundation HttpKernel ---------- - * The `getPublicDir()` method has been added to the `BundleInterface`. * Removed `Client`, use `HttpKernelBrowser` instead * The `Kernel::getRootDir()` and the `kernel.root_dir` parameter have been removed * The `KernelInterface::getName()` and the `kernel.name` parameter have been removed @@ -303,6 +302,32 @@ HttpKernel * Removed `TranslatorListener` in favor of `LocaleAwareListener` * The `DebugHandlersListener` class has been made `final` * Removed `SaveSessionListener` in favor of `AbstractSessionListener` + * Added new Bundle directory convention consistent with standard skeletons: + + ``` + └── MyBundle/ + ├── config/ + ├── public/ + ├── src/ + │ └── MyBundle.php + ├── templates/ + └── translations/ + ``` + + To make this work properly, it is necessary to change the root path of the bundle: + + ```php + class MyBundle extends Bundle + { + public function getPath(): string + { + return \dirname(__DIR__); + } + } + ``` + + As many bundles must be compatible with a range of Symfony versions, the current + directory convention is not deprecated yet, but it will be in the future. Intl ---- diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php index b0db71c929..2577664179 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php @@ -48,11 +48,10 @@ class DoctrineValidationPass implements CompilerPassInterface } $files = $container->getParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files'); - $validationPath = 'Resources/config/validation.'.$this->managerType.'.'.$extension; + $validationPath = '/config/validation.'.$this->managerType.'.'.$extension; - foreach ($container->getParameter('kernel.bundles') as $bundle) { - $reflection = new \ReflectionClass($bundle); - if ($container->fileExists($file = \dirname($reflection->getFileName()).'/'.$validationPath)) { + foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) { + if ($container->fileExists($file = $bundle['path'].'/Resources'.$validationPath) || $container->fileExists($file = $bundle['path'].$validationPath)) { $files[] = $file; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php index ff7352790c..111ed815c7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php @@ -137,13 +137,7 @@ EOT $validAssetDirs = []; /** @var BundleInterface $bundle */ foreach ($kernel->getBundles() as $bundle) { - if (!method_exists($bundle, 'getPublicDir')) { - @trigger_error(sprintf('Not defining "getPublicDir()" method in the "%s" class is deprecated since Symfony 4.4 and will not be supported in 5.0.', \get_class($bundle)), E_USER_DEPRECATED); - $publicDir = 'Resources/public'; - } else { - $publicDir = ltrim($bundle->getPublicDir(), '\\/'); - } - if (!is_dir($originDir = $bundle->getPath().\DIRECTORY_SEPARATOR.$publicDir)) { + if (!is_dir($originDir = $bundle->getPath().'/Resources/public') && !is_dir($originDir = $bundle->getPath().'/public')) { continue; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php index 7058730388..85c1e52905 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -162,7 +162,9 @@ EOF if (null !== $input->getArgument('bundle')) { try { $bundle = $kernel->getBundle($input->getArgument('bundle')); - $transPaths = [$bundle->getPath().'/Resources/translations']; + $bundleDir = $bundle->getPath(); + $transPaths = [is_dir($bundleDir.'/Resources/translations') ? $bundleDir.'/Resources/translations' : $bundleDir.'/translations']; + $viewsPaths = [is_dir($bundleDir.'/Resources/views') ? $bundleDir.'/Resources/views' : $bundleDir.'/templates']; if ($this->defaultTransPath) { $transPaths[] = $this->defaultTransPath; } @@ -171,7 +173,6 @@ EOF $notice = sprintf('Storing translations files for "%s" in the "%s" directory is deprecated since Symfony 4.2, ', $dir, $bundle->getName()); @trigger_error($notice.($this->defaultTransPath ? sprintf('use the "%s" directory instead.', $this->defaultTransPath) : 'configure and use "framework.translator.default_path" instead.'), E_USER_DEPRECATED); } - $viewsPaths = [$bundle->getPath().'/Resources/views']; if ($this->defaultViewsPath) { $viewsPaths[] = $this->defaultViewsPath; } @@ -206,13 +207,14 @@ EOF } } elseif ($input->getOption('all')) { foreach ($kernel->getBundles() as $bundle) { - $transPaths[] = $bundle->getPath().'/Resources/translations'; + $bundleDir = $bundle->getPath(); + $transPaths[] = is_dir($bundleDir.'/Resources/translations') ? $bundleDir.'/Resources/translations' : $bundle->getPath().'/translations'; + $viewsPaths[] = is_dir($bundleDir.'/Resources/views') ? $bundleDir.'/Resources/views' : $bundle->getPath().'/templates'; if (is_dir($deprecatedPath = sprintf('%s/Resources/%s/translations', $rootDir, $bundle->getName()))) { $transPaths[] = $deprecatedPath; $notice = sprintf('Storing translations files for "%s" in the "%s" directory is deprecated since Symfony 4.2, ', $bundle->getName(), $deprecatedPath); @trigger_error($notice.($this->defaultTransPath ? sprintf('use the "%s" directory instead.', $this->defaultTransPath) : 'configure and use "framework.translator.default_path" instead.'), E_USER_DEPRECATED); } - $viewsPaths[] = $bundle->getPath().'/Resources/views'; if (is_dir($deprecatedPath = sprintf('%s/Resources/%s/views', $rootDir, $bundle->getName()))) { $viewsPaths[] = $deprecatedPath; $notice = sprintf('Loading Twig templates for "%s" from the "%s" directory is deprecated since Symfony 4.2, ', $bundle->getName(), $deprecatedPath); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php index ded71fd60e..25bd9c1e29 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php @@ -154,7 +154,9 @@ EOF if (null !== $input->getArgument('bundle')) { try { $foundBundle = $kernel->getBundle($input->getArgument('bundle')); - $transPaths = [$foundBundle->getPath().'/Resources/translations']; + $bundleDir = $foundBundle->getPath(); + $transPaths = [is_dir($bundleDir.'/Resources/translations') ? $bundleDir.'/Resources/translations' : $bundleDir.'/translations']; + $viewsPaths = [is_dir($bundleDir.'/Resources/views') ? $bundleDir.'/Resources/views' : $bundleDir.'/templates']; if ($this->defaultTransPath) { $transPaths[] = $this->defaultTransPath; } @@ -163,7 +165,6 @@ EOF $notice = sprintf('Storing translations files for "%s" in the "%s" directory is deprecated since Symfony 4.2, ', $foundBundle->getName(), $dir); @trigger_error($notice.($this->defaultTransPath ? sprintf('use the "%s" directory instead.', $this->defaultTransPath) : 'configure and use "framework.translator.default_path" instead.'), E_USER_DEPRECATED); } - $viewsPaths = [$foundBundle->getPath().'/Resources/views']; if ($this->defaultViewsPath) { $viewsPaths[] = $this->defaultViewsPath; } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 158c329fa3..f0b4bf00e3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1152,7 +1152,7 @@ class FrameworkExtension extends Extension $defaultDir = $container->getParameterBag()->resolveValue($config['default_path']); $rootDir = $container->getParameter('kernel.root_dir'); foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) { - if ($container->fileExists($dir = $bundle['path'].'/Resources/translations')) { + if ($container->fileExists($dir = $bundle['path'].'/Resources/translations') || $container->fileExists($dir = $bundle['path'].'/translations')) { $dirs[] = $dir; } else { $nonExistingDirs[] = $dir; @@ -1314,20 +1314,20 @@ class FrameworkExtension extends Extension } foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) { - $dirname = $bundle['path']; + $configDir = is_dir($bundle['path'].'/Resources/config') ? $bundle['path'].'/Resources/config' : $bundle['path'].'/config'; if ( - $container->fileExists($file = $dirname.'/Resources/config/validation.yaml', false) || - $container->fileExists($file = $dirname.'/Resources/config/validation.yml', false) + $container->fileExists($file = $configDir.'/validation.yaml', false) || + $container->fileExists($file = $configDir.'/validation.yml', false) ) { $fileRecorder('yml', $file); } - if ($container->fileExists($file = $dirname.'/Resources/config/validation.xml', false)) { + if ($container->fileExists($file = $configDir.'/validation.xml', false)) { $fileRecorder('xml', $file); } - if ($container->fileExists($dir = $dirname.'/Resources/config/validation', '/^$/')) { + if ($container->fileExists($dir = $configDir.'/validation', '/^$/')) { $this->registerMappingFilesFromDir($dir, $fileRecorder); } } @@ -1508,20 +1508,20 @@ class FrameworkExtension extends Extension }; foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) { - $dirname = $bundle['path']; + $configDir = is_dir($bundle['path'].'/Resources/config') ? $bundle['path'].'/Resources/config' : $bundle['path'].'/config'; - if ($container->fileExists($file = $dirname.'/Resources/config/serialization.xml', false)) { + if ($container->fileExists($file = $configDir.'/serialization.xml', false)) { $fileRecorder('xml', $file); } if ( - $container->fileExists($file = $dirname.'/Resources/config/serialization.yaml', false) || - $container->fileExists($file = $dirname.'/Resources/config/serialization.yml', false) + $container->fileExists($file = $configDir.'/serialization.yaml', false) || + $container->fileExists($file = $configDir.'/serialization.yml', false) ) { $fileRecorder('yml', $file); } - if ($container->fileExists($dir = $dirname.'/Resources/config/serialization', '/^$/')) { + if ($container->fileExists($dir = $configDir.'/serialization', '/^$/')) { $this->registerMappingFilesFromDir($dir, $fileRecorder); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Entity/LegacyPerson.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Entity/LegacyPerson.php new file mode 100644 index 0000000000..8135e95e89 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Entity/LegacyPerson.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\LegacyBundle\Entity; + +class LegacyPerson +{ + public $name; + public $age; + + public function __construct(string $name, string $age) + { + $this->name = $name; + $this->age = $age; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/LegacyBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/LegacyBundle.php new file mode 100644 index 0000000000..e38e38824b --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/LegacyBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\LegacyBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class LegacyBundle extends Bundle +{ +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/config/serialization.yaml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/config/serialization.yaml new file mode 100644 index 0000000000..c878793ea9 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/config/serialization.yaml @@ -0,0 +1,4 @@ +Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\LegacyBundle\Entity\LegacyPerson: + attributes: + name: + serialized_name: 'full_name' diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/config/validation.yaml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/config/validation.yaml new file mode 100644 index 0000000000..c59b8cb168 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/config/validation.yaml @@ -0,0 +1,5 @@ +Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\LegacyBundle\Entity\LegacyPerson: + properties: + age: + - GreaterThan: + value: 18 diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/public/legacy.css b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/public/legacy.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/translations/legacy.en.yaml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/translations/legacy.en.yaml new file mode 100644 index 0000000000..1860a9d634 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/translations/legacy.en.yaml @@ -0,0 +1 @@ +ok_label: OK diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/views/index.html.twig b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/views/index.html.twig new file mode 100644 index 0000000000..d86bac9de5 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/views/index.html.twig @@ -0,0 +1 @@ +OK diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/config/serialization.yaml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/config/serialization.yaml new file mode 100644 index 0000000000..d83e457f0a --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/config/serialization.yaml @@ -0,0 +1,4 @@ +Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\ModernBundle\src\Entity\ModernPerson: + attributes: + name: + serialized_name: 'full_name' diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/config/validation.yaml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/config/validation.yaml new file mode 100644 index 0000000000..f3044c3b19 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/config/validation.yaml @@ -0,0 +1,5 @@ +Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\ModernBundle\src\Entity\ModernPerson: + properties: + age: + - GreaterThan: + value: 18 diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/public/modern.css b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/public/modern.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/src/Entity/ModernPerson.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/src/Entity/ModernPerson.php new file mode 100644 index 0000000000..6c22925d65 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/src/Entity/ModernPerson.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\ModernBundle\src\Entity; + +class ModernPerson +{ + public $name; + public $age; + + public function __construct(string $name, string $age) + { + $this->name = $name; + $this->age = $age; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/src/ModernBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/src/ModernBundle.php new file mode 100644 index 0000000000..cc29f998ee --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/src/ModernBundle.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\ModernBundle\src; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class ModernBundle extends Bundle +{ + public function getPath(): string + { + return \dirname(__DIR__); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/templates/index.html.twig b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/templates/index.html.twig new file mode 100644 index 0000000000..d86bac9de5 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/templates/index.html.twig @@ -0,0 +1 @@ +OK diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/translations/modern.en.yaml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/translations/modern.en.yaml new file mode 100644 index 0000000000..1860a9d634 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/translations/modern.en.yaml @@ -0,0 +1 @@ +ok_label: OK diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/BundlePathsTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/BundlePathsTest.php new file mode 100644 index 0000000000..f447300c2c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/BundlePathsTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Command\AssetsInstallCommand; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\LegacyBundle\Entity\LegacyPerson; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\ModernBundle\src\Entity\ModernPerson; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Filesystem\Filesystem; + +class BundlePathsTest extends AbstractWebTestCase +{ + public function testBundlePublicDir() + { + $kernel = static::bootKernel(['test_case' => 'BundlePaths']); + $projectDir = sys_get_temp_dir().'/'.uniqid('sf_bundle_paths', true); + + $fs = new Filesystem(); + $fs->mkdir($projectDir.'/public'); + $command = (new Application($kernel))->add(new AssetsInstallCommand($fs, $projectDir)); + $exitCode = (new CommandTester($command))->execute(['target' => $projectDir.'/public']); + + $this->assertSame(0, $exitCode); + $this->assertFileExists($projectDir.'/public/bundles/modern/modern.css'); + $this->assertFileExists($projectDir.'/public/bundles/legacy/legacy.css'); + + $fs->remove($projectDir); + } + + public function testBundleTwigTemplatesDir() + { + static::bootKernel(['test_case' => 'BundlePaths']); + $twig = static::$container->get('twig'); + $bundlesMetadata = static::$container->getParameter('kernel.bundles_metadata'); + + $this->assertSame([$bundlesMetadata['LegacyBundle']['path'].'/Resources/views'], $twig->getLoader()->getPaths('Legacy')); + $this->assertSame("OK\n", $twig->render('@Legacy/index.html.twig')); + + $this->assertSame([$bundlesMetadata['ModernBundle']['path'].'/templates'], $twig->getLoader()->getPaths('Modern')); + $this->assertSame("OK\n", $twig->render('@Modern/index.html.twig')); + } + + public function testBundleTranslationsDir() + { + static::bootKernel(['test_case' => 'BundlePaths']); + $translator = static::$container->get('translator'); + + $this->assertSame('OK', $translator->trans('ok_label', [], 'legacy')); + $this->assertSame('OK', $translator->trans('ok_label', [], 'modern')); + } + + public function testBundleValidationConfigDir() + { + static::bootKernel(['test_case' => 'BundlePaths']); + $validator = static::$container->get('validator'); + + $this->assertTrue($validator->hasMetadataFor(LegacyPerson::class)); + $this->assertCount(1, $constraintViolationList = $validator->validate(new LegacyPerson('john', 5))); + $this->assertSame('This value should be greater than 18.', $constraintViolationList->get(0)->getMessage()); + + $this->assertTrue($validator->hasMetadataFor(ModernPerson::class)); + $this->assertCount(1, $constraintViolationList = $validator->validate(new ModernPerson('john', 5))); + $this->assertSame('This value should be greater than 18.', $constraintViolationList->get(0)->getMessage()); + } + + public function testBundleSerializationConfigDir() + { + static::bootKernel(['test_case' => 'BundlePaths']); + $serializer = static::$container->get('serializer'); + + $this->assertEquals(['full_name' => 'john', 'age' => 5], $serializer->normalize(new LegacyPerson('john', 5), 'json')); + $this->assertEquals(['full_name' => 'john', 'age' => 5], $serializer->normalize(new ModernPerson('john', 5), 'json')); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/BundlePaths/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/BundlePaths/bundles.php new file mode 100644 index 0000000000..7e01199ea7 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/BundlePaths/bundles.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\LegacyBundle\LegacyBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\ModernBundle\src\ModernBundle; +use Symfony\Bundle\TwigBundle\TwigBundle; + +return [ + new FrameworkBundle(), + new TwigBundle(), + new ModernBundle(), + new LegacyBundle(), +]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/BundlePaths/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/BundlePaths/config.yml new file mode 100644 index 0000000000..4a2d4c57ef --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/BundlePaths/config.yml @@ -0,0 +1,11 @@ +imports: + - { resource: ../config/default.yml } + +framework: + translator: true + validation: true + serializer: true + +twig: + strict_variables: '%kernel.debug%' + exception_controller: ~ diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index e21b60b002..b15516d465 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -188,7 +188,7 @@ class TwigExtension extends Extension } $container->addResource(new FileExistenceResource($defaultOverrideBundlePath)); - if (file_exists($dir = $bundle['path'].'/Resources/views')) { + if (file_exists($dir = $bundle['path'].'/Resources/views') || file_exists($dir = $bundle['path'].'/templates')) { $bundleHierarchy[$name][] = $dir; } $container->addResource(new FileExistenceResource($dir)); diff --git a/src/Symfony/Bundle/TwigBundle/TemplateIterator.php b/src/Symfony/Bundle/TwigBundle/TemplateIterator.php index 4d4d835dfe..b96a18318b 100644 --- a/src/Symfony/Bundle/TwigBundle/TemplateIterator.php +++ b/src/Symfony/Bundle/TwigBundle/TemplateIterator.php @@ -65,9 +65,11 @@ class TemplateIterator implements \IteratorAggregate $name = substr($name, 0, -6); } + $bundleTemplatesDir = is_dir($bundle->getPath().'/Resources/views') ? $bundle->getPath().'/Resources/views' : $bundle->getPath().'/templates'; + $templates = array_merge( $templates, - $this->findTemplatesInDirectory($bundle->getPath().'/Resources/views', $name), + $this->findTemplatesInDirectory($bundleTemplatesDir, $name), $this->findTemplatesInDirectory($this->rootDir.'/Resources/'.$bundle->getName().'/views', $name) ); if (null !== $this->defaultPath) { diff --git a/src/Symfony/Component/HttpKernel/Bundle/Bundle.php b/src/Symfony/Component/HttpKernel/Bundle/Bundle.php index ca190ee695..26dea9b205 100644 --- a/src/Symfony/Component/HttpKernel/Bundle/Bundle.php +++ b/src/Symfony/Component/HttpKernel/Bundle/Bundle.php @@ -135,11 +135,6 @@ abstract class Bundle implements BundleInterface { } - public function getPublicDir(): string - { - return 'Resources/public'; - } - /** * Returns the bundle's container extension class. * diff --git a/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php b/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php index c45754b3ee..88a95d8332 100644 --- a/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php +++ b/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php @@ -19,8 +19,6 @@ use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; * BundleInterface. * * @author Fabien Potencier - * - * @method string getPublicDir() Returns relative path for the public assets directory */ interface BundleInterface extends ContainerAwareInterface { diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index feae351734..4087d4f66a 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -4,9 +4,8 @@ CHANGELOG 4.4.0 ----- - * Implementing the `BundleInterface` without implementing the `getPublicDir()` method is deprecated. - This method will be added to the interface in 5.0. * The `DebugHandlersListener` class has been marked as `final` + * Added new Bundle directory convention consistent with standard skeletons 4.3.0 -----