Skip to content
Permalink
Browse files

feature #32845 [HttpKernel][FrameworkBundle] Add alternative conventi…

…on for bundle directories (yceruto)

This PR was squashed before being merged into the 4.4 branch (closes #32845).

Discussion
----------

[HttpKernel][FrameworkBundle] Add alternative convention for bundle directories

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #32453
| License       | MIT
| Doc PR        | TODO

We already know that bundles must be compatible with many Symfony's versions, so it is very likely that current bundles won't be able to use this feature soon, unless they create symbolic links to support both structures.

The point is that this is already happening, so in the future when our bundles stop to support <=4.3 then you'll be sure to change the current directory structure.

We have recently added the `getPublicDir()` method in #31975, here I'm removing it in favor of hardcoding a new convention.

I've added some functional tests in which I've changed everything to this structure:
```
-- ModernBundle
   |-- config/
   |-- public/
   |-- src/
       |-- ModernBundle.php
   |-- templates/
   |-- translations/
```
WDYT?

Commits
-------

6996e1c [HttpKernel][FrameworkBundle] Add alternative convention for bundle directories
  • Loading branch information...
nicolas-grekas committed Aug 13, 2019
2 parents d6773bc + 6996e1c commit 32e0a2520035104c49db020b3e7d425909cbb942
Showing with 308 additions and 42 deletions.
  1. +26 −2 UPGRADE-4.4.md
  2. +26 −1 UPGRADE-5.0.md
  3. +3 −4 src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php
  4. +1 −7 src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php
  5. +6 −4 src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php
  6. +3 −2 src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php
  7. +11 −11 src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
  8. +24 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Entity/LegacyPerson.php
  9. +18 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/LegacyBundle.php
  10. +4 −0 ...y/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/config/serialization.yaml
  11. +5 −0 ...fony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/config/validation.yaml
  12. 0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/public/legacy.css
  13. +1 −0 ...Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/translations/legacy.en.yaml
  14. +1 −0 ...mfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/LegacyBundle/Resources/views/index.html.twig
  15. +4 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/config/serialization.yaml
  16. +5 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/config/validation.yaml
  17. 0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/public/modern.css
  18. +24 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/src/Entity/ModernPerson.php
  19. +22 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/src/ModernBundle.php
  20. +1 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/templates/index.html.twig
  21. +1 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/ModernBundle/translations/modern.en.yaml
  22. +84 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/BundlePathsTest.php
  23. +22 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/BundlePaths/bundles.php
  24. +11 −0 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/BundlePaths/config.yml
  25. +1 −1 src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php
  26. +3 −1 src/Symfony/Bundle/TwigBundle/TemplateIterator.php
  27. +0 −5 src/Symfony/Component/HttpKernel/Bundle/Bundle.php
  28. +0 −2 src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php
  29. +1 −2 src/Symfony/Component/HttpKernel/CHANGELOG.md
@@ -113,9 +113,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
----
@@ -291,7 +291,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
@@ -308,6 +307,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
----
@@ -48,11 +48,10 @@ private function updateValidatorMappingFiles(ContainerBuilder $container, string
}
$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;
}
}
@@ -137,13 +137,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$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;
}
@@ -162,7 +162,9 @@ protected function execute(InputInterface $input, OutputInterface $output)
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 @@ protected function execute(InputInterface $input, OutputInterface $output)
$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 @@ protected function execute(InputInterface $input, OutputInterface $output)
}
} 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);
@@ -154,7 +154,9 @@ protected function execute(InputInterface $input, OutputInterface $output)
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 @@ protected function execute(InputInterface $input, OutputInterface $output)
$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;
}
@@ -1156,7 +1156,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
$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;
@@ -1318,20 +1318,20 @@ private function registerValidatorMapping(ContainerBuilder $container, array $co
}
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);
}
}
@@ -1512,20 +1512,20 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
};
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);
}
}
@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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;
}
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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
{
}
@@ -0,0 +1,4 @@
Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\LegacyBundle\Entity\LegacyPerson:
attributes:
name:
serialized_name: 'full_name'
@@ -0,0 +1,5 @@
Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\LegacyBundle\Entity\LegacyPerson:
properties:
age:
- GreaterThan:
value: 18
@@ -0,0 +1,4 @@
Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\ModernBundle\src\Entity\ModernPerson:
attributes:
name:
serialized_name: 'full_name'
@@ -0,0 +1,5 @@
Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\ModernBundle\src\Entity\ModernPerson:
properties:
age:
- GreaterThan:
value: 18
@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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;
}
}
@@ -0,0 +1,22 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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__);
}
}

0 comments on commit 32e0a25

Please sign in to comment.
You can’t perform that action at this time.