adds visibility to aliases

This commit is contained in:
Johannes Schmitt 2011-01-07 15:44:29 +01:00 committed by Fabien Potencier
parent 89433fbcfe
commit 3785a99b94
22 changed files with 235 additions and 25 deletions

View File

@ -298,7 +298,7 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
$this->assertEquals('doctrine.dbal.conn1_connection.configuration', (string) $args[1]);
$this->assertEquals('doctrine.dbal.conn1_connection.event_manager', (string) $args[2]);
$this->assertEquals('doctrine.orm.dm2_entity_manager', $container->getAlias('doctrine.orm.entity_manager'));
$this->assertEquals('doctrine.orm.dm2_entity_manager', (string) $container->getAlias('doctrine.orm.entity_manager'));
$definition = $container->getDefinition('doctrine.orm.dm1_entity_manager');
$this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getClass());

View File

@ -179,7 +179,7 @@ abstract class AbstractMongoDBExtensionTest extends TestCase
$this->assertEquals('%doctrine.odm.mongodb.connection_class%', $definition->getClass());
$this->assertEquals(array('mongodb://localhost:27017', array('connect' => true), new Reference('doctrine.odm.mongodb.conn1_configuration')), $definition->getArguments());
$this->assertEquals('doctrine.odm.mongodb.dm2_document_manager', $container->getAlias('doctrine.odm.mongodb.document_manager'));
$this->assertEquals('doctrine.odm.mongodb.dm2_document_manager', (string) $container->getAlias('doctrine.odm.mongodb.document_manager'));
$definition = $container->getDefinition('doctrine.odm.mongodb.dm1_document_manager');
$this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getClass());

View File

@ -2,6 +2,7 @@
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
@ -443,7 +444,7 @@ class SecurityExtension extends Extension
// Existing DAO service provider
if (isset($provider['id'])) {
$container->setAlias($name, $provider['id']);
$container->setAlias($name, new Alias($provider['id'], false));
return $provider['id'];
}

View File

@ -26,7 +26,7 @@
</parameters>
<services>
<service id="security.acl.dbal.connection" alias="doctrine.dbal.default_connection" />
<service id="security.acl.dbal.connection" alias="doctrine.dbal.default_connection" public="false" />
<service id="security.acl.object_identity_retrieval_strategy" class="%security.acl.object_identity_retrieval_strategy.class%" public="false"></service>
@ -62,7 +62,7 @@
<argument>%security.acl.cache.doctrine.prefix%</argument>
</service>
<service id="security.acl.cache.doctrine.cache_impl" alias="doctrine.orm.default_result_cache" />
<service id="security.acl.cache.doctrine.cache_impl" alias="doctrine.orm.default_result_cache" public="false" />
<service id="security.acl.permission.map" class="%security.acl.permission.map.class%" public="false"></service>

View File

@ -16,7 +16,7 @@
</parameters>
<services>
<service id="zend.logger" class="%zend.logger.class%" />
<service id="zend.logger" class="%zend.logger.class%" public="false" />
<service id="zend.logger.writer.filesystem" class="%zend.logger.writer.filesystem.class%" public="false">
<tag name="zend.logger.writer" />

View File

@ -0,0 +1,30 @@
<?php
namespace Symfony\Component\DependencyInjection;
class Alias
{
protected $id;
protected $public;
public function __construct($id, $public = true)
{
$this->id = strtolower($id);
$this->public = $public;
}
public function isPublic()
{
return $this->public;
}
public function setPublic($boolean)
{
$this->public = (Boolean) $boolean;
}
public function __toString()
{
return $this->id;
}
}

View File

@ -23,8 +23,19 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
*/
class InlineServiceDefinitionsPass implements CompilerPassInterface
{
protected $aliasMap;
public function process(ContainerBuilder $container)
{
$this->aliasMap = array();
foreach ($container->getAliases() as $id => $alias) {
if (!$alias->isPublic()) {
continue;
}
$this->aliasMap[$id] = (string) $alias;
}
foreach ($container->getDefinitions() as $id => $definition) {
$definition->setArguments(
$this->inlineArguments($container, $definition->getArguments())
@ -65,7 +76,7 @@ class InlineServiceDefinitionsPass implements CompilerPassInterface
return false;
}
$references = count(array_keys($container->getAliases(), $id, true));
$references = count(array_keys($this->aliasMap, $id, true));
foreach ($container->getDefinitions() as $cDefinition)
{
if ($references > 1) {

View File

@ -37,6 +37,8 @@ class PassConfig
);
$this->removingPasses = array(
new RemovePrivateAliasesPass(),
new ReplaceAliasByActualDefinitionPass(),
new InlineServiceDefinitionsPass(),
new RemoveUnusedDefinitionsPass(),
);

View File

@ -0,0 +1,26 @@
<?php
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Remove private aliases from the container. They were only used to establish
* dependencies between services, and these dependencies have been resolved in
* one of the previous passes.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class RemovePrivateAliasesPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
foreach ($container->getAliases() as $id => $alias) {
if ($alias->isPublic()) {
continue;
}
$container->removeAlias($id);
}
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Replaces aliases with actual service definitions, effectively removing these
* aliases.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
foreach ($container->getAliases() as $id => $alias) {
$aliasId = (string) $alias;
$definition = $container->getDefinition($aliasId = (string) $alias);
if ($definition->isPublic()) {
continue;
}
$definition->setPublic(true);
$container->setDefinition($id, $definition);
$container->remove($aliasId);
$this->updateReferences($container, $aliasId, $id);
// we have to restart the process due to concurrent modification of
// the container
$this->process($container);
break;
}
}
protected function updateReferences($container, $currentId, $newId)
{
foreach ($container->getAliases() as $id => $alias) {
if ($currentId === (string) $alias) {
$container->setAlias($id, $newId);
}
}
foreach ($container->getDefinitions() as $definition) {
$definition->setArguments(
$this->updateArgumentReferences($definition->getArguments(), $currentId, $newId)
);
$definition->setMethodCalls(
$this->updateArgumentReferences($definition->getMethodCalls(), $currentId, $newId)
);
}
}
protected function updateArgumentReferences(array $arguments, $currentId, $newId)
{
foreach ($arguments as $k => $argument) {
if (is_array($argument)) {
$arguments[$k] = $this->updateArgumentReferences($argument, $currentId, $newId);
} else if ($argument instanceof Reference) {
if ($currentId === (string) $argument) {
$arguments[$k] = new Reference($newId, $argument->getInvalidBehavior());
}
}
}
return $arguments;
}
}

View File

@ -2,6 +2,7 @@
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -32,6 +33,13 @@ class ResolveReferencesToAliasesPass implements CompilerPassInterface
$definition->setArguments($this->processArguments($definition->getArguments()));
$definition->setMethodCalls($this->processArguments($definition->getMethodCalls()));
}
foreach ($container->getAliases() as $id => $alias) {
$aliasId = (string) $alias;
if ($aliasId !== $defId = $this->getDefinitionId($aliasId)) {
$container->setAlias($id, new Alias($defId, $alias->isPublic()));
}
}
}
protected function processArguments(array $arguments)
@ -54,7 +62,7 @@ class ResolveReferencesToAliasesPass implements CompilerPassInterface
protected function getDefinitionId($id)
{
if ($this->container->hasAlias($id)) {
return $this->getDefinitionId($this->container->getAlias($id));
return $this->getDefinitionId((string) $this->container->getAlias($id));
}
return $id;

View File

@ -379,12 +379,17 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
* Sets an alias for an existing service.
*
* @param string $alias The alias to create
* @param string $id The service to alias
* @param mixed $id The service to alias
*/
public function setAlias($alias, $id)
{
$alias = strtolower($alias);
$id = strtolower($id);
if (is_string($id)) {
$id = new Alias($id);
} else if (!$id instanceof Alias) {
throw new \InvalidArgumentException('$id must be a string, or an Alias object.');
}
unset($this->definitions[$alias]);
@ -410,7 +415,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
public function hasAlias($id)
{
return array_key_exists(strtolower($id), $this->aliases);
return isset($this->aliases[strtolower($id)]);
}
/**
@ -629,7 +634,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
$id = strtolower($id);
if ($this->hasAlias($id)) {
return $this->findDefinition($this->getAlias($id));
return $this->findDefinition((string) $this->getAlias($id));
}
return $this->getDefinition($id);

View File

@ -782,7 +782,7 @@ EOF;
return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
} else {
if ($this->container->hasAlias($id)) {
$id = $this->container->getAlias($id);
$id = (string) $this->container->getAlias($id);
}
return sprintf('$this->get(\'%s\')', $id);

View File

@ -138,7 +138,10 @@ class XmlDumper extends Dumper
protected function addServiceAlias($alias, $id)
{
return sprintf(" <service id=\"%s\" alias=\"%s\" />\n", $alias, $id);
if ($id->isPublic()) {
return sprintf(" <service id=\"%s\" alias=\"%s\" />\n", $alias, $id);
}
return sprintf(" <service id=\"%s\" alias=\"%s\" public=\"false\" />\n", $alias, $id);
}
protected function addServices()

View File

@ -116,7 +116,11 @@ class YamlDumper extends Dumper
protected function addServiceAlias($alias, $id)
{
return sprintf(" %s: @%s\n", $alias, $id);
if ($id->isPublic()) {
return sprintf(" %s: @%s\n", $alias, $id);
} else {
return sprintf(" %s:\n alias: %s\n public: false", $alias, $id);
}
}
protected function addServices()

View File

@ -2,6 +2,8 @@
namespace Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\InterfaceInjector;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
@ -124,7 +126,11 @@ class XmlFileLoader extends FileLoader
protected function parseDefinition($id, $service, $file)
{
if ((string) $service['alias']) {
$this->container->setAlias($id, (string) $service['alias']);
$public = true;
if (isset($service['public'])) {
$public = $service->getAttributeAsPhp('public');
}
$this->container->setAlias($id, new Alias((string) $service['alias'], $public));
return;
}

View File

@ -2,6 +2,8 @@
namespace Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\InterfaceInjector;
use Symfony\Component\DependencyInjection\Definition;
@ -128,6 +130,11 @@ class YamlFileLoader extends FileLoader
if (is_string($service) && 0 === strpos($service, '@')) {
$this->container->setAlias($id, substr($service, 1));
return;
} else if (isset($service['alias'])) {
$public = !array_key_exists('public', $service) || (Boolean) $service['public'];
$this->container->setAlias($id, new Alias($service['alias'], $public));
return;
}

View File

@ -10,6 +10,8 @@
namespace Symfony\Tests\Component\DependencyInjection;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
@ -137,7 +139,7 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder->setAlias('bar', 'foo');
$this->assertTrue($builder->hasAlias('bar'), '->hasAlias() returns true if the alias exists');
$this->assertFalse($builder->hasAlias('foobar'), '->hasAlias() returns false if the alias does not exist');
$this->assertEquals('foo', $builder->getAlias('bar'), '->getAlias() returns the aliased service');
$this->assertEquals('foo', (string) $builder->getAlias('bar'), '->getAlias() returns the aliased service');
$this->assertTrue($builder->has('bar'), '->setAlias() defines a new service');
$this->assertTrue($builder->get('bar') === $builder->get('foo'), '->setAlias() creates a service that is an alias to another one');
@ -157,11 +159,21 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder = new ContainerBuilder();
$builder->setAlias('bar', 'foo');
$builder->setAlias('foobar', 'foo');
$this->assertEquals(array('bar' => 'foo', 'foobar' => 'foo'), $builder->getAliases(), '->getAliases() returns all service aliases');
$builder->setAlias('moo', new Alias('foo', false));
$aliases = $builder->getAliases();
$this->assertEquals('foo', (string) $aliases['bar']);
$this->assertTrue($aliases['bar']->isPublic());
$this->assertEquals('foo', (string) $aliases['foobar']);
$this->assertEquals('foo', (string) $aliases['moo']);
$this->assertFalse($aliases['moo']->isPublic());
$builder->register('bar', 'stdClass');
$this->assertEquals(array('foobar' => 'foo'), $builder->getAliases(), '->getAliases() does not return aliased services that have been overridden');
$this->assertFalse($builder->hasAlias('bar'));
$builder->set('foobar', 'stdClass');
$this->assertEquals(array(), $builder->getAliases(), '->getAliases() does not return aliased services that have been overridden');
$builder->set('moo', 'stdClass');
$this->assertEquals(0, count($builder->getAliases()), '->getAliases() does not return aliased services that have been overridden');
}
/**
@ -171,7 +183,10 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
{
$builder = new ContainerBuilder();
$builder->setAliases(array('bar' => 'foo', 'foobar' => 'foo'));
$this->assertEquals(array('bar' => 'foo', 'foobar' => 'foo'), $builder->getAliases(), '->getAliases() returns all service aliases');
$aliases = $builder->getAliases();
$this->assertTrue(isset($aliases['bar']));
$this->assertTrue(isset($aliases['foobar']));
}
/**
@ -182,7 +197,10 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder = new ContainerBuilder();
$builder->setAliases(array('bar' => 'foo'));
$builder->addAliases(array('foobar' => 'foo'));
$this->assertEquals(array('bar' => 'foo', 'foobar' => 'foo'), $builder->getAliases(), '->getAliases() returns all service aliases');
$aliases = $builder->getAliases();
$this->assertTrue(isset($aliases['bar']));
$this->assertTrue(isset($aliases['foobar']));
}
/**
@ -327,7 +345,10 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$config->setAlias('alias_for_foo', 'foo');
$container->merge($config);
$this->assertEquals(array('foo', 'bar', 'baz'), array_keys($container->getDefinitions()), '->merge() merges definitions already defined ones');
$this->assertEquals(array('alias_for_foo' => 'foo'), $container->getAliases(), '->merge() registers defined aliases');
$aliases = $container->getAliases();
$this->assertTrue(isset($aliases['alias_for_foo']));
$this->assertEquals('foo', (string) $aliases['alias_for_foo']);
$container = new ContainerBuilder();
$container->register('foo', 'FooClass');

View File

@ -43,6 +43,7 @@
</call>
</service>
<service id="alias_for_foo" alias="foo" />
<service id="another_alias_for_foo" alias="foo" public="false" />
<service id="factory_service" factory-method="getInstance" factory-service="baz_factory" />
</services>
</container>

View File

@ -18,4 +18,7 @@ services:
calls:
- [ setBar, [ foo, @foo, [true, false] ] ]
alias_for_foo: @foo
another_alias_for_foo:
alias: foo
public: false
factory_service: { class: BazClass, factory_method: getInstance, factory_service: baz_factory }

View File

@ -146,7 +146,11 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$aliases = $container->getAliases();
$this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses <service> elements');
$this->assertEquals('foo', $aliases['alias_for_foo'], '->load() parses aliases');
$this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases');
$this->assertTrue($aliases['alias_for_foo']->isPublic());
$this->assertTrue(isset($aliases['another_alias_for_foo']));
$this->assertEquals('foo', (string) $aliases['another_alias_for_foo']);
$this->assertFalse($aliases['another_alias_for_foo']->isPublic());
}
public function testConvertDomElementToArray()

View File

@ -110,7 +110,11 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$aliases = $container->getAliases();
$this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses aliases');
$this->assertEquals('foo', $aliases['alias_for_foo'], '->load() parses aliases');
$this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases');
$this->assertTrue($aliases['alias_for_foo']->isPublic());
$this->assertTrue(isset($aliases['another_alias_for_foo']));
$this->assertEquals('foo', (string) $aliases['another_alias_for_foo']);
$this->assertFalse($aliases['another_alias_for_foo']->isPublic());
}
public function testExtensions()