[FrameworkBundle] PropertyInfo support
This commit is contained in:
parent
864a58faa3
commit
f84a92a601
@ -0,0 +1,76 @@
|
|||||||
|
<?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\DependencyInjection\Compiler;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds extractors to the property_info service.
|
||||||
|
*
|
||||||
|
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||||
|
*/
|
||||||
|
class PropertyInfoPass implements CompilerPassInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function process(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
if (!$container->hasDefinition('property_info')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$listExtractors = $this->findAndSortTaggedServices('property_info.list_extractor', $container);
|
||||||
|
$container->getDefinition('property_info')->replaceArgument(0, $listExtractors);
|
||||||
|
|
||||||
|
$typeExtractors = $this->findAndSortTaggedServices('property_info.type_extractor', $container);
|
||||||
|
$container->getDefinition('property_info')->replaceArgument(1, $typeExtractors);
|
||||||
|
|
||||||
|
$descriptionExtractors = $this->findAndSortTaggedServices('property_info.description_extractor', $container);
|
||||||
|
$container->getDefinition('property_info')->replaceArgument(2, $descriptionExtractors);
|
||||||
|
|
||||||
|
$accessExtractors = $this->findAndSortTaggedServices('property_info.access_extractor', $container);
|
||||||
|
$container->getDefinition('property_info')->replaceArgument(3, $accessExtractors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all services with the given tag name and order them by their priority.
|
||||||
|
*
|
||||||
|
* @param string $tagName
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function findAndSortTaggedServices($tagName, ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$services = $container->findTaggedServiceIds($tagName);
|
||||||
|
|
||||||
|
$sortedServices = array();
|
||||||
|
foreach ($services as $serviceId => $tags) {
|
||||||
|
foreach ($tags as $attributes) {
|
||||||
|
$priority = isset($attributes['priority']) ? $attributes['priority'] : 0;
|
||||||
|
$sortedServices[$priority][] = new Reference($serviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($sortedServices)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
krsort($sortedServices);
|
||||||
|
|
||||||
|
// Flatten the array
|
||||||
|
return call_user_func_array('array_merge', $sortedServices);
|
||||||
|
}
|
||||||
|
}
|
@ -38,6 +38,16 @@ class SerializerPass implements CompilerPassInterface
|
|||||||
$container->getDefinition('serializer')->replaceArgument(1, $encoders);
|
$container->getDefinition('serializer')->replaceArgument(1, $encoders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all services with the given tag name and order them by their priority.
|
||||||
|
*
|
||||||
|
* @param string $tagName
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*/
|
||||||
private function findAndSortTaggedServices($tagName, ContainerBuilder $container)
|
private function findAndSortTaggedServices($tagName, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$services = $container->findTaggedServiceIds($tagName);
|
$services = $container->findTaggedServiceIds($tagName);
|
||||||
@ -48,8 +58,8 @@ class SerializerPass implements CompilerPassInterface
|
|||||||
|
|
||||||
$sortedServices = array();
|
$sortedServices = array();
|
||||||
foreach ($services as $serviceId => $tags) {
|
foreach ($services as $serviceId => $tags) {
|
||||||
foreach ($tags as $tag) {
|
foreach ($tags as $attributes) {
|
||||||
$priority = isset($tag['priority']) ? $tag['priority'] : 0;
|
$priority = isset($attributes['priority']) ? $attributes['priority'] : 0;
|
||||||
$sortedServices[$priority][] = new Reference($serviceId);
|
$sortedServices[$priority][] = new Reference($serviceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,6 +178,7 @@ class Configuration implements ConfigurationInterface
|
|||||||
$this->addAnnotationsSection($rootNode);
|
$this->addAnnotationsSection($rootNode);
|
||||||
$this->addSerializerSection($rootNode);
|
$this->addSerializerSection($rootNode);
|
||||||
$this->addPropertyAccessSection($rootNode);
|
$this->addPropertyAccessSection($rootNode);
|
||||||
|
$this->addPropertyInfoSection($rootNode);
|
||||||
|
|
||||||
return $treeBuilder;
|
return $treeBuilder;
|
||||||
}
|
}
|
||||||
@ -723,4 +724,16 @@ class Configuration implements ConfigurationInterface
|
|||||||
->end()
|
->end()
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addPropertyInfoSection(ArrayNodeDefinition $rootNode)
|
||||||
|
{
|
||||||
|
$rootNode
|
||||||
|
->children()
|
||||||
|
->arrayNode('property_info')
|
||||||
|
->info('Property info configuration')
|
||||||
|
->canBeEnabled()
|
||||||
|
->end()
|
||||||
|
->end()
|
||||||
|
;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,6 +131,10 @@ class FrameworkExtension extends Extension
|
|||||||
$this->registerSerializerConfiguration($config['serializer'], $container, $loader);
|
$this->registerSerializerConfiguration($config['serializer'], $container, $loader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($config['property_info'])) {
|
||||||
|
$this->registerPropertyInfoConfiguration($config['property_info'], $container, $loader);
|
||||||
|
}
|
||||||
|
|
||||||
$loader->load('debug_prod.xml');
|
$loader->load('debug_prod.xml');
|
||||||
$definition = $container->findDefinition('debug.debug_handlers_listener');
|
$definition = $container->findDefinition('debug.debug_handlers_listener');
|
||||||
|
|
||||||
@ -986,6 +990,28 @@ class FrameworkExtension extends Extension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads property info configuration.
|
||||||
|
*
|
||||||
|
* @param array $config
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
* @param XmlFileLoader $loader
|
||||||
|
*/
|
||||||
|
private function registerPropertyInfoConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
|
||||||
|
{
|
||||||
|
if (!$config['enabled']) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$loader->load('property_info.xml');
|
||||||
|
|
||||||
|
if (class_exists('phpDocumentor\Reflection\ClassReflector')) {
|
||||||
|
$definition = $container->register('property_info.php_doc_extractor', 'Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor');
|
||||||
|
$definition->addTag('property_info.description_extractor', array('priority' => -1000));
|
||||||
|
$definition->addTag('property_info.type_extractor', array('priority' => -1001));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the base path for the XSD files.
|
* Returns the base path for the XSD files.
|
||||||
*
|
*
|
||||||
|
@ -15,6 +15,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintVal
|
|||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass;
|
||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass;
|
||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass;
|
||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
|
||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
|
||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass;
|
||||||
@ -90,6 +91,7 @@ class FrameworkBundle extends Bundle
|
|||||||
$container->addCompilerPass(new TranslationDumperPass());
|
$container->addCompilerPass(new TranslationDumperPass());
|
||||||
$container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING);
|
$container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||||
$container->addCompilerPass(new SerializerPass());
|
$container->addCompilerPass(new SerializerPass());
|
||||||
|
$container->addCompilerPass(new PropertyInfoPass());
|
||||||
|
|
||||||
if ($container->getParameter('kernel.debug')) {
|
if ($container->getParameter('kernel.debug')) {
|
||||||
$container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING);
|
$container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<service id="property_info" class="Symfony\Component\PropertyInfo\PropertyInfoExtractor" >
|
||||||
|
<argument type="collection" />
|
||||||
|
<argument type="collection" />
|
||||||
|
<argument type="collection" />
|
||||||
|
<argument type="collection" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<!-- Extractor -->
|
||||||
|
<service id="property_info.reflection_extractor" class="Symfony\Component\PropertyInfo\ReflectionExtractor" public="false">
|
||||||
|
<tag name="property_info.list_extractor" priority="-1000" />
|
||||||
|
<tag name="property_info.type_extractor" priority="-1000" />
|
||||||
|
<tag name="property_info.access_extractor" priority="-1000" />
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
</container>
|
@ -33,6 +33,7 @@
|
|||||||
<xsd:element name="annotations" type="annotations" minOccurs="0" maxOccurs="1" />
|
<xsd:element name="annotations" type="annotations" minOccurs="0" maxOccurs="1" />
|
||||||
<xsd:element name="property-access" type="property_access" minOccurs="0" maxOccurs="1" />
|
<xsd:element name="property-access" type="property_access" minOccurs="0" maxOccurs="1" />
|
||||||
<xsd:element name="serializer" type="serializer" minOccurs="0" maxOccurs="1" />
|
<xsd:element name="serializer" type="serializer" minOccurs="0" maxOccurs="1" />
|
||||||
|
<xsd:element name="property-info" type="property_info" minOccurs="0" maxOccurs="1" />
|
||||||
</xsd:all>
|
</xsd:all>
|
||||||
|
|
||||||
<xsd:attribute name="http-method-override" type="xsd:boolean" />
|
<xsd:attribute name="http-method-override" type="xsd:boolean" />
|
||||||
@ -220,4 +221,8 @@
|
|||||||
<xsd:attribute name="enable-annotations" type="xsd:boolean" />
|
<xsd:attribute name="enable-annotations" type="xsd:boolean" />
|
||||||
<xsd:attribute name="name-converter" type="xsd:string" />
|
<xsd:attribute name="name-converter" type="xsd:string" />
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|
||||||
|
<xsd:complexType name="property_info">
|
||||||
|
<xsd:attribute name="enabled" type="xsd:boolean" />
|
||||||
|
</xsd:complexType>
|
||||||
</xsd:schema>
|
</xsd:schema>
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
<?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\DependencyInjection\Compiler;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
|
||||||
|
class PropertyInfoPassTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testServicesAreOrderedAccordingToPriority()
|
||||||
|
{
|
||||||
|
$services = array(
|
||||||
|
'n3' => array('tag' => array()),
|
||||||
|
'n1' => array('tag' => array('priority' => 200)),
|
||||||
|
'n2' => array('tag' => array('priority' => 100)),
|
||||||
|
);
|
||||||
|
|
||||||
|
$expected = array(
|
||||||
|
new Reference('n1'),
|
||||||
|
new Reference('n2'),
|
||||||
|
new Reference('n3'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds'));
|
||||||
|
|
||||||
|
$container->expects($this->any())
|
||||||
|
->method('findTaggedServiceIds')
|
||||||
|
->will($this->returnValue($services));
|
||||||
|
|
||||||
|
$propertyInfoPass = new PropertyInfoPass();
|
||||||
|
|
||||||
|
$method = new \ReflectionMethod(
|
||||||
|
'Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass',
|
||||||
|
'findAndSortTaggedServices'
|
||||||
|
);
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$actual = $method->invoke($propertyInfoPass, 'tag', $container);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturningEmptyArrayWhenNoService()
|
||||||
|
{
|
||||||
|
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds'));
|
||||||
|
|
||||||
|
$container->expects($this->any())
|
||||||
|
->method('findTaggedServiceIds')
|
||||||
|
->will($this->returnValue(array()));
|
||||||
|
|
||||||
|
$propertyInfoPass = new PropertyInfoPass();
|
||||||
|
|
||||||
|
$method = new \ReflectionMethod(
|
||||||
|
'Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass',
|
||||||
|
'findAndSortTaggedServices'
|
||||||
|
);
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$actual = $method->invoke($propertyInfoPass, 'tag', $container);
|
||||||
|
|
||||||
|
$this->assertEquals(array(), $actual);
|
||||||
|
}
|
||||||
|
}
|
@ -87,7 +87,7 @@ class SerializerPassTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds'));
|
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds'));
|
||||||
|
|
||||||
$container->expects($this->atLeastOnce())
|
$container->expects($this->any())
|
||||||
->method('findTaggedServiceIds')
|
->method('findTaggedServiceIds')
|
||||||
->will($this->returnValue($services));
|
->will($this->returnValue($services));
|
||||||
|
|
||||||
|
@ -179,6 +179,9 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||||||
'magic_call' => false,
|
'magic_call' => false,
|
||||||
'throw_exception_on_invalid_index' => false,
|
'throw_exception_on_invalid_index' => false,
|
||||||
),
|
),
|
||||||
|
'property_info' => array(
|
||||||
|
'enabled' => false,
|
||||||
|
),
|
||||||
'assets' => array(
|
'assets' => array(
|
||||||
'version' => null,
|
'version' => null,
|
||||||
'version_format' => '%%s?%%s',
|
'version_format' => '%%s?%%s',
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$container->loadFromExtension('framework', array(
|
||||||
|
'property_info' => array(
|
||||||
|
'enabled' => true,
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<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"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
|
||||||
|
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
||||||
|
|
||||||
|
<framework:config>
|
||||||
|
<framework:property-info enabled="true" />
|
||||||
|
</framework:config>
|
||||||
|
</container>
|
@ -0,0 +1,3 @@
|
|||||||
|
framework:
|
||||||
|
property_info:
|
||||||
|
enabled: true
|
@ -488,6 +488,18 @@ abstract class FrameworkExtensionTest extends TestCase
|
|||||||
$this->assertFalse($container->hasDefinition('serializer'));
|
$this->assertFalse($container->hasDefinition('serializer'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPropertyInfoDisabled()
|
||||||
|
{
|
||||||
|
$container = $this->createContainerFromFile('default_config');
|
||||||
|
$this->assertFalse($container->has('property_info'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPropertyInfoEnabled()
|
||||||
|
{
|
||||||
|
$container = $this->createContainerFromFile('property_info');
|
||||||
|
$this->assertTrue($container->has('property_info'));
|
||||||
|
}
|
||||||
|
|
||||||
protected function createContainer(array $data = array())
|
protected function createContainer(array $data = array())
|
||||||
{
|
{
|
||||||
return new ContainerBuilder(new ParameterBag(array_merge(array(
|
return new ContainerBuilder(new ParameterBag(array_merge(array(
|
||||||
|
@ -47,7 +47,9 @@
|
|||||||
"symfony/expression-language": "~2.6|~3.0.0",
|
"symfony/expression-language": "~2.6|~3.0.0",
|
||||||
"symfony/process": "~2.0,>=2.0.5|~3.0.0",
|
"symfony/process": "~2.0,>=2.0.5|~3.0.0",
|
||||||
"symfony/validator": "~2.5|~3.0.0",
|
"symfony/validator": "~2.5|~3.0.0",
|
||||||
"symfony/yaml": "~2.0,>=2.0.5|~3.0.0"
|
"symfony/yaml": "~2.0,>=2.0.5|~3.0.0",
|
||||||
|
"symfony/property-info": "~2.8|~3.0.0",
|
||||||
|
"phpdocumentor/reflection": "^1.0.7"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"symfony/console": "For using the console commands",
|
"symfony/console": "For using the console commands",
|
||||||
@ -55,7 +57,8 @@
|
|||||||
"symfony/form": "For using forms",
|
"symfony/form": "For using forms",
|
||||||
"symfony/serializer": "For using the serializer service",
|
"symfony/serializer": "For using the serializer service",
|
||||||
"symfony/validator": "For using validation",
|
"symfony/validator": "For using validation",
|
||||||
"symfony/yaml": "For using the debug:config and lint:yaml commands"
|
"symfony/yaml": "For using the debug:config and lint:yaml commands",
|
||||||
|
"symfony/property-info": "For using the property_info_extractor service"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": { "Symfony\\Bundle\\FrameworkBundle\\": "" }
|
"psr-4": { "Symfony\\Bundle\\FrameworkBundle\\": "" }
|
||||||
|
Reference in New Issue
Block a user