feature #21924 [FrameworkBundle] Allow to configure Serializer mapping paths (chalasr)

This PR was merged into the 3.3-dev branch.

Discussion
----------

[FrameworkBundle] Allow to configure Serializer mapping paths

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

Follows https://github.com/symfony/symfony/pull/19086 for the Serializer

Commits
-------

5446903296 [FrameworkBundle] Allow configuring serializer mapping paths
This commit is contained in:
Fabien Potencier 2017-03-22 13:19:32 -07:00
commit 3023e4b707
15 changed files with 112 additions and 38 deletions

View File

@ -37,6 +37,8 @@ CHANGELOG
`Symfony\Bundle\WebServerBundle\WebServerBundle` in your AppKernel to use them.
* Added `$defaultLocale` as 3rd argument of `Translator::__construct()`
making `Translator` works with any PSR-11 container
* Added `framework.serializer.mapping` config option allowing to define custom
serialization mapping files and directories
3.2.0
-----

View File

@ -687,6 +687,15 @@ class Configuration implements ConfigurationInterface
->scalarNode('cache')->end()
->scalarNode('name_converter')->end()
->scalarNode('circular_reference_handler')->end()
->arrayNode('mapping')
->addDefaultsIfNotSet()
->fixXmlConfig('path')
->children()
->arrayNode('paths')
->prototype('scalar')->end()
->end()
->end()
->end()
->end()
->end()
->end()

View File

@ -986,8 +986,7 @@ class FrameworkExtension extends Extension
$container->setParameter('validator.translation_domain', $config['translation_domain']);
$files = array('xml' => array(), 'yml' => array());
$this->getValidatorMappingFiles($container, $files);
$this->getValidatorMappingFilesFromConfig($container, $config, $files);
$this->registerValidatorMapping($container, $config, $files);
if (!empty($files['xml'])) {
$validatorBuilder->addMethodCall('addXmlMappings', array($files['xml']));
@ -1028,51 +1027,54 @@ class FrameworkExtension extends Extension
}
}
private function getValidatorMappingFiles(ContainerBuilder $container, array &$files)
private function registerValidatorMapping(ContainerBuilder $container, array $config, array &$files)
{
$fileRecorder = function ($extension, $path) use (&$files) {
$files['yaml' === $extension ? 'yml' : $extension][] = $path;
};
if (interface_exists('Symfony\Component\Form\FormInterface')) {
$reflClass = new \ReflectionClass('Symfony\Component\Form\FormInterface');
$files['xml'][] = dirname($reflClass->getFileName()).'/Resources/config/validation.xml';
$fileRecorder('xml', dirname($reflClass->getFileName()).'/Resources/config/validation.xml');
}
foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
$dirname = $bundle['path'];
if ($container->fileExists($file = $dirname.'/Resources/config/validation.yml', false)) {
$files['yml'][] = $file;
$fileRecorder('yml', $file);
}
if ($container->fileExists($file = $dirname.'/Resources/config/validation.xml', false)) {
$files['xml'][] = $file;
$fileRecorder('xml', $file);
}
if ($container->fileExists($dir = $dirname.'/Resources/config/validation', '/^$/')) {
$this->getValidatorMappingFilesFromDir($dir, $files);
$this->registerMappingFilesFromDir($dir, $fileRecorder);
}
}
$this->registerMappingFilesFromConfig($container, $config, $fileRecorder);
}
private function getValidatorMappingFilesFromDir($dir, array &$files)
private function registerMappingFilesFromDir($dir, callable $fileRecorder)
{
foreach (Finder::create()->followLinks()->files()->in($dir)->name('/\.(xml|ya?ml)$/') as $file) {
$extension = $file->getExtension();
$files['yaml' === $extension ? 'yml' : $extension][] = $file->getRealpath();
$fileRecorder($file->getExtension(), $file->getRealPath());
}
}
private function getValidatorMappingFilesFromConfig(ContainerBuilder $container, array $config, array &$files)
private function registerMappingFilesFromConfig(ContainerBuilder $container, array $config, callable $fileRecorder)
{
foreach ($config['mapping']['paths'] as $path) {
if (is_dir($path)) {
$this->getValidatorMappingFilesFromDir($path, $files);
$this->registerMappingFilesFromDir($path, $fileRecorder);
$container->addResource(new DirectoryResource($path, '/^$/'));
} elseif ($container->fileExists($path, false)) {
if (preg_match('/\.(xml|ya?ml)$/', $path, $matches)) {
$extension = $matches[1];
$files['yaml' === $extension ? 'yml' : $extension][] = $path;
} else {
if (!preg_match('/\.(xml|ya?ml)$/', $path, $matches)) {
throw new \RuntimeException(sprintf('Unsupported mapping type in "%s", supported types are XML & Yaml.', $path));
}
$fileRecorder($matches[1], $path);
} else {
throw new \RuntimeException(sprintf('Could not open file or directory "%s".', $path));
}
@ -1230,39 +1232,30 @@ class FrameworkExtension extends Extension
$serializerLoaders[] = $annotationLoader;
}
$fileRecorder = function ($extension, $path) use (&$serializerLoaders) {
$definition = new Definition(in_array($extension, array('yaml', 'yml')) ? 'Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader' : 'Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($path));
$definition->setPublic(false);
$serializerLoaders[] = $definition;
};
foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
$dirname = $bundle['path'];
if ($container->fileExists($file = $dirname.'/Resources/config/serialization.xml', false)) {
$definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($file));
$definition->setPublic(false);
$serializerLoaders[] = $definition;
$fileRecorder('xml', $file);
}
if ($container->fileExists($file = $dirname.'/Resources/config/serialization.yml', false)) {
$definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader', array($file));
$definition->setPublic(false);
$serializerLoaders[] = $definition;
$fileRecorder('yml', $file);
}
if ($container->fileExists($dir = $dirname.'/Resources/config/serialization')) {
foreach (Finder::create()->followLinks()->files()->in($dir)->name('*.xml') as $file) {
$definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($file->getPathname()));
$definition->setPublic(false);
$serializerLoaders[] = $definition;
}
foreach (Finder::create()->followLinks()->files()->in($dir)->name('*.yml') as $file) {
$definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader', array($file->getPathname()));
$definition->setPublic(false);
$serializerLoaders[] = $definition;
}
$this->registerMappingFilesFromDir($dir, $fileRecorder);
}
}
$this->registerMappingFilesFromConfig($container, $config, $fileRecorder);
$chainLoader->replaceArgument(0, $serializerLoaders);
$container->getDefinition('serializer.mapping.cache_warmer')->replaceArgument(0, $serializerLoaders);

View File

@ -176,7 +176,7 @@
<xsd:complexType name="validation">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="static-method" type="xsd:string" />
<xsd:element name="mapping" type="validation_mapping" />
<xsd:element name="mapping" type="file_mapping" />
</xsd:choice>
<xsd:attribute name="enabled" type="xsd:boolean" />
@ -185,7 +185,7 @@
<xsd:attribute name="static-method" type="xsd:boolean" />
</xsd:complexType>
<xsd:complexType name="validation_mapping">
<xsd:complexType name="file_mapping">
<xsd:sequence>
<xsd:element name="path" type="xsd:string" minOccurs="1" maxOccurs="unbounded" />
</xsd:sequence>
@ -204,6 +204,9 @@
</xsd:complexType>
<xsd:complexType name="serializer">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="mapping" type="file_mapping" />
</xsd:choice>
<xsd:attribute name="enabled" type="xsd:boolean" />
<xsd:attribute name="cache" type="xsd:string" />
<xsd:attribute name="enable-annotations" type="xsd:boolean" />

View File

@ -226,6 +226,7 @@ class ConfigurationTest extends TestCase
'serializer' => array(
'enabled' => !class_exists(FullStack::class),
'enable_annotations' => !class_exists(FullStack::class),
'mapping' => array('paths' => array()),
),
'property_access' => array(
'magic_call' => false,

View File

@ -0,0 +1,15 @@
<?php
$container->loadFromExtension('framework', array(
'annotations' => array('enabled' => true),
'serializer' => array(
'enable_annotations' => true,
'mapping' => array(
'paths' => array(
'%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files',
'%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml',
'%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml',
),
),
),
));

View File

@ -0,0 +1,17 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony">
<framework:config>
<framework:annotations enabled="true" />
<framework:serializer enable-annotations="true">
<framework:mapping>
<framework:path>%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files</framework:path>
<framework:path>%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml</framework:path>
<framework:path>%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml</framework:path>
</framework:mapping>
</framework:serializer>
</framework:config>
</container>

View File

@ -0,0 +1,10 @@
framework:
annotations:
enabled: true
serializer:
enable_annotations: true
mapping:
paths:
- "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files"
- "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml"
- "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml"

View File

@ -28,10 +28,12 @@ use Symfony\Component\Cache\Adapter\ProxyAdapter;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader;
@ -800,6 +802,28 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertEquals(new Reference('foo'), $cache);
}
public function testSerializerMapping()
{
$container = $this->createContainerFromFile('serializer_mapping', array('kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'path' => __DIR__.'/Fixtures/TestBundle', 'parent' => null))));
$configDir = __DIR__.'/Fixtures/TestBundle/Resources/config';
$expectedLoaders = array(
new Definition(AnnotationLoader::class, array(new Reference('annotation_reader'))),
new Definition(XmlFileLoader::class, array($configDir.'/serialization.xml')),
new Definition(YamlFileLoader::class, array($configDir.'/serialization.yml')),
new Definition(XmlFileLoader::class, array($configDir.'/serializer_mapping/files/foo.xml')),
new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/files/foo.yml')),
new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/serialization.yml')),
new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/serialization.yaml')),
);
foreach ($expectedLoaders as $definition) {
$definition->setPublic(false);
}
$loaders = $container->getDefinition('serializer.mapping.chain_loader')->getArgument(0);
$this->assertEquals(sort($expectedLoaders), sort($loaders));
}
public function testAssetHelperWhenAssetsAreEnabled()
{
$container = $this->createContainerFromFile('full');