Added support for deprecating an alias

This commit is contained in:
Joost van Driel 2017-10-27 15:58:53 +02:00 committed by Renan
parent a9f8ca57af
commit 0eb071b9f8
10 changed files with 249 additions and 3 deletions

View File

@ -11,17 +11,24 @@
namespace Symfony\Component\DependencyInjection;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
class Alias
{
private $id;
private $public;
private $private;
private $deprecated;
private $deprecationTemplate;
private static $defaultDeprecationTemplate = 'The "%service_id%" service alias is deprecated. You should stop using it, as it will soon be removed.';
public function __construct(string $id, bool $public = true)
{
$this->id = $id;
$this->public = $public;
$this->private = 2 > \func_num_args();
$this->deprecated = false;
}
/**
@ -78,6 +85,58 @@ class Alias
return $this->private;
}
/**
* Whether this alias is deprecated, that means it should not be called
* anymore.
*
* @param bool $status Defaults to true
* @param string $template Optional template message to use if the alias is deprecated
*
* @return $this
*
* @throws InvalidArgumentException when the message template is invalid
*/
public function setDeprecated($status = true, $template = null)
{
if (null !== $template) {
if (preg_match('#[\r\n]|\*/#', $template)) {
throw new InvalidArgumentException('Invalid characters found in deprecation template.');
}
if (false === strpos($template, '%service_id%')) {
throw new InvalidArgumentException('The deprecation template must contain the "%service_id%" placeholder.');
}
$this->deprecationTemplate = $template;
}
$this->deprecated = (bool) $status;
return $this;
}
/**
* Returns whether this alias is deprecated.
*
* @return bool
*/
public function isDeprecated()
{
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)
{
return str_replace('%service_id%', $id, $this->deprecationTemplate ?: self::$defaultDeprecationTemplate);
}
/**
* Returns the Id of this alias.
*

View File

@ -579,7 +579,12 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
}
if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) {
return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices, $isConstructorArgument);
$aliasDefinition = $this->aliasDefinitions[$id];
if ($aliasDefinition->isDeprecated()) {
@trigger_error($aliasDefinition->getDeprecationMessage($id), E_USER_DEPRECATED);
}
return $this->doGet((string) $aliasDefinition, $invalidBehavior, $inlineServices, $isConstructorArgument);
}
try {

View File

@ -220,6 +220,10 @@ class XmlFileLoader extends FileLoader
$alias->setPublic($defaults['public']);
}
if ($deprecated = $this->getChildren($service, 'deprecated')) {
$alias->setDeprecated(true, $deprecated[0]->nodeValue ?: null);
}
return;
}
@ -667,8 +671,12 @@ EOF
}
}
$allowedTags = array('deprecated');
foreach ($alias->childNodes as $child) {
if ($child instanceof \DOMElement && self::NS === $child->namespaceURI) {
if (!$child instanceof \DOMElement && self::NS !== $child->namespaceURI) {
continue;
}
if (!in_array($child->localName, $allowedTags, true)) {
throw new InvalidArgumentException(sprintf('Invalid child element "%s" defined for alias "%s" in "%s".', $child->localName, $alias->getAttribute('id'), $file));
}
}

View File

@ -349,8 +349,13 @@ class YamlFileLoader extends FileLoader
}
foreach ($service as $key => $value) {
if (!\in_array($key, ['alias', 'public'])) {
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) {
$alias->setDeprecated(true, $value);
}
}

View File

@ -0,0 +1,110 @@
<?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\Component\DependencyInjection\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Alias;
class AliasTest extends TestCase
{
public function testConstructor()
{
$alias = new Alias('foo');
$this->assertEquals('foo', (string) $alias);
$this->assertTrue($alias->isPublic());
}
public function testCanConstructANonPublicAlias()
{
$alias = new Alias('foo', false);
$this->assertEquals('foo', (string) $alias);
$this->assertFalse($alias->isPublic());
}
public function testCanConstructAPrivateAlias()
{
$alias = new Alias('foo', false, false);
$this->assertEquals('foo', (string) $alias);
$this->assertFalse($alias->isPublic());
$this->assertFalse($alias->isPrivate());
}
public function testCanSetPublic()
{
$alias = new Alias('foo', false);
$alias->setPublic(true);
$this->assertTrue($alias->isPublic());
}
public function testCanDeprecateAnAlias()
{
$alias = new Alias('foo', false);
$alias->setDeprecated(true, 'The %service_id% service is deprecated.');
$this->assertTrue($alias->isDeprecated());
}
public function testItHasADefaultDeprecationMessage()
{
$alias = new Alias('foo', false);
$alias->setDeprecated();
$expectedMessage = 'The "foo" service alias is deprecated. You should stop using it, as it will soon be removed.';
$this->assertEquals($expectedMessage, $alias->getDeprecationMessage('foo'));
}
public function testReturnsCorrectDeprecationMessage()
{
$alias = new Alias('foo', false);
$alias->setDeprecated(true, 'The "%service_id%" is deprecated.');
$expectedMessage = 'The "foo" is deprecated.';
$this->assertEquals($expectedMessage, $alias->getDeprecationMessage('foo'));
}
public function testCanOverrideDeprecation()
{
$alias = new Alias('foo', false);
$alias->setDeprecated();
$initial = $alias->isDeprecated();
$alias->setDeprecated(false);
$final = $alias->isDeprecated();
$this->assertTrue($initial);
$this->assertFalse($final);
}
/**
* @dataProvider invalidDeprecationMessageProvider
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
*/
public function testCannotDeprecateWithAnInvalidTemplate($message)
{
$def = new Alias('foo');
$def->setDeprecated(true, $message);
}
public function invalidDeprecationMessageProvider()
{
return array(
"With \rs" => array("invalid \r message %service_id%"),
"With \ns" => array("invalid \n message %service_id%"),
'With */s' => array('invalid */ message %service_id%'),
'message not containing required %service_id% variable' => array('this is deprecated'),
);
}
}

View File

@ -259,6 +259,22 @@ class ContainerBuilderTest extends TestCase
}
}
/**
* @group legacy
* @expectedDeprecation The "foobar" service alias is deprecated. You should stop using it, as it will soon be removed.
*/
public function testDeprecatedAlias()
{
$builder = new ContainerBuilder();
$builder->register('foo', 'stdClass');
$alias = new Alias('foo');
$alias->setDeprecated();
$builder->setAlias('foobar', $alias);
$builder->get('foobar');
}
public function testGetAliases()
{
$builder = new ContainerBuilder();

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<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="foo" class="Foo">
</service>
<service id="alias_for_foo" alias="foo">
<deprecated />
</service>
<service id="alias_for_foobar" alias="foobar">
<deprecated>The "%service_id%" service alias is deprecated.</deprecated>
</service>
</services>
</container>

View File

@ -0,0 +1,4 @@
services:
alias_for_foobar:
alias: foobar
deprecated: The "%service_id%" service alias is deprecated.

View File

@ -351,6 +351,21 @@ class XmlFileLoaderTest extends TestCase
$this->assertSame($message, $container->getDefinition('bar')->getDeprecationMessage('bar'));
}
public function testDeprecatedAliases()
{
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('deprecated_alias_definitions.xml');
$this->assertTrue($container->getAlias('alias_for_foo')->isDeprecated());
$message = 'The "alias_for_foo" service alias is deprecated. You should stop using it, as it will soon be removed.';
$this->assertSame($message, $container->getAlias('alias_for_foo')->getDeprecationMessage('alias_for_foo'));
$this->assertTrue($container->getAlias('alias_for_foobar')->isDeprecated());
$message = 'The "alias_for_foobar" service alias is deprecated.';
$this->assertSame($message, $container->getAlias('alias_for_foobar')->getDeprecationMessage('alias_for_foobar'));
}
public function testConvertDomElementToArray()
{
$doc = new \DOMDocument('1.0');

View File

@ -175,6 +175,17 @@ class YamlFileLoaderTest extends TestCase
$this->assertEquals(['decorated', 'decorated.pif-pouf', 5], $services['decorator_service_with_name_and_priority']->getDecoratedService());
}
public function testDeprecatedAliases()
{
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('deprecated_alias_definitions.yml');
$this->assertTrue($container->getAlias('alias_for_foobar')->isDeprecated());
$message = 'The "alias_for_foobar" service alias is deprecated.';
$this->assertSame($message, $container->getAlias('alias_for_foobar')->getDeprecationMessage('alias_for_foobar'));
}
public function testLoadFactoryShortSyntax()
{
$container = new ContainerBuilder();