Skip to content

Commit

Permalink
Added support for deprecating aliases (runtime+dumper)
Browse files Browse the repository at this point in the history
  • Loading branch information
Renan committed Jan 24, 2019
1 parent 0eb071b commit 6711e59
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 24 deletions.
20 changes: 4 additions & 16 deletions src/Symfony/Component/DependencyInjection/Alias.php
Expand Up @@ -86,10 +86,10 @@ public function isPrivate()
}

/**
* Whether this alias is deprecated, that means it should not be called
* Whether this alias is deprecated, that means it should not be referenced
* anymore.
*
* @param bool $status Defaults to true
* @param bool $status Whether this alias is deprecated, defaults to true
* @param string $template Optional template message to use if the alias is deprecated
*
* @return $this
Expand All @@ -115,24 +115,12 @@ public function setDeprecated($status = true, $template = null)
return $this;
}

/**
* Returns whether this alias is deprecated.
*
* @return bool
*/
public function isDeprecated()
public function isDeprecated(): bool
{
return $this->deprecated;
}

/**
* Message to use if this alias is deprecated.
*
* @param string $id Service id relying on this alias
*
* @return string
*/
public function getDeprecationMessage($id)
public function getDeprecationMessage(string $id): string
{
return str_replace('%service_id%', $id, $this->deprecationTemplate ?: self::$defaultDeprecationTemplate);
}
Expand Down
Expand Up @@ -31,6 +31,7 @@ public function process(ContainerBuilder $container)

foreach ($container->getAliases() as $id => $alias) {
$aliasId = (string) $alias;

if ($aliasId !== $defId = $this->getDefinitionId($aliasId, $container)) {
$container->setAlias($id, $defId)->setPublic($alias->isPublic())->setPrivate($alias->isPrivate());
}
Expand Down Expand Up @@ -60,8 +61,15 @@ private function getDefinitionId(string $id, ContainerBuilder $container): strin
if (isset($seen[$id])) {
throw new ServiceCircularReferenceException($id, array_merge(array_keys($seen), [$id]));
}

$seen[$id] = true;
$id = (string) $container->getAlias($id);
$alias = $container->getAlias($id);

if ($alias->isDeprecated()) {
@trigger_error($alias->getDeprecationMessage($id), E_USER_DEPRECATED);
}

$id = (string) $alias;
}

return $id;
Expand Down
Expand Up @@ -579,12 +579,13 @@ private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_
}

if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) {
$aliasDefinition = $this->aliasDefinitions[$id];
if ($aliasDefinition->isDeprecated()) {
@trigger_error($aliasDefinition->getDeprecationMessage($id), E_USER_DEPRECATED);
$alias = $this->aliasDefinitions[$id];

if ($alias->isDeprecated()) {
@trigger_error($alias->getDeprecationMessage($id), E_USER_DEPRECATED);
}

return $this->doGet((string) $aliasDefinition, $invalidBehavior, $inlineServices, $isConstructorArgument);
return $this->doGet((string) $alias, $invalidBehavior, $inlineServices, $isConstructorArgument);
}

try {
Expand Down
47 changes: 47 additions & 0 deletions src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Expand Up @@ -218,6 +218,7 @@ public function dump(array $options = [])
$code =
$this->startClass($options['class'], $baseClass, $baseClassWithNamespace).
$this->addServices($services).
$this->addDeprecatedAliases().
$this->addDefaultParametersMethod()
;

Expand Down Expand Up @@ -1115,6 +1116,15 @@ private function addMethodMap(): string
}
}

$aliases = $this->container->getAliases();
foreach ($aliases as $alias => $id) {
if (!$id->isDeprecated()) {
continue;
}
$id = (string) $id;
$code .= ' '.$this->doExport($alias).' => '.$this->doExport($this->generateMethodName($alias)).",\n";
}

return $code ? " \$this->methodMap = [\n{$code} ];\n" : '';
}

Expand All @@ -1141,6 +1151,10 @@ private function addAliases(): string
$code = " \$this->aliases = [\n";
ksort($aliases);
foreach ($aliases as $alias => $id) {
if ($id->isDeprecated()) {
continue;
}

$id = (string) $id;
while (isset($aliases[$id])) {
$id = (string) $aliases[$id];
Expand All @@ -1151,6 +1165,39 @@ private function addAliases(): string
return $code." ];\n";
}

private function addDeprecatedAliases(): string
{
$code = '';
$aliases = $this->container->getAliases();
foreach ($aliases as $alias => $definition) {
if (!$definition->isDeprecated()) {
continue;
}
$public = $definition->isPublic() ? 'public' : 'private';
$id = (string) $definition;
$methodNameAlias = $this->generateMethodName($alias);
$idExported = $this->export($id);
$messageExported = $this->export($definition->getDeprecationMessage($alias));
$code = <<<EOF
/*{$this->docStar}
* Gets the $public '$alias' alias.
*
* @return object The "$id" service.
*/
protected function {$methodNameAlias}()
{
@trigger_error($messageExported, E_USER_DEPRECATED);
return \$this->get($idExported);
}
EOF;
}

return $code;
}

private function addInlineRequires(): string
{
if (!$this->hotPathTag || !$this->inlineRequires) {
Expand Down
Expand Up @@ -671,12 +671,11 @@ private function validateAlias(\DOMElement $alias, $file)
}
}

$allowedTags = array('deprecated');
foreach ($alias->childNodes as $child) {
if (!$child instanceof \DOMElement && self::NS !== $child->namespaceURI) {
continue;
}
if (!in_array($child->localName, $allowedTags, true)) {
if (!\in_array($child->localName, ['deprecated'], true)) {
throw new InvalidArgumentException(sprintf('Invalid child element "%s" defined for alias "%s" in "%s".', $child->localName, $alias->getAttribute('id'), $file));
}
}
Expand Down
Expand Up @@ -351,7 +351,6 @@ private function parseDefinition($id, $service, $file, array $defaults)
foreach ($service as $key => $value) {
if (!\in_array($key, ['alias', 'public', 'deprecated'])) {
throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias" and "public".', $key, $id, $file));
continue;
}

if ('deprecated' === $key) {
Expand Down
Expand Up @@ -83,6 +83,48 @@ public function testResolveFactory()
$this->assertSame('Factory', (string) $resolvedBarFactory[0]);
}

/**
* @group legacy
* @expectedDeprecation The "deprecated_foo_alias" service alias is deprecated. You should stop using it, as it will soon be removed.
*/
public function testDeprecationNoticeWhenReferencedByAlias()
{
$container = new ContainerBuilder();

$container->register('foo', 'stdClass');

$aliasDeprecated = new Alias('foo');
$aliasDeprecated->setDeprecated(true);
$container->setAlias('deprecated_foo_alias', $aliasDeprecated);

$alias = new Alias('deprecated_foo_alias');
$container->setAlias('alias', $alias);

$this->process($container);
}

/**
* @group legacy
* @expectedDeprecation The "foo_aliased" service alias is deprecated. You should stop using it, as it will soon be removed.
*/
public function testDeprecationNoticeWhenReferencedByDefinition()
{
$container = new ContainerBuilder();

$container->register('foo', 'stdClass');

$aliasDeprecated = new Alias('foo');
$aliasDeprecated->setDeprecated(true);
$container->setAlias('foo_aliased', $aliasDeprecated);

$container
->register('definition')
->setArguments([new Reference('foo_aliased')])
;

$this->process($container);
}

protected function process(ContainerBuilder $container)
{
$pass = new ResolveReferencesToAliasesPass();
Expand Down
Expand Up @@ -325,6 +325,24 @@ public function testAliases()
$this->assertSame($foo, $container->get('alias_for_alias'));
}

/**
* @group legacy
* @expectedDeprecation The "alias_for_foo_deprecated" service alias is deprecated. You should stop using it, as it will soon be removed.
*/
public function testAliasesDeprecation()
{
$container = include self::$fixturesPath.'/containers/container_alias_deprecation.php';
$container->compile();
$dumper = new PhpDumper($container);

$this->assertStringEqualsFile(self::$fixturesPath.'/php/container_alias_deprecation.php', $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Test_Aliases_Deprecation']));

require self::$fixturesPath.'/php/container_alias_deprecation.php';
$container = new \Symfony_DI_PhpDumper_Test_Aliases_Deprecation();
$container->get('alias_for_foo_non_deprecated');
$container->get('alias_for_foo_deprecated');
}

public function testFrozenContainerWithoutAliases()
{
$container = new ContainerBuilder();
Expand Down
@@ -0,0 +1,21 @@
<?php

use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();

$container
->register('foo', 'stdClass')
->setPublic(true)
;

$container
->setAlias('alias_for_foo_deprecated', 'foo')
->setDeprecated(true)
->setPublic(true);

$container
->setAlias('alias_for_foo_non_deprecated', 'foo')
->setPublic(true);

return $container;
@@ -0,0 +1,73 @@
<?php

use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;

/**
* This class has been auto-generated
* by the Symfony Dependency Injection Component.
*
* @final since Symfony 3.3
*/
class Symfony_DI_PhpDumper_Test_Aliases_Deprecation extends Container
{
private $parameters;
private $targetDirs = [];

public function __construct()
{
$this->services = $this->privates = [];
$this->methodMap = [
'foo' => 'getFooService',
'alias_for_foo_deprecated' => 'getAliasForFooDeprecatedService',
];
$this->aliases = [
'alias_for_foo_non_deprecated' => 'foo',
];
}

public function compile()
{
throw new LogicException('You cannot compile a dumped container that was already compiled.');
}

public function isCompiled()
{
return true;
}

public function getRemovedIds()
{
return [
'Psr\\Container\\ContainerInterface' => true,
'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
];
}

/**
* Gets the public 'foo' shared service.
*
* @return \stdClass
*/
protected function getFooService()
{
return $this->services['foo'] = new \stdClass();
}

/**
* Gets the public 'alias_for_foo_deprecated' alias.
*
* @return object The "foo" service.
*/
protected function getAliasForFooDeprecatedService()
{
@trigger_error('The "alias_for_foo_deprecated" service alias is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED);

return $this->get('foo');
}
}

0 comments on commit 6711e59

Please sign in to comment.