Skip to content

Commit

Permalink
[DI] Deprecate case insentivity of service identifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Jan 10, 2017
1 parent 16d33e1 commit 99083df
Show file tree
Hide file tree
Showing 16 changed files with 254 additions and 73 deletions.
4 changes: 4 additions & 0 deletions UPGRADE-3.3.md
Expand Up @@ -10,6 +10,10 @@ ClassLoader
DependencyInjection
-------------------

* The `Reference` and `Alias` classes do not make service identifiers lowercase anymore.

* Case insensitivity of service identifiers is deprecated and will be removed in 4.0.

* Using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and
will not be supported anymore in 4.0.

Expand Down
7 changes: 6 additions & 1 deletion src/Symfony/Component/DependencyInjection/Alias.php
Expand Up @@ -22,7 +22,12 @@ class Alias
*/
public function __construct($id, $public = true)
{
$this->id = strtolower($id);
if (!is_string($id)) {
$type = is_object($id) ? get_class($id) : gettype($id);
$id = (string) $id;
@trigger_error(sprintf('Non-string identifiers are deprecated since Symfony 3.3 and won\'t be supported in 4.0 for Alias to "%s" ("%s" given.) Cast it to string beforehand.', $id, $type), E_USER_DEPRECATED);
}
$this->id = $id;
$this->public = $public;
}

Expand Down
4 changes: 3 additions & 1 deletion src/Symfony/Component/DependencyInjection/CHANGELOG.md
Expand Up @@ -4,10 +4,12 @@ CHANGELOG
3.3.0
-----

* deprecated case insensitivity of service identifiers
* added "iterator" argument type for lazy iteration over a set of values and services
* added "closure-proxy" argument type for turning services' methods into lazy callables
* added file-wide configurable defaults for service attributes "public", "tags",
"autowire" and a new "inherit-tags"
"autowire" and "inherit-tags"
* added "inherit-tags" service attribute to control tags' inheritance from parent context
* made the "class" attribute optional, using the "id" as fallback
* using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and
will not be supported anymore in 4.0
Expand Down
Expand Up @@ -51,12 +51,13 @@ public function process(ContainerBuilder $container)
private function updateDefinition(ContainerBuilder $container, $id, Definition $definition, array $resolveClassPassChanges, array $previous = array())
{
// circular reference
if (isset($previous[$id])) {
$lcId = strtolower($id);
if (isset($previous[$lcId])) {
return;
}

$factory = $definition->getFactory();
if (null === $factory || (!isset($resolveClassPassChanges[$id]) && null !== $definition->getClass())) {
if (null === $factory || (!isset($resolveClassPassChanges[$lcId]) && null !== $definition->getClass())) {
return;
}

Expand All @@ -69,9 +70,9 @@ private function updateDefinition(ContainerBuilder $container, $id, Definition $
}
} else {
if ($factory[0] instanceof Reference) {
$previous[$id] = true;
$previous[$lcId] = true;
$factoryDefinition = $container->findDefinition((string) $factory[0]);
$this->updateDefinition($container, strtolower($factory[0]), $factoryDefinition, $resolveClassPassChanges, $previous);
$this->updateDefinition($container, $factory[0], $factoryDefinition, $resolveClassPassChanges, $previous);
$class = $factoryDefinition->getClass();
} else {
$class = $factory[0];
Expand All @@ -96,7 +97,7 @@ private function updateDefinition(ContainerBuilder $container, $id, Definition $
}
}

if (null !== $returnType && (!isset($resolveClassPassChanges[$id]) || $returnType !== $resolveClassPassChanges[$id])) {
if (null !== $returnType && (!isset($resolveClassPassChanges[$lcId]) || $returnType !== $resolveClassPassChanges[$lcId])) {
@trigger_error(sprintf('Relying on its factory\'s return-type to define the class of service "%s" is deprecated since Symfony 3.3 and won\'t work in 4.0. Set the "class" attribute to "%s" on the service definition instead.', $id, $returnType), E_USER_DEPRECATED);
}
$definition->setClass($returnType);
Expand Down
Expand Up @@ -31,8 +31,8 @@ public function process(ContainerBuilder $container)
continue;
}
if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $id)) {
$this->changes[$id] = $container->getCaseSensitiveId($id);
$definition->setClass($this->changes[$id]);
$this->changes[strtolower($id)] = $id;
$definition->setClass($id);
}
}
}
Expand Down
47 changes: 40 additions & 7 deletions src/Symfony/Component/DependencyInjection/Container.php
Expand Up @@ -70,6 +70,11 @@ class Container implements ResettableContainerInterface
protected $aliases = array();
protected $loading = array();

/**
* @internal
*/
protected $normalizedIds = array();

private $underscoreMap = array('_' => '', '.' => '_', '\\' => '_');
private $envCache = array();

Expand Down Expand Up @@ -164,7 +169,7 @@ public function setParameter($name, $value)
*/
public function set($id, $service)
{
$id = strtolower($id);
$id = $this->normalizeId($id);

if ('service_container' === $id) {
throw new InvalidArgumentException('You cannot set service "service_container".');
Expand Down Expand Up @@ -215,8 +220,8 @@ public function has($id)
return true;
}

if (--$i && $id !== $lcId = strtolower($id)) {
$id = $lcId;
if (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
$id = $normalizedId;
continue;
}

Expand Down Expand Up @@ -254,7 +259,7 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE
// Attempt to retrieve the service by checking first aliases then
// available services. Service IDs are case insensitive, however since
// this method can be called thousands of times during a request, avoid
// calling strtolower() unless necessary.
// calling $this->normalizeId($id) unless necessary.
for ($i = 2;;) {
if ('service_container' === $id) {
return $this;
Expand All @@ -273,8 +278,8 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE

if (isset($this->methodMap[$id])) {
$method = $this->methodMap[$id];
} elseif (--$i && $id !== $lcId = strtolower($id)) {
$id = $lcId;
} elseif (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
$id = $normalizedId;
continue;
} elseif (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) {
// We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
Expand Down Expand Up @@ -329,7 +334,7 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE
*/
public function initialized($id)
{
$id = strtolower($id);
$id = $this->normalizeId($id);

if ('service_container' === $id) {
return false;
Expand Down Expand Up @@ -426,6 +431,34 @@ protected function getEnv($name)
return $this->envCache[$name] = $this->getParameter("env($name)");
}

/**
* Returns the case sensitive id used at registration time.
*
* @param string $id
*
* @return string
*
* @internal
*/
public function normalizeId($id)
{
if (!is_string($id)) {
$type = is_object($id) ? get_class($id) : gettype($id);
$id = (string) $id;
@trigger_error(sprintf('Non-string service identifiers are deprecated since Symfony 3.3 and won\'t be supported in 4.0 for service "%s" ("%s" given.) Cast it to string beforehand.', $id, $type), E_USER_DEPRECATED);
}
if (isset($this->normalizedIds[$normalizedId = strtolower($id)])) {
$normalizedId = $this->normalizedIds[$normalizedId];
if ($id !== $normalizedId) {
@trigger_error(sprintf('Service identifiers will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since version 3.3.', $id, $normalizedId), E_USER_DEPRECATED);
}
} else {
$normalizedId = $this->normalizedIds[$normalizedId] = $id;
}

return $normalizedId;
}

private function __clone()
{
}
Expand Down
77 changes: 30 additions & 47 deletions src/Symfony/Component/DependencyInjection/ContainerBuilder.php
Expand Up @@ -103,11 +103,6 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
private $envCounters = array();

/**
* @var array a map of case less to case sensitive ids
*/
private $caseSensitiveIds = array();

/**
* Sets the track resources flag.
*
Expand Down Expand Up @@ -372,18 +367,14 @@ public function getCompiler()
*/
public function set($id, $service)
{
$caseSensitiveId = $id;
$id = strtolower($caseSensitiveId);
$id = $this->normalizeId($id);

if ($this->isFrozen() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
// setting a synthetic service on a frozen container is alright
throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a frozen container is not allowed.', $id));
}

unset($this->definitions[$id], $this->aliasDefinitions[$id]);
if ($id !== $caseSensitiveId) {
$this->caseSensitiveIds[$id] = $caseSensitiveId;
}

parent::set($id, $service);
}
Expand All @@ -395,7 +386,7 @@ public function set($id, $service)
*/
public function removeDefinition($id)
{
unset($this->definitions[strtolower($id)]);
unset($this->definitions[$this->normalizeId($id)]);
}

/**
Expand All @@ -407,7 +398,7 @@ public function removeDefinition($id)
*/
public function has($id)
{
$id = strtolower($id);
$id = $this->normalizeId($id);

return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id);
}
Expand All @@ -429,7 +420,7 @@ public function has($id)
*/
public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
{
$id = strtolower($id);
$id = $this->normalizeId($id);

if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
return $service;
Expand Down Expand Up @@ -637,11 +628,10 @@ public function setAliases(array $aliases)
*/
public function setAlias($alias, $id)
{
$caseSensitiveAlias = $alias;
$alias = strtolower($caseSensitiveAlias);
$alias = $this->normalizeId($alias);

if (is_string($id)) {
$id = new Alias($id);
$id = new Alias($this->normalizeId($id));
} elseif (!$id instanceof Alias) {
throw new InvalidArgumentException('$id must be a string, or an Alias object.');
}
Expand All @@ -651,9 +641,6 @@ public function setAlias($alias, $id)
}

unset($this->definitions[$alias]);
if ($alias !== $caseSensitiveAlias) {
$this->caseSensitiveIds[$alias] = $caseSensitiveAlias;
}

$this->aliasDefinitions[$alias] = $id;
}
Expand All @@ -665,7 +652,7 @@ public function setAlias($alias, $id)
*/
public function removeAlias($alias)
{
unset($this->aliasDefinitions[strtolower($alias)]);
unset($this->aliasDefinitions[$this->normalizeId($alias)]);
}

/**
Expand All @@ -677,7 +664,7 @@ public function removeAlias($alias)
*/
public function hasAlias($id)
{
return isset($this->aliasDefinitions[strtolower($id)]);
return isset($this->aliasDefinitions[$this->normalizeId($id)]);
}

/**
Expand All @@ -701,7 +688,7 @@ public function getAliases()
*/
public function getAlias($id)
{
$id = strtolower($id);
$id = $this->normalizeId($id);

if (!isset($this->aliasDefinitions[$id])) {
throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id));
Expand Down Expand Up @@ -791,13 +778,9 @@ public function setDefinition($id, Definition $definition)
throw new BadMethodCallException('Adding definition to a frozen container is not allowed');
}

$caseSensitiveId = $id;
$id = strtolower($caseSensitiveId);
$id = $this->normalizeId($id);

unset($this->aliasDefinitions[$id]);
if ($id !== $caseSensitiveId) {
$this->caseSensitiveIds[$id] = $caseSensitiveId;
}

return $this->definitions[$id] = $definition;
}
Expand All @@ -811,7 +794,7 @@ public function setDefinition($id, Definition $definition)
*/
public function hasDefinition($id)
{
return isset($this->definitions[strtolower($id)]);
return isset($this->definitions[$this->normalizeId($id)]);
}

/**
Expand All @@ -825,7 +808,7 @@ public function hasDefinition($id)
*/
public function getDefinition($id)
{
$id = strtolower($id);
$id = $this->normalizeId($id);

if (!isset($this->definitions[$id])) {
throw new ServiceNotFoundException($id);
Expand All @@ -847,7 +830,7 @@ public function getDefinition($id)
*/
public function findDefinition($id)
{
$id = strtolower($id);
$id = $this->normalizeId($id);

while (isset($this->aliasDefinitions[$id])) {
$id = (string) $this->aliasDefinitions[$id];
Expand All @@ -856,22 +839,6 @@ public function findDefinition($id)
return $this->getDefinition($id);
}

/**
* Returns the case sensitive id used at registration time.
*
* @param string $id
*
* @return string
*
* @internal
*/
public function getCaseSensitiveId($id)
{
$id = strtolower($id);

return isset($this->caseSensitiveIds[$id]) ? $this->caseSensitiveIds[$id] : $id;
}

/**
* Creates a service for a service definition.
*
Expand Down Expand Up @@ -1188,6 +1155,22 @@ public function getEnvCounters()
return $this->envCounters;
}

/**
* @internal
*/
public function getNormalizedIds()
{
$normalizedIds = array();

foreach ($this->normalizedIds as $k => $v) {
if ($v !== (string) $k) {
$normalizedIds[$k] = $v;
}
}

return $normalizedIds;
}

/**
* Returns the Service Conditionals.
*
Expand Down Expand Up @@ -1247,7 +1230,7 @@ private function callMethod($service, $call)
private function shareService(Definition $definition, $service, $id)
{
if (null !== $id && $definition->isShared()) {
$this->services[strtolower($id)] = $service;
$this->services[$this->normalizeId($id)] = $service;
}
}

Expand Down

0 comments on commit 99083df

Please sign in to comment.