Move into nested docroot

This commit is contained in:
Rob Davies 2017-02-13 15:31:17 +00:00
parent 83a0d3a149
commit c8b70abde9
13405 changed files with 0 additions and 0 deletions
web/vendor/symfony/dependency-injection
.gitignoreAlias.phpCHANGELOG.md
Compiler
Container.phpContainerAware.phpContainerAwareInterface.phpContainerAwareTrait.phpContainerBuilder.phpContainerInterface.phpDefinition.phpDefinitionDecorator.php
Dumper
Exception
ExpressionLanguage.phpExpressionLanguageProvider.php
Extension
IntrospectableContainerInterface.phpLICENSE
LazyProxy
Loader
Parameter.php
ParameterBag
README.mdReference.phpResettableContainerInterface.phpScope.phpScopeInterface.phpSimpleXMLElement.phpTaggedContainerInterface.phpVariable.phpcomposer.jsonphpunit.xml.dist

View file

@ -0,0 +1,3 @@
vendor/
composer.lock
phpunit.xml

View file

@ -0,0 +1,58 @@
<?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;
class Alias
{
private $id;
private $public;
/**
* @param string $id Alias identifier
* @param bool $public If this alias is public
*/
public function __construct($id, $public = true)
{
$this->id = strtolower($id);
$this->public = $public;
}
/**
* Checks if this DI Alias should be public or not.
*
* @return bool
*/
public function isPublic()
{
return $this->public;
}
/**
* Sets if this Alias is public.
*
* @param bool $boolean If this Alias should be public
*/
public function setPublic($boolean)
{
$this->public = (bool) $boolean;
}
/**
* Returns the Id of this alias.
*
* @return string The alias id
*/
public function __toString()
{
return $this->id;
}
}

View file

@ -0,0 +1,57 @@
CHANGELOG
=========
2.8.0
-----
* deprecated the abstract ContainerAware class in favor of ContainerAwareTrait
* deprecated IntrospectableContainerInterface, to be merged with ContainerInterface in 3.0
* allowed specifying a directory to recursively load all configuration files it contains
* deprecated the concept of scopes
* added `Definition::setShared()` and `Definition::isShared()`
* added ResettableContainerInterface to be able to reset the container to release memory on shutdown
* added a way to define the priority of service decoration
* added support for service autowiring
2.7.0
-----
* deprecated synchronized services
2.6.0
-----
* added new factory syntax and deprecated the old one
2.5.0
-----
* added DecoratorServicePass and a way to override a service definition (Definition::setDecoratedService())
* deprecated SimpleXMLElement class.
2.4.0
-----
* added support for expressions in service definitions
* added ContainerAwareTrait to add default container aware behavior to a class
2.2.0
-----
* added Extension::isConfigEnabled() to ease working with enableable configurations
* added an Extension base class with sensible defaults to be used in conjunction
with the Config component.
* added PrependExtensionInterface (to be able to allow extensions to prepend
application configuration settings for any Bundle)
2.1.0
-----
* added IntrospectableContainerInterface (to be able to check if a service
has been initialized or not)
* added ConfigurationExtensionInterface
* added Definition::clearTag()
* component exceptions that inherit base SPL classes are now used exclusively
(this includes dumped containers)
* [BC BREAK] fixed unescaping of class arguments, method
ParameterBag::unescapeValue() was made public

View file

@ -0,0 +1,152 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Run this pass before passes that need to know more about the relation of
* your services.
*
* This class will populate the ServiceReferenceGraph with information. You can
* retrieve the graph in other passes from the compiler.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AnalyzeServiceReferencesPass implements RepeatablePassInterface
{
private $graph;
private $container;
private $currentId;
private $currentDefinition;
private $repeatedPass;
private $onlyConstructorArguments;
/**
* @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls
*/
public function __construct($onlyConstructorArguments = false)
{
$this->onlyConstructorArguments = (bool) $onlyConstructorArguments;
}
/**
* {@inheritdoc}
*/
public function setRepeatedPass(RepeatedPass $repeatedPass)
{
$this->repeatedPass = $repeatedPass;
}
/**
* Processes a ContainerBuilder object to populate the service reference graph.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$this->container = $container;
$this->graph = $container->getCompiler()->getServiceReferenceGraph();
$this->graph->clear();
foreach ($container->getDefinitions() as $id => $definition) {
if ($definition->isSynthetic() || $definition->isAbstract()) {
continue;
}
$this->currentId = $id;
$this->currentDefinition = $definition;
$this->processArguments($definition->getArguments());
if ($definition->getFactoryService(false)) {
$this->processArguments(array(new Reference($definition->getFactoryService(false))));
}
if (is_array($definition->getFactory())) {
$this->processArguments($definition->getFactory());
}
if (!$this->onlyConstructorArguments) {
$this->processArguments($definition->getMethodCalls());
$this->processArguments($definition->getProperties());
if ($definition->getConfigurator()) {
$this->processArguments(array($definition->getConfigurator()));
}
}
}
foreach ($container->getAliases() as $id => $alias) {
$this->graph->connect($id, $alias, (string) $alias, $this->getDefinition((string) $alias), null);
}
}
/**
* Processes service definitions for arguments to find relationships for the service graph.
*
* @param array $arguments An array of Reference or Definition objects relating to service definitions
*/
private function processArguments(array $arguments)
{
foreach ($arguments as $argument) {
if (is_array($argument)) {
$this->processArguments($argument);
} elseif ($argument instanceof Reference) {
$this->graph->connect(
$this->currentId,
$this->currentDefinition,
$this->getDefinitionId((string) $argument),
$this->getDefinition((string) $argument),
$argument
);
} elseif ($argument instanceof Definition) {
$this->processArguments($argument->getArguments());
$this->processArguments($argument->getMethodCalls());
$this->processArguments($argument->getProperties());
if (is_array($argument->getFactory())) {
$this->processArguments($argument->getFactory());
}
if ($argument->getFactoryService(false)) {
$this->processArguments(array(new Reference($argument->getFactoryService(false))));
}
}
}
}
/**
* Returns a service definition given the full name or an alias.
*
* @param string $id A full id or alias for a service definition
*
* @return Definition|null The definition related to the supplied id
*/
private function getDefinition($id)
{
$id = $this->getDefinitionId($id);
return null === $id ? null : $this->container->getDefinition($id);
}
private function getDefinitionId($id)
{
while ($this->container->hasAlias($id)) {
$id = (string) $this->container->getAlias($id);
}
if (!$this->container->hasDefinition($id)) {
return;
}
return $id;
}
}

View file

@ -0,0 +1,41 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
/**
* Sets a service to be an alias of another one, given a format pattern.
*/
class AutoAliasServicePass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
foreach ($container->findTaggedServiceIds('auto_alias') as $serviceId => $tags) {
foreach ($tags as $tag) {
if (!isset($tag['format'])) {
throw new InvalidArgumentException(sprintf('Missing tag information "format" on auto_alias service "%s".', $serviceId));
}
$aliasId = $container->getParameterBag()->resolveValue($tag['format']);
if ($container->hasDefinition($aliasId) || $container->hasAlias($aliasId)) {
$container->setAlias($serviceId, new Alias($aliasId));
}
}
}
}
}

View file

@ -0,0 +1,286 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Reference;
/**
* Guesses constructor arguments of services definitions and try to instantiate services if necessary.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class AutowirePass implements CompilerPassInterface
{
private $container;
private $reflectionClasses = array();
private $definedTypes = array();
private $types;
private $notGuessableTypes = array();
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
$throwingAutoloader = function ($class) { throw new \ReflectionException(sprintf('Class %s does not exist', $class)); };
spl_autoload_register($throwingAutoloader);
try {
$this->container = $container;
foreach ($container->getDefinitions() as $id => $definition) {
if ($definition->isAutowired()) {
$this->completeDefinition($id, $definition);
}
}
} catch (\Exception $e) {
} catch (\Throwable $e) {
}
spl_autoload_unregister($throwingAutoloader);
// Free memory and remove circular reference to container
$this->container = null;
$this->reflectionClasses = array();
$this->definedTypes = array();
$this->types = null;
$this->notGuessableTypes = array();
if (isset($e)) {
throw $e;
}
}
/**
* Wires the given definition.
*
* @param string $id
* @param Definition $definition
*
* @throws RuntimeException
*/
private function completeDefinition($id, Definition $definition)
{
if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
return;
}
$this->container->addClassResource($reflectionClass);
if (!$constructor = $reflectionClass->getConstructor()) {
return;
}
$arguments = $definition->getArguments();
foreach ($constructor->getParameters() as $index => $parameter) {
if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
continue;
}
try {
if (!$typeHint = $parameter->getClass()) {
// no default value? Then fail
if (!$parameter->isOptional()) {
throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id));
}
// specifically pass the default value
$arguments[$index] = $parameter->getDefaultValue();
continue;
}
if (null === $this->types) {
$this->populateAvailableTypes();
}
if (isset($this->types[$typeHint->name]) && !isset($this->notGuessableTypes[$typeHint->name])) {
$value = new Reference($this->types[$typeHint->name]);
} else {
try {
$value = $this->createAutowiredDefinition($typeHint, $id);
} catch (RuntimeException $e) {
if ($parameter->allowsNull()) {
$value = null;
} elseif ($parameter->isDefaultValueAvailable()) {
$value = $parameter->getDefaultValue();
} else {
throw $e;
}
}
}
} catch (\ReflectionException $e) {
// Typehint against a non-existing class
if (!$parameter->isDefaultValueAvailable()) {
throw new RuntimeException(sprintf('Cannot autowire argument %s for %s because the type-hinted class does not exist (%s).', $index + 1, $definition->getClass(), $e->getMessage()), 0, $e);
}
$value = $parameter->getDefaultValue();
}
$arguments[$index] = $value;
}
// it's possible index 1 was set, then index 0, then 2, etc
// make sure that we re-order so they're injected as expected
ksort($arguments);
$definition->setArguments($arguments);
}
/**
* Populates the list of available types.
*/
private function populateAvailableTypes()
{
$this->types = array();
foreach ($this->container->getDefinitions() as $id => $definition) {
$this->populateAvailableType($id, $definition);
}
}
/**
* Populates the list of available types for a given definition.
*
* @param string $id
* @param Definition $definition
*/
private function populateAvailableType($id, Definition $definition)
{
// Never use abstract services
if ($definition->isAbstract()) {
return;
}
foreach ($definition->getAutowiringTypes() as $type) {
$this->definedTypes[$type] = true;
$this->types[$type] = $id;
}
if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
return;
}
foreach ($reflectionClass->getInterfaces() as $reflectionInterface) {
$this->set($reflectionInterface->name, $id);
}
do {
$this->set($reflectionClass->name, $id);
} while ($reflectionClass = $reflectionClass->getParentClass());
}
/**
* Associates a type and a service id if applicable.
*
* @param string $type
* @param string $id
*/
private function set($type, $id)
{
if (isset($this->definedTypes[$type])) {
return;
}
if (!isset($this->types[$type])) {
$this->types[$type] = $id;
return;
}
if ($this->types[$type] === $id) {
return;
}
if (!isset($this->notGuessableTypes[$type])) {
$this->notGuessableTypes[$type] = true;
$this->types[$type] = (array) $this->types[$type];
}
$this->types[$type][] = $id;
}
/**
* Registers a definition for the type if possible or throws an exception.
*
* @param \ReflectionClass $typeHint
* @param string $id
*
* @return Reference A reference to the registered definition
*
* @throws RuntimeException
*/
private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
{
if (isset($this->notGuessableTypes[$typeHint->name])) {
$classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
$matchingServices = implode(', ', $this->types[$typeHint->name]);
throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices));
}
if (!$typeHint->isInstantiable()) {
$classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface));
}
$argumentId = sprintf('autowired.%s', $typeHint->name);
$argumentDefinition = $this->container->register($argumentId, $typeHint->name);
$argumentDefinition->setPublic(false);
$this->populateAvailableType($argumentId, $argumentDefinition);
try {
$this->completeDefinition($argumentId, $argumentDefinition);
} catch (RuntimeException $e) {
$classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
$message = sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface);
throw new RuntimeException($message, 0, $e);
}
return new Reference($argumentId);
}
/**
* Retrieves the reflection class associated with the given service.
*
* @param string $id
* @param Definition $definition
*
* @return \ReflectionClass|false
*/
private function getReflectionClass($id, Definition $definition)
{
if (isset($this->reflectionClasses[$id])) {
return $this->reflectionClasses[$id];
}
// Cannot use reflection if the class isn't set
if (!$class = $definition->getClass()) {
return false;
}
$class = $this->container->getParameterBag()->resolveValue($class);
try {
$reflector = new \ReflectionClass($class);
} catch (\ReflectionException $e) {
$reflector = false;
}
return $this->reflectionClasses[$id] = $reflector;
}
}

View file

@ -0,0 +1,81 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Checks your services for circular references.
*
* References from method calls are ignored since we might be able to resolve
* these references depending on the order in which services are called.
*
* Circular reference from method calls will only be detected at run-time.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class CheckCircularReferencesPass implements CompilerPassInterface
{
private $currentPath;
private $checkedNodes;
/**
* Checks the ContainerBuilder object for circular references.
*
* @param ContainerBuilder $container The ContainerBuilder instances
*/
public function process(ContainerBuilder $container)
{
$graph = $container->getCompiler()->getServiceReferenceGraph();
$this->checkedNodes = array();
foreach ($graph->getNodes() as $id => $node) {
$this->currentPath = array($id);
$this->checkOutEdges($node->getOutEdges());
}
}
/**
* Checks for circular references.
*
* @param ServiceReferenceGraphEdge[] $edges An array of Edges
*
* @throws ServiceCircularReferenceException When a circular reference is found.
*/
private function checkOutEdges(array $edges)
{
foreach ($edges as $edge) {
$node = $edge->getDestNode();
$id = $node->getId();
if (empty($this->checkedNodes[$id])) {
// don't check circular dependencies for lazy services
if (!$node->getValue() || !$node->getValue()->isLazy()) {
$searchKey = array_search($id, $this->currentPath);
$this->currentPath[] = $id;
if (false !== $searchKey) {
throw new ServiceCircularReferenceException($id, array_slice($this->currentPath, $searchKey));
}
$this->checkOutEdges($node->getOutEdges());
}
$this->checkedNodes[$id] = true;
array_pop($this->currentPath);
}
}
}
}

View file

@ -0,0 +1,90 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/**
* This pass validates each definition individually only taking the information
* into account which is contained in the definition itself.
*
* Later passes can rely on the following, and specifically do not need to
* perform these checks themselves:
*
* - non synthetic, non abstract services always have a class set
* - synthetic services are always public
* - synthetic services are always of non-prototype scope
* - shared services are always of non-prototype scope
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class CheckDefinitionValidityPass implements CompilerPassInterface
{
/**
* Processes the ContainerBuilder to validate the Definition.
*
* @param ContainerBuilder $container
*
* @throws RuntimeException When the Definition is invalid
*/
public function process(ContainerBuilder $container)
{
foreach ($container->getDefinitions() as $id => $definition) {
// synthetic service is public
if ($definition->isSynthetic() && !$definition->isPublic()) {
throw new RuntimeException(sprintf('A synthetic service ("%s") must be public.', $id));
}
// synthetic service has non-prototype scope
if ($definition->isSynthetic() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) {
throw new RuntimeException(sprintf('A synthetic service ("%s") cannot be of scope "prototype".', $id));
}
// shared service has non-prototype scope
if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) {
throw new RuntimeException(sprintf('A shared service ("%s") cannot be of scope "prototype".', $id));
}
if ($definition->getFactory() && ($definition->getFactoryClass(false) || $definition->getFactoryService(false) || $definition->getFactoryMethod(false))) {
throw new RuntimeException(sprintf('A service ("%s") can use either the old or the new factory syntax, not both.', $id));
}
// non-synthetic, non-abstract service has class
if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) {
if ($definition->getFactory() || $definition->getFactoryClass(false) || $definition->getFactoryService(false)) {
throw new RuntimeException(sprintf('Please add the class to service "%s" even if it is constructed by a factory since we might need to add method calls based on compile-time checks.', $id));
}
throw new RuntimeException(sprintf(
'The definition for "%s" has no class. If you intend to inject '
.'this service dynamically at runtime, please mark it as synthetic=true. '
.'If this is an abstract definition solely used by child definitions, '
.'please add abstract=true, otherwise specify a class to get rid of this error.',
$id
));
}
// tag attribute values must be scalars
foreach ($definition->getTags() as $name => $tags) {
foreach ($tags as $attributes) {
foreach ($attributes as $attribute => $value) {
if (!is_scalar($value) && null !== $value) {
throw new RuntimeException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $id, $name, $attribute));
}
}
}
}
}
}
}

View file

@ -0,0 +1,63 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Checks that all references are pointing to a valid service.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class CheckExceptionOnInvalidReferenceBehaviorPass implements CompilerPassInterface
{
private $container;
private $sourceId;
public function process(ContainerBuilder $container)
{
$this->container = $container;
foreach ($container->getDefinitions() as $id => $definition) {
$this->sourceId = $id;
$this->processDefinition($definition);
}
}
private function processDefinition(Definition $definition)
{
$this->processReferences($definition->getArguments());
$this->processReferences($definition->getMethodCalls());
$this->processReferences($definition->getProperties());
}
private function processReferences(array $arguments)
{
foreach ($arguments as $argument) {
if (is_array($argument)) {
$this->processReferences($argument);
} elseif ($argument instanceof Definition) {
$this->processDefinition($argument);
} elseif ($argument instanceof Reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $argument->getInvalidBehavior()) {
$destId = (string) $argument;
if (!$this->container->has($destId)) {
throw new ServiceNotFoundException($destId, $this->sourceId);
}
}
}
}
}

View file

@ -0,0 +1,165 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Exception\ScopeCrossingInjectionException;
use Symfony\Component\DependencyInjection\Exception\ScopeWideningInjectionException;
/**
* Checks the validity of references.
*
* The following checks are performed by this pass:
* - target definitions are not abstract
* - target definitions are of equal or wider scope
* - target definitions are in the same scope hierarchy
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class CheckReferenceValidityPass implements CompilerPassInterface
{
private $container;
private $currentId;
private $currentScope;
private $currentScopeAncestors;
private $currentScopeChildren;
/**
* Processes the ContainerBuilder to validate References.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$this->container = $container;
$children = $this->container->getScopeChildren(false);
$ancestors = array();
$scopes = $this->container->getScopes(false);
foreach ($scopes as $name => $parent) {
$ancestors[$name] = array($parent);
while (isset($scopes[$parent])) {
$ancestors[$name][] = $parent = $scopes[$parent];
}
}
foreach ($container->getDefinitions() as $id => $definition) {
if ($definition->isSynthetic() || $definition->isAbstract()) {
continue;
}
$this->currentId = $id;
$this->currentScope = $scope = $definition->getScope(false);
if (ContainerInterface::SCOPE_CONTAINER === $scope) {
$this->currentScopeChildren = array_keys($scopes);
$this->currentScopeAncestors = array();
} elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
$this->currentScopeChildren = isset($children[$scope]) ? $children[$scope] : array();
$this->currentScopeAncestors = isset($ancestors[$scope]) ? $ancestors[$scope] : array();
}
$this->validateReferences($definition->getArguments());
$this->validateReferences($definition->getMethodCalls());
$this->validateReferences($definition->getProperties());
}
}
/**
* Validates an array of References.
*
* @param array $arguments An array of Reference objects
*
* @throws RuntimeException when there is a reference to an abstract definition.
*/
private function validateReferences(array $arguments)
{
foreach ($arguments as $argument) {
if (is_array($argument)) {
$this->validateReferences($argument);
} elseif ($argument instanceof Reference) {
$targetDefinition = $this->getDefinition((string) $argument);
if (null !== $targetDefinition && $targetDefinition->isAbstract()) {
throw new RuntimeException(sprintf(
'The definition "%s" has a reference to an abstract definition "%s". '
.'Abstract definitions cannot be the target of references.',
$this->currentId,
$argument
));
}
$this->validateScope($argument, $targetDefinition);
}
}
}
/**
* Validates the scope of a single Reference.
*
* @param Reference $reference
* @param Definition $definition
*
* @throws ScopeWideningInjectionException when the definition references a service of a narrower scope
* @throws ScopeCrossingInjectionException when the definition references a service of another scope hierarchy
*/
private function validateScope(Reference $reference, Definition $definition = null)
{
if (ContainerInterface::SCOPE_PROTOTYPE === $this->currentScope) {
return;
}
if (!$reference->isStrict(false)) {
return;
}
if (null === $definition) {
return;
}
if ($this->currentScope === $scope = $definition->getScope(false)) {
return;
}
$id = (string) $reference;
if (in_array($scope, $this->currentScopeChildren, true)) {
throw new ScopeWideningInjectionException($this->currentId, $this->currentScope, $id, $scope);
}
if (!in_array($scope, $this->currentScopeAncestors, true)) {
throw new ScopeCrossingInjectionException($this->currentId, $this->currentScope, $id, $scope);
}
}
/**
* Returns the Definition given an id.
*
* @param string $id Definition identifier
*
* @return Definition
*/
private function getDefinition($id)
{
if (!$this->container->hasDefinition($id)) {
return;
}
return $this->container->getDefinition($id);
}
}

View file

@ -0,0 +1,107 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* This class is used to remove circular dependencies between individual passes.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class Compiler
{
private $passConfig;
private $log = array();
private $loggingFormatter;
private $serviceReferenceGraph;
public function __construct()
{
$this->passConfig = new PassConfig();
$this->serviceReferenceGraph = new ServiceReferenceGraph();
$this->loggingFormatter = new LoggingFormatter();
}
/**
* Returns the PassConfig.
*
* @return PassConfig The PassConfig instance
*/
public function getPassConfig()
{
return $this->passConfig;
}
/**
* Returns the ServiceReferenceGraph.
*
* @return ServiceReferenceGraph The ServiceReferenceGraph instance
*/
public function getServiceReferenceGraph()
{
return $this->serviceReferenceGraph;
}
/**
* Returns the logging formatter which can be used by compilation passes.
*
* @return LoggingFormatter
*/
public function getLoggingFormatter()
{
return $this->loggingFormatter;
}
/**
* Adds a pass to the PassConfig.
*
* @param CompilerPassInterface $pass A compiler pass
* @param string $type The type of the pass
*/
public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION)
{
$this->passConfig->addPass($pass, $type);
}
/**
* Adds a log message.
*
* @param string $string The log message
*/
public function addLogMessage($string)
{
$this->log[] = $string;
}
/**
* Returns the log.
*
* @return array Log array
*/
public function getLog()
{
return $this->log;
}
/**
* Run the Compiler and process all Passes.
*
* @param ContainerBuilder $container
*/
public function compile(ContainerBuilder $container)
{
foreach ($this->passConfig->getPasses() as $pass) {
$pass->process($container);
}
}
}

View file

@ -0,0 +1,29 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Interface that must be implemented by compilation passes.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface CompilerPassInterface
{
/**
* You can modify the container here before it is dumped to PHP code.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container);
}

View file

@ -0,0 +1,68 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Alias;
/**
* Overwrites a service but keeps the overridden one.
*
* @author Christophe Coevoet <stof@notk.org>
* @author Fabien Potencier <fabien@symfony.com>
* @author Diego Saint Esteben <diego@saintesteben.me>
*/
class DecoratorServicePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definitions = new \SplPriorityQueue();
$order = PHP_INT_MAX;
foreach ($container->getDefinitions() as $id => $definition) {
if (!$decorated = $definition->getDecoratedService()) {
continue;
}
$definitions->insert(array($id, $definition), array($decorated[2], --$order));
}
foreach ($definitions as $arr) {
list($id, $definition) = $arr;
list($inner, $renamedId) = $definition->getDecoratedService();
$definition->setDecoratedService(null);
if (!$renamedId) {
$renamedId = $id.'.inner';
}
// we create a new alias/service for the service we are replacing
// to be able to reference it in the new one
if ($container->hasAlias($inner)) {
$alias = $container->getAlias($inner);
$public = $alias->isPublic();
$container->setAlias($renamedId, new Alias((string) $alias, false));
} else {
$decoratedDefinition = $container->getDefinition($inner);
$definition->setTags(array_merge($decoratedDefinition->getTags(), $definition->getTags()));
$definition->setAutowiringTypes(array_merge($decoratedDefinition->getAutowiringTypes(), $definition->getAutowiringTypes()));
$public = $decoratedDefinition->isPublic();
$decoratedDefinition->setPublic(false);
$decoratedDefinition->setTags(array());
$decoratedDefinition->setAutowiringTypes(array());
$container->setDefinition($renamedId, $decoratedDefinition);
}
$container->setAlias($inner, new Alias($id, $public));
}
}
}

View file

@ -0,0 +1,37 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* A pass to automatically process extensions if they implement
* CompilerPassInterface.
*
* @author Wouter J <wouter@wouterj.nl>
*/
class ExtensionCompilerPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
foreach ($container->getExtensions() as $extension) {
if (!$extension instanceof CompilerPassInterface) {
continue;
}
$extension->process($container);
}
}
}

View file

@ -0,0 +1,147 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Inline service definitions where this is possible.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class InlineServiceDefinitionsPass implements RepeatablePassInterface
{
private $repeatedPass;
private $graph;
private $compiler;
private $formatter;
private $currentId;
/**
* {@inheritdoc}
*/
public function setRepeatedPass(RepeatedPass $repeatedPass)
{
$this->repeatedPass = $repeatedPass;
}
/**
* Processes the ContainerBuilder for inline service definitions.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$this->compiler = $container->getCompiler();
$this->formatter = $this->compiler->getLoggingFormatter();
$this->graph = $this->compiler->getServiceReferenceGraph();
$container->setDefinitions($this->inlineArguments($container, $container->getDefinitions(), true));
}
/**
* Processes inline arguments.
*
* @param ContainerBuilder $container The ContainerBuilder
* @param array $arguments An array of arguments
* @param bool $isRoot If we are processing the root definitions or not
*
* @return array
*/
private function inlineArguments(ContainerBuilder $container, array $arguments, $isRoot = false)
{
foreach ($arguments as $k => $argument) {
if ($isRoot) {
$this->currentId = $k;
}
if (is_array($argument)) {
$arguments[$k] = $this->inlineArguments($container, $argument);
} elseif ($argument instanceof Reference) {
if (!$container->hasDefinition($id = (string) $argument)) {
continue;
}
if ($this->isInlineableDefinition($container, $id, $definition = $container->getDefinition($id))) {
$this->compiler->addLogMessage($this->formatter->formatInlineService($this, $id, $this->currentId));
if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope(false)) {
$arguments[$k] = $definition;
} else {
$arguments[$k] = clone $definition;
}
}
} elseif ($argument instanceof Definition) {
$argument->setArguments($this->inlineArguments($container, $argument->getArguments()));
$argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls()));
$argument->setProperties($this->inlineArguments($container, $argument->getProperties()));
$configurator = $this->inlineArguments($container, array($argument->getConfigurator()));
$argument->setConfigurator($configurator[0]);
$factory = $this->inlineArguments($container, array($argument->getFactory()));
$argument->setFactory($factory[0]);
}
}
return $arguments;
}
/**
* Checks if the definition is inlineable.
*
* @param ContainerBuilder $container
* @param string $id
* @param Definition $definition
*
* @return bool If the definition is inlineable
*/
private function isInlineableDefinition(ContainerBuilder $container, $id, Definition $definition)
{
if (!$definition->isShared() || ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) {
return true;
}
if ($definition->isPublic() || $definition->isLazy()) {
return false;
}
if (!$this->graph->hasNode($id)) {
return true;
}
if ($this->currentId == $id) {
return false;
}
$ids = array();
foreach ($this->graph->getNode($id)->getInEdges() as $edge) {
$ids[] = $edge->getSourceNode()->getId();
}
if (count(array_unique($ids)) > 1) {
return false;
}
if (count($ids) > 1 && is_array($factory = $definition->getFactory()) && ($factory[0] instanceof Reference || $factory[0] instanceof Definition)) {
return false;
}
if (count($ids) > 1 && $definition->getFactoryService(false)) {
return false;
}
return $container->getDefinition(reset($ids))->getScope(false) === $definition->getScope(false);
}
}

View file

@ -0,0 +1,45 @@
<?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\Compiler;
/**
* Used to format logging messages during the compilation.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class LoggingFormatter
{
public function formatRemoveService(CompilerPassInterface $pass, $id, $reason)
{
return $this->format($pass, sprintf('Removed service "%s"; reason: %s.', $id, $reason));
}
public function formatInlineService(CompilerPassInterface $pass, $id, $target)
{
return $this->format($pass, sprintf('Inlined service "%s" to "%s".', $id, $target));
}
public function formatUpdateReference(CompilerPassInterface $pass, $serviceId, $oldDestId, $newDestId)
{
return $this->format($pass, sprintf('Changed reference of service "%s" previously pointing to "%s" to "%s".', $serviceId, $oldDestId, $newDestId));
}
public function formatResolveInheritance(CompilerPassInterface $pass, $childId, $parentId)
{
return $this->format($pass, sprintf('Resolving inheritance for "%s" (parent: %s).', $childId, $parentId));
}
public function format(CompilerPassInterface $pass, $message)
{
return sprintf('%s: %s', get_class($pass), $message);
}
}

View file

@ -0,0 +1,64 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
/**
* Merges extension configs into the container builder.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class MergeExtensionConfigurationPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
$parameters = $container->getParameterBag()->all();
$definitions = $container->getDefinitions();
$aliases = $container->getAliases();
$exprLangProviders = $container->getExpressionLanguageProviders();
foreach ($container->getExtensions() as $extension) {
if ($extension instanceof PrependExtensionInterface) {
$extension->prepend($container);
}
}
foreach ($container->getExtensions() as $name => $extension) {
if (!$config = $container->getExtensionConfig($name)) {
// this extension was not called
continue;
}
$config = $container->getParameterBag()->resolveValue($config);
$tmpContainer = new ContainerBuilder($container->getParameterBag());
$tmpContainer->setResourceTracking($container->isTrackingResources());
$tmpContainer->addObjectResource($extension);
foreach ($exprLangProviders as $provider) {
$tmpContainer->addExpressionLanguageProvider($provider);
}
$extension->load($config, $tmpContainer);
$container->merge($tmpContainer);
$container->getParameterBag()->add($parameters);
}
$container->addDefinitions($definitions);
$container->addAliases($aliases);
}
}

View file

@ -0,0 +1,224 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
/**
* Compiler Pass Configuration.
*
* This class has a default configuration embedded.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class PassConfig
{
const TYPE_AFTER_REMOVING = 'afterRemoving';
const TYPE_BEFORE_OPTIMIZATION = 'beforeOptimization';
const TYPE_BEFORE_REMOVING = 'beforeRemoving';
const TYPE_OPTIMIZE = 'optimization';
const TYPE_REMOVE = 'removing';
private $mergePass;
private $afterRemovingPasses = array();
private $beforeOptimizationPasses = array();
private $beforeRemovingPasses = array();
private $optimizationPasses;
private $removingPasses;
public function __construct()
{
$this->mergePass = new MergeExtensionConfigurationPass();
$this->optimizationPasses = array(
new ExtensionCompilerPass(),
new ResolveDefinitionTemplatesPass(),
new DecoratorServicePass(),
new ResolveParameterPlaceHoldersPass(),
new CheckDefinitionValidityPass(),
new ResolveReferencesToAliasesPass(),
new ResolveInvalidReferencesPass(),
new AutowirePass(),
new AnalyzeServiceReferencesPass(true),
new CheckCircularReferencesPass(),
new CheckReferenceValidityPass(),
);
$this->removingPasses = array(
new RemovePrivateAliasesPass(),
new ReplaceAliasByActualDefinitionPass(),
new RemoveAbstractDefinitionsPass(),
new RepeatedPass(array(
new AnalyzeServiceReferencesPass(),
new InlineServiceDefinitionsPass(),
new AnalyzeServiceReferencesPass(),
new RemoveUnusedDefinitionsPass(),
)),
new CheckExceptionOnInvalidReferenceBehaviorPass(),
);
}
/**
* Returns all passes in order to be processed.
*
* @return array An array of all passes to process
*/
public function getPasses()
{
return array_merge(
array($this->mergePass),
$this->beforeOptimizationPasses,
$this->optimizationPasses,
$this->beforeRemovingPasses,
$this->removingPasses,
$this->afterRemovingPasses
);
}
/**
* Adds a pass.
*
* @param CompilerPassInterface $pass A Compiler pass
* @param string $type The pass type
*
* @throws InvalidArgumentException when a pass type doesn't exist
*/
public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION)
{
$property = $type.'Passes';
if (!isset($this->$property)) {
throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type));
}
$this->{$property}[] = $pass;
}
/**
* Gets all passes for the AfterRemoving pass.
*
* @return array An array of passes
*/
public function getAfterRemovingPasses()
{
return $this->afterRemovingPasses;
}
/**
* Gets all passes for the BeforeOptimization pass.
*
* @return array An array of passes
*/
public function getBeforeOptimizationPasses()
{
return $this->beforeOptimizationPasses;
}
/**
* Gets all passes for the BeforeRemoving pass.
*
* @return array An array of passes
*/
public function getBeforeRemovingPasses()
{
return $this->beforeRemovingPasses;
}
/**
* Gets all passes for the Optimization pass.
*
* @return array An array of passes
*/
public function getOptimizationPasses()
{
return $this->optimizationPasses;
}
/**
* Gets all passes for the Removing pass.
*
* @return array An array of passes
*/
public function getRemovingPasses()
{
return $this->removingPasses;
}
/**
* Gets the Merge pass.
*
* @return CompilerPassInterface The merge pass
*/
public function getMergePass()
{
return $this->mergePass;
}
/**
* Sets the Merge Pass.
*
* @param CompilerPassInterface $pass The merge pass
*/
public function setMergePass(CompilerPassInterface $pass)
{
$this->mergePass = $pass;
}
/**
* Sets the AfterRemoving passes.
*
* @param array $passes An array of passes
*/
public function setAfterRemovingPasses(array $passes)
{
$this->afterRemovingPasses = $passes;
}
/**
* Sets the BeforeOptimization passes.
*
* @param array $passes An array of passes
*/
public function setBeforeOptimizationPasses(array $passes)
{
$this->beforeOptimizationPasses = $passes;
}
/**
* Sets the BeforeRemoving passes.
*
* @param array $passes An array of passes
*/
public function setBeforeRemovingPasses(array $passes)
{
$this->beforeRemovingPasses = $passes;
}
/**
* Sets the Optimization passes.
*
* @param array $passes An array of passes
*/
public function setOptimizationPasses(array $passes)
{
$this->optimizationPasses = $passes;
}
/**
* Sets the Removing passes.
*
* @param array $passes An array of passes
*/
public function setRemovingPasses(array $passes)
{
$this->removingPasses = $passes;
}
}

View file

@ -0,0 +1,38 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Removes abstract Definitions.
*/
class RemoveAbstractDefinitionsPass implements CompilerPassInterface
{
/**
* Removes abstract definitions from the ContainerBuilder.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$compiler = $container->getCompiler();
$formatter = $compiler->getLoggingFormatter();
foreach ($container->getDefinitions() as $id => $definition) {
if ($definition->isAbstract()) {
$container->removeDefinition($id);
$compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'abstract'));
}
}
}
}

View file

@ -0,0 +1,44 @@
<?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\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
{
/**
* Removes private aliases from the ContainerBuilder.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$compiler = $container->getCompiler();
$formatter = $compiler->getLoggingFormatter();
foreach ($container->getAliases() as $id => $alias) {
if ($alias->isPublic()) {
continue;
}
$container->removeAlias($id);
$compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'private alias'));
}
}
}

View file

@ -0,0 +1,84 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Removes unused service definitions from the container.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class RemoveUnusedDefinitionsPass implements RepeatablePassInterface
{
private $repeatedPass;
/**
* {@inheritdoc}
*/
public function setRepeatedPass(RepeatedPass $repeatedPass)
{
$this->repeatedPass = $repeatedPass;
}
/**
* Processes the ContainerBuilder to remove unused definitions.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$compiler = $container->getCompiler();
$formatter = $compiler->getLoggingFormatter();
$graph = $compiler->getServiceReferenceGraph();
$hasChanged = false;
foreach ($container->getDefinitions() as $id => $definition) {
if ($definition->isPublic()) {
continue;
}
if ($graph->hasNode($id)) {
$edges = $graph->getNode($id)->getInEdges();
$referencingAliases = array();
$sourceIds = array();
foreach ($edges as $edge) {
$node = $edge->getSourceNode();
$sourceIds[] = $node->getId();
if ($node->isAlias()) {
$referencingAliases[] = $node->getValue();
}
}
$isReferenced = (count(array_unique($sourceIds)) - count($referencingAliases)) > 0;
} else {
$referencingAliases = array();
$isReferenced = false;
}
if (1 === count($referencingAliases) && false === $isReferenced) {
$container->setDefinition((string) reset($referencingAliases), $definition);
$definition->setPublic(true);
$container->removeDefinition($id);
$compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'replaces alias '.reset($referencingAliases)));
} elseif (0 === count($referencingAliases) && false === $isReferenced) {
$container->removeDefinition($id);
$compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'unused'));
$hasChanged = true;
}
}
if ($hasChanged) {
$this->repeatedPass->setRepeat();
}
}
}

View file

@ -0,0 +1,28 @@
<?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\Compiler;
/**
* Interface that must be implemented by passes that are run as part of an
* RepeatedPass.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface RepeatablePassInterface extends CompilerPassInterface
{
/**
* Sets the RepeatedPass interface.
*
* @param RepeatedPass $repeatedPass
*/
public function setRepeatedPass(RepeatedPass $repeatedPass);
}

View file

@ -0,0 +1,84 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
/**
* A pass that might be run repeatedly.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class RepeatedPass implements CompilerPassInterface
{
/**
* @var bool
*/
private $repeat = false;
/**
* @var RepeatablePassInterface[]
*/
private $passes;
/**
* @param RepeatablePassInterface[] $passes An array of RepeatablePassInterface objects
*
* @throws InvalidArgumentException when the passes don't implement RepeatablePassInterface
*/
public function __construct(array $passes)
{
foreach ($passes as $pass) {
if (!$pass instanceof RepeatablePassInterface) {
throw new InvalidArgumentException('$passes must be an array of RepeatablePassInterface.');
}
$pass->setRepeatedPass($this);
}
$this->passes = $passes;
}
/**
* Process the repeatable passes that run more than once.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
do {
$this->repeat = false;
foreach ($this->passes as $pass) {
$pass->process($container);
}
} while ($this->repeat);
}
/**
* Sets if the pass should repeat.
*/
public function setRepeat()
{
$this->repeat = true;
}
/**
* Returns the passes.
*
* @return RepeatablePassInterface[] An array of RepeatablePassInterface objects
*/
public function getPasses()
{
return $this->passes;
}
}

View file

@ -0,0 +1,144 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
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
{
private $compiler;
private $formatter;
/**
* Process the Container to replace aliases with service definitions.
*
* @param ContainerBuilder $container
*
* @throws InvalidArgumentException if the service definition does not exist
*/
public function process(ContainerBuilder $container)
{
// Setup
$this->compiler = $container->getCompiler();
$this->formatter = $this->compiler->getLoggingFormatter();
// First collect all alias targets that need to be replaced
$seenAliasTargets = array();
$replacements = array();
foreach ($container->getAliases() as $definitionId => $target) {
$targetId = (string) $target;
// Special case: leave this target alone
if ('service_container' === $targetId) {
continue;
}
// Check if target needs to be replaces
if (isset($replacements[$targetId])) {
$container->setAlias($definitionId, $replacements[$targetId]);
}
// No neeed to process the same target twice
if (isset($seenAliasTargets[$targetId])) {
continue;
}
// Process new target
$seenAliasTargets[$targetId] = true;
try {
$definition = $container->getDefinition($targetId);
} catch (InvalidArgumentException $e) {
throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $definitionId, $targetId), null, $e);
}
if ($definition->isPublic()) {
continue;
}
// Remove private definition and schedule for replacement
$definition->setPublic(true);
$container->setDefinition($definitionId, $definition);
$container->removeDefinition($targetId);
$replacements[$targetId] = $definitionId;
}
// Now replace target instances in all definitions
foreach ($container->getDefinitions() as $definitionId => $definition) {
$definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments()));
$definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls()));
$definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties()));
$definition->setFactoryService($this->updateFactoryReferenceId($replacements, $definition->getFactoryService(false)), false);
$definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory()));
}
}
/**
* Recursively updates references in an array.
*
* @param array $replacements Table of aliases to replace
* @param string $definitionId Identifier of this definition
* @param array $arguments Where to replace the aliases
*
* @return array
*/
private function updateArgumentReferences(array $replacements, $definitionId, array $arguments)
{
foreach ($arguments as $k => $argument) {
// Handle recursion step
if (is_array($argument)) {
$arguments[$k] = $this->updateArgumentReferences($replacements, $definitionId, $argument);
continue;
}
// Skip arguments that don't need replacement
if (!$argument instanceof Reference) {
continue;
}
$referenceId = (string) $argument;
if (!isset($replacements[$referenceId])) {
continue;
}
// Perform the replacement
$newId = $replacements[$referenceId];
$arguments[$k] = new Reference($newId, $argument->getInvalidBehavior());
$this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $definitionId, $referenceId, $newId));
}
return $arguments;
}
/**
* Returns the updated reference for the factory service.
*
* @param array $replacements Table of aliases to replace
* @param string|null $referenceId Factory service reference identifier
*
* @return string|null
*/
private function updateFactoryReferenceId(array $replacements, $referenceId)
{
if (null === $referenceId) {
return;
}
return isset($replacements[$referenceId]) ? $replacements[$referenceId] : $referenceId;
}
private function updateFactoryReference(array $replacements, $factory)
{
if (is_array($factory) && $factory[0] instanceof Reference && isset($replacements[$referenceId = (string) $factory[0]])) {
$factory[0] = new Reference($replacements[$referenceId], $factory[0]->getInvalidBehavior());
}
return $factory;
}
}

View file

@ -0,0 +1,223 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/**
* This replaces all DefinitionDecorator instances with their equivalent fully
* merged Definition instance.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/
class ResolveDefinitionTemplatesPass implements CompilerPassInterface
{
private $compiler;
private $formatter;
private $currentId;
/**
* Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$this->compiler = $container->getCompiler();
$this->formatter = $this->compiler->getLoggingFormatter();
$container->setDefinitions($this->resolveArguments($container, $container->getDefinitions(), true));
}
/**
* Resolves definition decorator arguments.
*
* @param ContainerBuilder $container The ContainerBuilder
* @param array $arguments An array of arguments
* @param bool $isRoot If we are processing the root definitions or not
*
* @return array
*/
private function resolveArguments(ContainerBuilder $container, array $arguments, $isRoot = false)
{
foreach ($arguments as $k => $argument) {
if ($isRoot) {
// yes, we are specifically fetching the definition from the
// container to ensure we are not operating on stale data
$arguments[$k] = $argument = $container->getDefinition($k);
$this->currentId = $k;
}
if (is_array($argument)) {
$arguments[$k] = $this->resolveArguments($container, $argument);
} elseif ($argument instanceof Definition) {
if ($argument instanceof DefinitionDecorator) {
$arguments[$k] = $argument = $this->resolveDefinition($container, $argument);
if ($isRoot) {
$container->setDefinition($k, $argument);
}
}
$argument->setArguments($this->resolveArguments($container, $argument->getArguments()));
$argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls()));
$argument->setProperties($this->resolveArguments($container, $argument->getProperties()));
$configurator = $this->resolveArguments($container, array($argument->getConfigurator()));
$argument->setConfigurator($configurator[0]);
$factory = $this->resolveArguments($container, array($argument->getFactory()));
$argument->setFactory($factory[0]);
}
}
return $arguments;
}
/**
* Resolves the definition.
*
* @param ContainerBuilder $container The ContainerBuilder
* @param DefinitionDecorator $definition
*
* @return Definition
*
* @throws \RuntimeException When the definition is invalid
*/
private function resolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition)
{
if (!$container->hasDefinition($parent = $definition->getParent())) {
throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $this->currentId));
}
$parentDef = $container->getDefinition($parent);
if ($parentDef instanceof DefinitionDecorator) {
$id = $this->currentId;
$this->currentId = $parent;
$parentDef = $this->resolveDefinition($container, $parentDef);
$container->setDefinition($parent, $parentDef);
$this->currentId = $id;
}
$this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent));
$def = new Definition();
// merge in parent definition
// purposely ignored attributes: scope, abstract, tags
$def->setClass($parentDef->getClass());
$def->setArguments($parentDef->getArguments());
$def->setMethodCalls($parentDef->getMethodCalls());
$def->setProperties($parentDef->getProperties());
$def->setAutowiringTypes($parentDef->getAutowiringTypes());
if ($parentDef->getFactoryClass(false)) {
$def->setFactoryClass($parentDef->getFactoryClass(false));
}
if ($parentDef->getFactoryMethod(false)) {
$def->setFactoryMethod($parentDef->getFactoryMethod(false));
}
if ($parentDef->getFactoryService(false)) {
$def->setFactoryService($parentDef->getFactoryService(false));
}
if ($parentDef->isDeprecated()) {
$def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%'));
}
$def->setFactory($parentDef->getFactory());
$def->setConfigurator($parentDef->getConfigurator());
$def->setFile($parentDef->getFile());
$def->setPublic($parentDef->isPublic());
$def->setLazy($parentDef->isLazy());
$def->setAutowired($parentDef->isAutowired());
// overwrite with values specified in the decorator
$changes = $definition->getChanges();
if (isset($changes['class'])) {
$def->setClass($definition->getClass());
}
if (isset($changes['factory_class'])) {
$def->setFactoryClass($definition->getFactoryClass(false));
}
if (isset($changes['factory_method'])) {
$def->setFactoryMethod($definition->getFactoryMethod(false));
}
if (isset($changes['factory_service'])) {
$def->setFactoryService($definition->getFactoryService(false));
}
if (isset($changes['factory'])) {
$def->setFactory($definition->getFactory());
}
if (isset($changes['configurator'])) {
$def->setConfigurator($definition->getConfigurator());
}
if (isset($changes['file'])) {
$def->setFile($definition->getFile());
}
if (isset($changes['public'])) {
$def->setPublic($definition->isPublic());
}
if (isset($changes['lazy'])) {
$def->setLazy($definition->isLazy());
}
if (isset($changes['deprecated'])) {
$def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%'));
}
if (isset($changes['autowire'])) {
$def->setAutowired($definition->isAutowired());
}
if (isset($changes['decorated_service'])) {
$decoratedService = $definition->getDecoratedService();
if (null === $decoratedService) {
$def->setDecoratedService($decoratedService);
} else {
$def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]);
}
}
// merge arguments
foreach ($definition->getArguments() as $k => $v) {
if (is_numeric($k)) {
$def->addArgument($v);
continue;
}
if (0 !== strpos($k, 'index_')) {
throw new RuntimeException(sprintf('Invalid argument key "%s" found.', $k));
}
$index = (int) substr($k, strlen('index_'));
$def->replaceArgument($index, $v);
}
// merge properties
foreach ($definition->getProperties() as $k => $v) {
$def->setProperty($k, $v);
}
// append method calls
if (count($calls = $definition->getMethodCalls()) > 0) {
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
}
// merge autowiring types
foreach ($definition->getAutowiringTypes() as $autowiringType) {
$def->addAutowiringType($autowiringType);
}
// these attributes are always taken from the child
$def->setAbstract($definition->isAbstract());
$def->setScope($definition->getScope(false), false);
$def->setShared($definition->isShared());
$def->setTags($definition->getTags());
return $def;
}
}

View file

@ -0,0 +1,105 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/**
* Emulates the invalid behavior if the reference is not found within the
* container.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ResolveInvalidReferencesPass implements CompilerPassInterface
{
private $container;
/**
* Process the ContainerBuilder to resolve invalid references.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$this->container = $container;
foreach ($container->getDefinitions() as $definition) {
if ($definition->isSynthetic() || $definition->isAbstract()) {
continue;
}
$definition->setArguments(
$this->processArguments($definition->getArguments())
);
$calls = array();
foreach ($definition->getMethodCalls() as $call) {
try {
$calls[] = array($call[0], $this->processArguments($call[1], true));
} catch (RuntimeException $e) {
// this call is simply removed
}
}
$definition->setMethodCalls($calls);
$properties = array();
foreach ($definition->getProperties() as $name => $value) {
try {
$value = $this->processArguments(array($value), true);
$properties[$name] = reset($value);
} catch (RuntimeException $e) {
// ignore property
}
}
$definition->setProperties($properties);
}
}
/**
* Processes arguments to determine invalid references.
*
* @param array $arguments An array of Reference objects
* @param bool $inMethodCall
*
* @return array
*
* @throws RuntimeException When the config is invalid
*/
private function processArguments(array $arguments, $inMethodCall = false)
{
foreach ($arguments as $k => $argument) {
if (is_array($argument)) {
$arguments[$k] = $this->processArguments($argument, $inMethodCall);
} elseif ($argument instanceof Reference) {
$id = (string) $argument;
$invalidBehavior = $argument->getInvalidBehavior();
$exists = $this->container->has($id);
// resolve invalid behavior
if (!$exists && ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) {
$arguments[$k] = null;
} elseif (!$exists && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) {
if ($inMethodCall) {
throw new RuntimeException('Method shouldn\'t be called.');
}
$arguments[$k] = null;
}
}
}
return $arguments;
}
}

View file

@ -0,0 +1,73 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
/**
* Resolves all parameter placeholders "%somevalue%" to their real values.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ResolveParameterPlaceHoldersPass implements CompilerPassInterface
{
/**
* Processes the ContainerBuilder to resolve parameter placeholders.
*
* @param ContainerBuilder $container
*
* @throws ParameterNotFoundException
*/
public function process(ContainerBuilder $container)
{
$parameterBag = $container->getParameterBag();
foreach ($container->getDefinitions() as $id => $definition) {
try {
$definition->setClass($parameterBag->resolveValue($definition->getClass()));
$definition->setFile($parameterBag->resolveValue($definition->getFile()));
$definition->setArguments($parameterBag->resolveValue($definition->getArguments()));
if ($definition->getFactoryClass(false)) {
$definition->setFactoryClass($parameterBag->resolveValue($definition->getFactoryClass(false)));
}
$factory = $definition->getFactory();
if (is_array($factory) && isset($factory[0])) {
$factory[0] = $parameterBag->resolveValue($factory[0]);
$definition->setFactory($factory);
}
$calls = array();
foreach ($definition->getMethodCalls() as $name => $arguments) {
$calls[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($arguments);
}
$definition->setMethodCalls($calls);
$definition->setProperties($parameterBag->resolveValue($definition->getProperties()));
} catch (ParameterNotFoundException $e) {
$e->setSourceId($id);
throw $e;
}
}
$aliases = array();
foreach ($container->getAliases() as $name => $target) {
$aliases[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($target);
}
$container->setAliases($aliases);
$parameterBag->resolve();
}
}

View file

@ -0,0 +1,125 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Replaces all references to aliases with references to the actual service.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ResolveReferencesToAliasesPass implements CompilerPassInterface
{
private $container;
/**
* Processes the ContainerBuilder to replace references to aliases with actual service references.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$this->container = $container;
foreach ($container->getDefinitions() as $definition) {
if ($definition->isSynthetic() || $definition->isAbstract()) {
continue;
}
$definition->setArguments($this->processArguments($definition->getArguments()));
$definition->setMethodCalls($this->processArguments($definition->getMethodCalls()));
$definition->setProperties($this->processArguments($definition->getProperties()));
$definition->setFactory($this->processFactory($definition->getFactory()));
$definition->setFactoryService($this->processFactoryService($definition->getFactoryService(false)), false);
}
foreach ($container->getAliases() as $id => $alias) {
$aliasId = (string) $alias;
if ($aliasId !== $defId = $this->getDefinitionId($aliasId)) {
$container->setAlias($id, new Alias($defId, $alias->isPublic()));
}
}
}
/**
* Processes the arguments to replace aliases.
*
* @param array $arguments An array of References
*
* @return array An array of References
*/
private function processArguments(array $arguments)
{
foreach ($arguments as $k => $argument) {
if (is_array($argument)) {
$arguments[$k] = $this->processArguments($argument);
} elseif ($argument instanceof Reference) {
$defId = $this->getDefinitionId($id = (string) $argument);
if ($defId !== $id) {
$arguments[$k] = new Reference($defId, $argument->getInvalidBehavior(), $argument->isStrict(false));
}
}
}
return $arguments;
}
private function processFactoryService($factoryService)
{
if (null === $factoryService) {
return;
}
return $this->getDefinitionId($factoryService);
}
private function processFactory($factory)
{
if (null === $factory || !is_array($factory) || !$factory[0] instanceof Reference) {
return $factory;
}
$defId = $this->getDefinitionId($id = (string) $factory[0]);
if ($defId !== $id) {
$factory[0] = new Reference($defId, $factory[0]->getInvalidBehavior(), $factory[0]->isStrict(false));
}
return $factory;
}
/**
* Resolves an alias into a definition id.
*
* @param string $id The definition or alias id to resolve
*
* @return string The definition id with aliases resolved
*/
private function getDefinitionId($id)
{
$seen = array();
while ($this->container->hasAlias($id)) {
if (isset($seen[$id])) {
throw new ServiceCircularReferenceException($id, array_keys($seen));
}
$seen[$id] = true;
$id = (string) $this->container->getAlias($id);
}
return $id;
}
}

View file

@ -0,0 +1,114 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
/**
* This is a directed graph of your services.
*
* This information can be used by your compiler passes instead of collecting
* it themselves which improves performance quite a lot.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ServiceReferenceGraph
{
/**
* @var ServiceReferenceGraphNode[]
*/
private $nodes = array();
/**
* Checks if the graph has a specific node.
*
* @param string $id Id to check
*
* @return bool
*/
public function hasNode($id)
{
return isset($this->nodes[$id]);
}
/**
* Gets a node by identifier.
*
* @param string $id The id to retrieve
*
* @return ServiceReferenceGraphNode
*
* @throws InvalidArgumentException if no node matches the supplied identifier
*/
public function getNode($id)
{
if (!isset($this->nodes[$id])) {
throw new InvalidArgumentException(sprintf('There is no node with id "%s".', $id));
}
return $this->nodes[$id];
}
/**
* Returns all nodes.
*
* @return ServiceReferenceGraphNode[]
*/
public function getNodes()
{
return $this->nodes;
}
/**
* Clears all nodes.
*/
public function clear()
{
$this->nodes = array();
}
/**
* Connects 2 nodes together in the Graph.
*
* @param string $sourceId
* @param string $sourceValue
* @param string $destId
* @param string $destValue
* @param string $reference
*/
public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null)
{
$sourceNode = $this->createNode($sourceId, $sourceValue);
$destNode = $this->createNode($destId, $destValue);
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference);
$sourceNode->addOutEdge($edge);
$destNode->addInEdge($edge);
}
/**
* Creates a graph node.
*
* @param string $id
* @param string $value
*
* @return ServiceReferenceGraphNode
*/
private function createNode($id, $value)
{
if (isset($this->nodes[$id]) && $this->nodes[$id]->getValue() === $value) {
return $this->nodes[$id];
}
return $this->nodes[$id] = new ServiceReferenceGraphNode($id, $value);
}
}

View file

@ -0,0 +1,68 @@
<?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\Compiler;
/**
* Represents an edge in your service graph.
*
* Value is typically a reference.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ServiceReferenceGraphEdge
{
private $sourceNode;
private $destNode;
private $value;
/**
* @param ServiceReferenceGraphNode $sourceNode
* @param ServiceReferenceGraphNode $destNode
* @param string $value
*/
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null)
{
$this->sourceNode = $sourceNode;
$this->destNode = $destNode;
$this->value = $value;
}
/**
* Returns the value of the edge.
*
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Returns the source node.
*
* @return ServiceReferenceGraphNode
*/
public function getSourceNode()
{
return $this->sourceNode;
}
/**
* Returns the destination node.
*
* @return ServiceReferenceGraphNode
*/
public function getDestNode()
{
return $this->destNode;
}
}

View file

@ -0,0 +1,120 @@
<?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\Compiler;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Alias;
/**
* Represents a node in your service graph.
*
* Value is typically a definition, or an alias.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ServiceReferenceGraphNode
{
private $id;
private $inEdges = array();
private $outEdges = array();
private $value;
/**
* @param string $id The node identifier
* @param mixed $value The node value
*/
public function __construct($id, $value)
{
$this->id = $id;
$this->value = $value;
}
/**
* Adds an in edge to this node.
*
* @param ServiceReferenceGraphEdge $edge
*/
public function addInEdge(ServiceReferenceGraphEdge $edge)
{
$this->inEdges[] = $edge;
}
/**
* Adds an out edge to this node.
*
* @param ServiceReferenceGraphEdge $edge
*/
public function addOutEdge(ServiceReferenceGraphEdge $edge)
{
$this->outEdges[] = $edge;
}
/**
* Checks if the value of this node is an Alias.
*
* @return bool True if the value is an Alias instance
*/
public function isAlias()
{
return $this->value instanceof Alias;
}
/**
* Checks if the value of this node is a Definition.
*
* @return bool True if the value is a Definition instance
*/
public function isDefinition()
{
return $this->value instanceof Definition;
}
/**
* Returns the identifier.
*
* @return string
*/
public function getId()
{
return $this->id;
}
/**
* Returns the in edges.
*
* @return array The in ServiceReferenceGraphEdge array
*/
public function getInEdges()
{
return $this->inEdges;
}
/**
* Returns the out edges.
*
* @return array The out ServiceReferenceGraphEdge array
*/
public function getOutEdges()
{
return $this->outEdges;
}
/**
* Returns the value of this Node.
*
* @return mixed The value
*/
public function getValue()
{
return $this->value;
}
}

View file

@ -0,0 +1,595 @@
<?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;
use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
/**
* Container is a dependency injection container.
*
* It gives access to object instances (services).
*
* Services and parameters are simple key/pair stores.
*
* Parameter and service keys are case insensitive.
*
* A service id can contain lowercased letters, digits, underscores, and dots.
* Underscores are used to separate words, and dots to group services
* under namespaces:
*
* <ul>
* <li>request</li>
* <li>mysql_session_storage</li>
* <li>symfony.mysql_session_storage</li>
* </ul>
*
* A service can also be defined by creating a method named
* getXXXService(), where XXX is the camelized version of the id:
*
* <ul>
* <li>request -> getRequestService()</li>
* <li>mysql_session_storage -> getMysqlSessionStorageService()</li>
* <li>symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()</li>
* </ul>
*
* The container can have three possible behaviors when a service does not exist:
*
* * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default)
* * NULL_ON_INVALID_REFERENCE: Returns null
* * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference
* (for instance, ignore a setter if the service does not exist)
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class Container implements IntrospectableContainerInterface, ResettableContainerInterface
{
/**
* @var ParameterBagInterface
*/
protected $parameterBag;
protected $services = array();
protected $methodMap = array();
protected $aliases = array();
protected $scopes = array();
protected $scopeChildren = array();
protected $scopedServices = array();
protected $scopeStacks = array();
protected $loading = array();
private $underscoreMap = array('_' => '', '.' => '_', '\\' => '_');
/**
* @param ParameterBagInterface $parameterBag A ParameterBagInterface instance
*/
public function __construct(ParameterBagInterface $parameterBag = null)
{
$this->parameterBag = $parameterBag ?: new ParameterBag();
}
/**
* Compiles the container.
*
* This method does two things:
*
* * Parameter values are resolved;
* * The parameter bag is frozen.
*/
public function compile()
{
$this->parameterBag->resolve();
$this->parameterBag = new FrozenParameterBag($this->parameterBag->all());
}
/**
* Returns true if the container parameter bag are frozen.
*
* @return bool true if the container parameter bag are frozen, false otherwise
*/
public function isFrozen()
{
return $this->parameterBag instanceof FrozenParameterBag;
}
/**
* Gets the service container parameter bag.
*
* @return ParameterBagInterface A ParameterBagInterface instance
*/
public function getParameterBag()
{
return $this->parameterBag;
}
/**
* Gets a parameter.
*
* @param string $name The parameter name
*
* @return mixed The parameter value
*
* @throws InvalidArgumentException if the parameter is not defined
*/
public function getParameter($name)
{
return $this->parameterBag->get($name);
}
/**
* Checks if a parameter exists.
*
* @param string $name The parameter name
*
* @return bool The presence of parameter in container
*/
public function hasParameter($name)
{
return $this->parameterBag->has($name);
}
/**
* Sets a parameter.
*
* @param string $name The parameter name
* @param mixed $value The parameter value
*/
public function setParameter($name, $value)
{
$this->parameterBag->set($name, $value);
}
/**
* Sets a service.
*
* Setting a service to null resets the service: has() returns false and get()
* behaves in the same way as if the service was never created.
*
* Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0.
*
* @param string $id The service identifier
* @param object $service The service instance
* @param string $scope The scope of the service
*
* @throws RuntimeException When trying to set a service in an inactive scope
* @throws InvalidArgumentException When trying to set a service in the prototype scope
*/
public function set($id, $service, $scope = self::SCOPE_CONTAINER)
{
if (!in_array($scope, array('container', 'request')) || ('request' === $scope && 'request' !== $id)) {
@trigger_error('The concept of container scopes is deprecated since version 2.8 and will be removed in 3.0. Omit the third parameter.', E_USER_DEPRECATED);
}
if (self::SCOPE_PROTOTYPE === $scope) {
throw new InvalidArgumentException(sprintf('You cannot set service "%s" of scope "prototype".', $id));
}
$id = strtolower($id);
if ('service_container' === $id) {
// BC: 'service_container' is no longer a self-reference but always
// $this, so ignore this call.
// @todo Throw InvalidArgumentException in next major release.
return;
}
if (self::SCOPE_CONTAINER !== $scope) {
if (!isset($this->scopedServices[$scope])) {
throw new RuntimeException(sprintf('You cannot set service "%s" of inactive scope.', $id));
}
$this->scopedServices[$scope][$id] = $service;
}
if (isset($this->aliases[$id])) {
unset($this->aliases[$id]);
}
$this->services[$id] = $service;
if (method_exists($this, $method = 'synchronize'.strtr($id, $this->underscoreMap).'Service')) {
$this->$method();
}
if (null === $service) {
if (self::SCOPE_CONTAINER !== $scope) {
unset($this->scopedServices[$scope][$id]);
}
unset($this->services[$id]);
}
}
/**
* Returns true if the given service is defined.
*
* @param string $id The service identifier
*
* @return bool true if the service is defined, false otherwise
*/
public function has($id)
{
for ($i = 2;;) {
if ('service_container' === $id
|| isset($this->aliases[$id])
|| isset($this->services[$id])
|| array_key_exists($id, $this->services)
) {
return true;
}
if (--$i && $id !== $lcId = strtolower($id)) {
$id = $lcId;
} else {
return method_exists($this, 'get'.strtr($id, $this->underscoreMap).'Service');
}
}
}
/**
* Gets a service.
*
* If a service is defined both through a set() method and
* with a get{$id}Service() method, the former has always precedence.
*
* @param string $id The service identifier
* @param int $invalidBehavior The behavior when the service does not exist
*
* @return object The associated service
*
* @throws ServiceCircularReferenceException When a circular reference is detected
* @throws ServiceNotFoundException When the service is not defined
* @throws \Exception if an exception has been thrown when the service has been resolved
*
* @see Reference
*/
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.
for ($i = 2;;) {
if ('service_container' === $id) {
return $this;
}
if (isset($this->aliases[$id])) {
$id = $this->aliases[$id];
}
// Re-use shared service instance if it exists.
if (isset($this->services[$id]) || array_key_exists($id, $this->services)) {
return $this->services[$id];
}
if (isset($this->loading[$id])) {
throw new ServiceCircularReferenceException($id, array_keys($this->loading));
}
if (isset($this->methodMap[$id])) {
$method = $this->methodMap[$id];
} elseif (--$i && $id !== $lcId = strtolower($id)) {
$id = $lcId;
continue;
} elseif (method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) {
// $method is set to the right value, proceed
} else {
if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
if (!$id) {
throw new ServiceNotFoundException($id);
}
$alternatives = array();
foreach ($this->getServiceIds() as $knownId) {
$lev = levenshtein($id, $knownId);
if ($lev <= strlen($id) / 3 || false !== strpos($knownId, $id)) {
$alternatives[] = $knownId;
}
}
throw new ServiceNotFoundException($id, null, null, $alternatives);
}
return;
}
$this->loading[$id] = true;
try {
$service = $this->$method();
} catch (\Exception $e) {
unset($this->loading[$id]);
unset($this->services[$id]);
if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
return;
}
throw $e;
} catch (\Throwable $e) {
unset($this->loading[$id]);
unset($this->services[$id]);
throw $e;
}
unset($this->loading[$id]);
return $service;
}
}
/**
* Returns true if the given service has actually been initialized.
*
* @param string $id The service identifier
*
* @return bool true if service has already been initialized, false otherwise
*/
public function initialized($id)
{
$id = strtolower($id);
if ('service_container' === $id) {
// BC: 'service_container' was a synthetic service previously.
// @todo Change to false in next major release.
return true;
}
if (isset($this->aliases[$id])) {
$id = $this->aliases[$id];
}
return isset($this->services[$id]) || array_key_exists($id, $this->services);
}
/**
* {@inheritdoc}
*/
public function reset()
{
if (!empty($this->scopedServices)) {
throw new LogicException('Resetting the container is not allowed when a scope is active.');
}
$this->services = array();
}
/**
* Gets all service ids.
*
* @return array An array of all defined service ids
*/
public function getServiceIds()
{
$ids = array();
foreach (get_class_methods($this) as $method) {
if (preg_match('/^get(.+)Service$/', $method, $match)) {
$ids[] = self::underscore($match[1]);
}
}
$ids[] = 'service_container';
return array_unique(array_merge($ids, array_keys($this->services)));
}
/**
* This is called when you enter a scope.
*
* @param string $name
*
* @throws RuntimeException When the parent scope is inactive
* @throws InvalidArgumentException When the scope does not exist
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function enterScope($name)
{
if ('request' !== $name) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
if (!isset($this->scopes[$name])) {
throw new InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name));
}
if (self::SCOPE_CONTAINER !== $this->scopes[$name] && !isset($this->scopedServices[$this->scopes[$name]])) {
throw new RuntimeException(sprintf('The parent scope "%s" must be active when entering this scope.', $this->scopes[$name]));
}
// check if a scope of this name is already active, if so we need to
// remove all services of this scope, and those of any of its child
// scopes from the global services map
if (isset($this->scopedServices[$name])) {
$services = array($this->services, $name => $this->scopedServices[$name]);
unset($this->scopedServices[$name]);
foreach ($this->scopeChildren[$name] as $child) {
if (isset($this->scopedServices[$child])) {
$services[$child] = $this->scopedServices[$child];
unset($this->scopedServices[$child]);
}
}
// update global map
$this->services = call_user_func_array('array_diff_key', $services);
array_shift($services);
// add stack entry for this scope so we can restore the removed services later
if (!isset($this->scopeStacks[$name])) {
$this->scopeStacks[$name] = new \SplStack();
}
$this->scopeStacks[$name]->push($services);
}
$this->scopedServices[$name] = array();
}
/**
* This is called to leave the current scope, and move back to the parent
* scope.
*
* @param string $name The name of the scope to leave
*
* @throws InvalidArgumentException if the scope is not active
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function leaveScope($name)
{
if ('request' !== $name) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
if (!isset($this->scopedServices[$name])) {
throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $name));
}
// remove all services of this scope, or any of its child scopes from
// the global service map
$services = array($this->services, $this->scopedServices[$name]);
unset($this->scopedServices[$name]);
foreach ($this->scopeChildren[$name] as $child) {
if (isset($this->scopedServices[$child])) {
$services[] = $this->scopedServices[$child];
unset($this->scopedServices[$child]);
}
}
// update global map
$this->services = call_user_func_array('array_diff_key', $services);
// check if we need to restore services of a previous scope of this type
if (isset($this->scopeStacks[$name]) && count($this->scopeStacks[$name]) > 0) {
$services = $this->scopeStacks[$name]->pop();
$this->scopedServices += $services;
if ($this->scopeStacks[$name]->isEmpty()) {
unset($this->scopeStacks[$name]);
}
foreach ($services as $array) {
foreach ($array as $id => $service) {
$this->set($id, $service, $name);
}
}
}
}
/**
* Adds a scope to the container.
*
* @param ScopeInterface $scope
*
* @throws InvalidArgumentException
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function addScope(ScopeInterface $scope)
{
$name = $scope->getName();
$parentScope = $scope->getParentName();
if ('request' !== $name) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) {
throw new InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name));
}
if (isset($this->scopes[$name])) {
throw new InvalidArgumentException(sprintf('A scope with name "%s" already exists.', $name));
}
if (self::SCOPE_CONTAINER !== $parentScope && !isset($this->scopes[$parentScope])) {
throw new InvalidArgumentException(sprintf('The parent scope "%s" does not exist, or is invalid.', $parentScope));
}
$this->scopes[$name] = $parentScope;
$this->scopeChildren[$name] = array();
// normalize the child relations
while ($parentScope !== self::SCOPE_CONTAINER) {
$this->scopeChildren[$parentScope][] = $name;
$parentScope = $this->scopes[$parentScope];
}
}
/**
* Returns whether this container has a certain scope.
*
* @param string $name The name of the scope
*
* @return bool
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function hasScope($name)
{
if ('request' !== $name) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return isset($this->scopes[$name]);
}
/**
* Returns whether this scope is currently active.
*
* This does not actually check if the passed scope actually exists.
*
* @param string $name
*
* @return bool
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function isScopeActive($name)
{
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
return isset($this->scopedServices[$name]);
}
/**
* Camelizes a string.
*
* @param string $id A string to camelize
*
* @return string The camelized string
*/
public static function camelize($id)
{
return strtr(ucwords(strtr($id, array('_' => ' ', '.' => '_ ', '\\' => '_ '))), array(' ' => ''));
}
/**
* A string to underscore.
*
* @param string $id The string to underscore
*
* @return string The underscored string
*/
public static function underscore($id)
{
return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), str_replace('_', '.', $id)));
}
private function __clone()
{
}
}

View file

@ -0,0 +1,35 @@
<?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;
/**
* A simple implementation of ContainerAwareInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since version 2.8, to be removed in 3.0. Use the ContainerAwareTrait instead.
*/
abstract class ContainerAware implements ContainerAwareInterface
{
/**
* @var ContainerInterface
*/
protected $container;
/**
* {@inheritdoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
}

View file

@ -0,0 +1,27 @@
<?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;
/**
* ContainerAwareInterface should be implemented by classes that depends on a Container.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ContainerAwareInterface
{
/**
* Sets the container.
*
* @param ContainerInterface|null $container A ContainerInterface instance or null
*/
public function setContainer(ContainerInterface $container = null);
}

View file

@ -0,0 +1,35 @@
<?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;
/**
* ContainerAware trait.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
trait ContainerAwareTrait
{
/**
* @var ContainerInterface
*/
protected $container;
/**
* Sets the container.
*
* @param ContainerInterface|null $container A ContainerInterface instance or null
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,145 @@
<?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;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
/**
* ContainerInterface is the interface implemented by service container classes.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface ContainerInterface
{
const EXCEPTION_ON_INVALID_REFERENCE = 1;
const NULL_ON_INVALID_REFERENCE = 2;
const IGNORE_ON_INVALID_REFERENCE = 3;
const SCOPE_CONTAINER = 'container';
const SCOPE_PROTOTYPE = 'prototype';
/**
* Sets a service.
*
* Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0.
*
* @param string $id The service identifier
* @param object $service The service instance
* @param string $scope The scope of the service
*/
public function set($id, $service, $scope = self::SCOPE_CONTAINER);
/**
* Gets a service.
*
* @param string $id The service identifier
* @param int $invalidBehavior The behavior when the service does not exist
*
* @return object The associated service
*
* @throws ServiceCircularReferenceException When a circular reference is detected
* @throws ServiceNotFoundException When the service is not defined
*
* @see Reference
*/
public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE);
/**
* Returns true if the given service is defined.
*
* @param string $id The service identifier
*
* @return bool true if the service is defined, false otherwise
*/
public function has($id);
/**
* Gets a parameter.
*
* @param string $name The parameter name
*
* @return mixed The parameter value
*
* @throws InvalidArgumentException if the parameter is not defined
*/
public function getParameter($name);
/**
* Checks if a parameter exists.
*
* @param string $name The parameter name
*
* @return bool The presence of parameter in container
*/
public function hasParameter($name);
/**
* Sets a parameter.
*
* @param string $name The parameter name
* @param mixed $value The parameter value
*/
public function setParameter($name, $value);
/**
* Enters the given scope.
*
* @param string $name
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function enterScope($name);
/**
* Leaves the current scope, and re-enters the parent scope.
*
* @param string $name
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function leaveScope($name);
/**
* Adds a scope to the container.
*
* @param ScopeInterface $scope
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function addScope(ScopeInterface $scope);
/**
* Whether this container has the given scope.
*
* @param string $name
*
* @return bool
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function hasScope($name);
/**
* Determines whether the given scope is currently active.
*
* It does however not check if the scope actually exists.
*
* @param string $name
*
* @return bool
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function isScopeActive($name);
}

View file

@ -0,0 +1,913 @@
<?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;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
/**
* Definition represents a service definition.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Definition
{
private $class;
private $file;
private $factory;
private $factoryClass;
private $factoryMethod;
private $factoryService;
private $shared = true;
private $deprecated = false;
private $deprecationTemplate = 'The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.';
private $scope = ContainerInterface::SCOPE_CONTAINER;
private $properties = array();
private $calls = array();
private $configurator;
private $tags = array();
private $public = true;
private $synthetic = false;
private $abstract = false;
private $synchronized = false;
private $lazy = false;
private $decoratedService;
private $autowired = false;
private $autowiringTypes = array();
protected $arguments;
/**
* @param string|null $class The service class
* @param array $arguments An array of arguments to pass to the service constructor
*/
public function __construct($class = null, array $arguments = array())
{
$this->class = $class;
$this->arguments = $arguments;
}
/**
* Sets a factory.
*
* @param string|array $factory A PHP function or an array containing a class/Reference and a method to call
*
* @return $this
*/
public function setFactory($factory)
{
if (is_string($factory) && strpos($factory, '::') !== false) {
$factory = explode('::', $factory, 2);
}
$this->factory = $factory;
return $this;
}
/**
* Gets the factory.
*
* @return string|array The PHP function or an array containing a class/Reference and a method to call
*/
public function getFactory()
{
return $this->factory;
}
/**
* Sets the name of the class that acts as a factory using the factory method,
* which will be invoked statically.
*
* @param string $factoryClass The factory class name
*
* @return $this
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function setFactoryClass($factoryClass)
{
@trigger_error(sprintf('%s(%s) is deprecated since version 2.6 and will be removed in 3.0. Use Definition::setFactory() instead.', __METHOD__, $factoryClass), E_USER_DEPRECATED);
$this->factoryClass = $factoryClass;
return $this;
}
/**
* Gets the factory class.
*
* @return string|null The factory class name
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function getFactoryClass($triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return $this->factoryClass;
}
/**
* Sets the factory method able to create an instance of this class.
*
* @param string $factoryMethod The factory method name
*
* @return $this
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function setFactoryMethod($factoryMethod)
{
@trigger_error(sprintf('%s(%s) is deprecated since version 2.6 and will be removed in 3.0. Use Definition::setFactory() instead.', __METHOD__, $factoryMethod), E_USER_DEPRECATED);
$this->factoryMethod = $factoryMethod;
return $this;
}
/**
* Sets the service that this service is decorating.
*
* @param null|string $id The decorated service id, use null to remove decoration
* @param null|string $renamedId The new decorated service id
* @param int $priority The priority of decoration
*
* @return $this
*
* @throws InvalidArgumentException In case the decorated service id and the new decorated service id are equals.
*/
public function setDecoratedService($id, $renamedId = null, $priority = 0)
{
if ($renamedId && $id == $renamedId) {
throw new \InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id));
}
if (null === $id) {
$this->decoratedService = null;
} else {
$this->decoratedService = array($id, $renamedId, (int) $priority);
}
return $this;
}
/**
* Gets the service that decorates this service.
*
* @return null|array An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated
*/
public function getDecoratedService()
{
return $this->decoratedService;
}
/**
* Gets the factory method.
*
* @return string|null The factory method name
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function getFactoryMethod($triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return $this->factoryMethod;
}
/**
* Sets the name of the service that acts as a factory using the factory method.
*
* @param string $factoryService The factory service id
*
* @return $this
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function setFactoryService($factoryService, $triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error(sprintf('%s(%s) is deprecated since version 2.6 and will be removed in 3.0. Use Definition::setFactory() instead.', __METHOD__, $factoryService), E_USER_DEPRECATED);
}
$this->factoryService = $factoryService;
return $this;
}
/**
* Gets the factory service id.
*
* @return string|null The factory service id
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function getFactoryService($triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return $this->factoryService;
}
/**
* Sets the service class.
*
* @param string $class The service class
*
* @return $this
*/
public function setClass($class)
{
$this->class = $class;
return $this;
}
/**
* Gets the service class.
*
* @return string|null The service class
*/
public function getClass()
{
return $this->class;
}
/**
* Sets the arguments to pass to the service constructor/factory method.
*
* @param array $arguments An array of arguments
*
* @return $this
*/
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
return $this;
}
public function setProperties(array $properties)
{
$this->properties = $properties;
return $this;
}
public function getProperties()
{
return $this->properties;
}
public function setProperty($name, $value)
{
$this->properties[$name] = $value;
return $this;
}
/**
* Adds an argument to pass to the service constructor/factory method.
*
* @param mixed $argument An argument
*
* @return $this
*/
public function addArgument($argument)
{
$this->arguments[] = $argument;
return $this;
}
/**
* Sets a specific argument.
*
* @param int $index
* @param mixed $argument
*
* @return $this
*
* @throws OutOfBoundsException When the replaced argument does not exist
*/
public function replaceArgument($index, $argument)
{
if ($index < 0 || $index > count($this->arguments) - 1) {
throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, count($this->arguments) - 1));
}
$this->arguments[$index] = $argument;
return $this;
}
/**
* Gets the arguments to pass to the service constructor/factory method.
*
* @return array The array of arguments
*/
public function getArguments()
{
return $this->arguments;
}
/**
* Gets an argument to pass to the service constructor/factory method.
*
* @param int $index
*
* @return mixed The argument value
*
* @throws OutOfBoundsException When the argument does not exist
*/
public function getArgument($index)
{
if ($index < 0 || $index > count($this->arguments) - 1) {
throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, count($this->arguments) - 1));
}
return $this->arguments[$index];
}
/**
* Sets the methods to call after service initialization.
*
* @param array $calls An array of method calls
*
* @return $this
*/
public function setMethodCalls(array $calls = array())
{
$this->calls = array();
foreach ($calls as $call) {
$this->addMethodCall($call[0], $call[1]);
}
return $this;
}
/**
* Adds a method to call after service initialization.
*
* @param string $method The method name to call
* @param array $arguments An array of arguments to pass to the method call
*
* @return $this
*
* @throws InvalidArgumentException on empty $method param
*/
public function addMethodCall($method, array $arguments = array())
{
if (empty($method)) {
throw new InvalidArgumentException(sprintf('Method name cannot be empty.'));
}
$this->calls[] = array($method, $arguments);
return $this;
}
/**
* Removes a method to call after service initialization.
*
* @param string $method The method name to remove
*
* @return $this
*/
public function removeMethodCall($method)
{
foreach ($this->calls as $i => $call) {
if ($call[0] === $method) {
unset($this->calls[$i]);
break;
}
}
return $this;
}
/**
* Check if the current definition has a given method to call after service initialization.
*
* @param string $method The method name to search for
*
* @return bool
*/
public function hasMethodCall($method)
{
foreach ($this->calls as $call) {
if ($call[0] === $method) {
return true;
}
}
return false;
}
/**
* Gets the methods to call after service initialization.
*
* @return array An array of method calls
*/
public function getMethodCalls()
{
return $this->calls;
}
/**
* Sets tags for this definition.
*
* @param array $tags
*
* @return $this
*/
public function setTags(array $tags)
{
$this->tags = $tags;
return $this;
}
/**
* Returns all tags.
*
* @return array An array of tags
*/
public function getTags()
{
return $this->tags;
}
/**
* Gets a tag by name.
*
* @param string $name The tag name
*
* @return array An array of attributes
*/
public function getTag($name)
{
return isset($this->tags[$name]) ? $this->tags[$name] : array();
}
/**
* Adds a tag for this definition.
*
* @param string $name The tag name
* @param array $attributes An array of attributes
*
* @return $this
*/
public function addTag($name, array $attributes = array())
{
$this->tags[$name][] = $attributes;
return $this;
}
/**
* Whether this definition has a tag with the given name.
*
* @param string $name
*
* @return bool
*/
public function hasTag($name)
{
return isset($this->tags[$name]);
}
/**
* Clears all tags for a given name.
*
* @param string $name The tag name
*
* @return $this
*/
public function clearTag($name)
{
unset($this->tags[$name]);
return $this;
}
/**
* Clears the tags for this definition.
*
* @return $this
*/
public function clearTags()
{
$this->tags = array();
return $this;
}
/**
* Sets a file to require before creating the service.
*
* @param string $file A full pathname to include
*
* @return $this
*/
public function setFile($file)
{
$this->file = $file;
return $this;
}
/**
* Gets the file to require before creating the service.
*
* @return string|null The full pathname to include
*/
public function getFile()
{
return $this->file;
}
/**
* Sets if the service must be shared or not.
*
* @param bool $shared Whether the service must be shared or not
*
* @return $this
*/
public function setShared($shared)
{
$this->shared = (bool) $shared;
return $this;
}
/**
* Whether this service is shared.
*
* @return bool
*/
public function isShared()
{
return $this->shared;
}
/**
* Sets the scope of the service.
*
* @param string $scope Whether the service must be shared or not
*
* @return $this
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function setScope($scope, $triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
if (ContainerInterface::SCOPE_PROTOTYPE === $scope) {
$this->setShared(false);
}
$this->scope = $scope;
return $this;
}
/**
* Returns the scope of the service.
*
* @return string
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function getScope($triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return $this->scope;
}
/**
* Sets the visibility of this service.
*
* @param bool $boolean
*
* @return $this
*/
public function setPublic($boolean)
{
$this->public = (bool) $boolean;
return $this;
}
/**
* Whether this service is public facing.
*
* @return bool
*/
public function isPublic()
{
return $this->public;
}
/**
* Sets the synchronized flag of this service.
*
* @param bool $boolean
*
* @return $this
*
* @deprecated since version 2.7, will be removed in 3.0.
*/
public function setSynchronized($boolean, $triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.7 and will be removed in 3.0.', E_USER_DEPRECATED);
}
$this->synchronized = (bool) $boolean;
return $this;
}
/**
* Whether this service is synchronized.
*
* @return bool
*
* @deprecated since version 2.7, will be removed in 3.0.
*/
public function isSynchronized($triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.7 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return $this->synchronized;
}
/**
* Sets the lazy flag of this service.
*
* @param bool $lazy
*
* @return $this
*/
public function setLazy($lazy)
{
$this->lazy = (bool) $lazy;
return $this;
}
/**
* Whether this service is lazy.
*
* @return bool
*/
public function isLazy()
{
return $this->lazy;
}
/**
* Sets whether this definition is synthetic, that is not constructed by the
* container, but dynamically injected.
*
* @param bool $boolean
*
* @return $this
*/
public function setSynthetic($boolean)
{
$this->synthetic = (bool) $boolean;
return $this;
}
/**
* Whether this definition is synthetic, that is not constructed by the
* container, but dynamically injected.
*
* @return bool
*/
public function isSynthetic()
{
return $this->synthetic;
}
/**
* Whether this definition is abstract, that means it merely serves as a
* template for other definitions.
*
* @param bool $boolean
*
* @return $this
*/
public function setAbstract($boolean)
{
$this->abstract = (bool) $boolean;
return $this;
}
/**
* Whether this definition is abstract, that means it merely serves as a
* template for other definitions.
*
* @return bool
*/
public function isAbstract()
{
return $this->abstract;
}
/**
* Whether this definition is deprecated, that means it should not be called
* anymore.
*
* @param bool $status
* @param string $template Template message to use if the definition 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;
}
/**
* Whether this definition is deprecated, that means it should not be called
* anymore.
*
* @return bool
*/
public function isDeprecated()
{
return $this->deprecated;
}
/**
* Message to use if this definition is deprecated.
*
* @param string $id Service id relying on this definition
*
* @return string
*/
public function getDeprecationMessage($id)
{
return str_replace('%service_id%', $id, $this->deprecationTemplate);
}
/**
* Sets a configurator to call after the service is fully initialized.
*
* @param callable $callable A PHP callable
*
* @return $this
*/
public function setConfigurator($callable)
{
$this->configurator = $callable;
return $this;
}
/**
* Gets the configurator to call after the service is fully initialized.
*
* @return callable|null The PHP callable to call
*/
public function getConfigurator()
{
return $this->configurator;
}
/**
* Sets types that will default to this definition.
*
* @param string[] $types
*
* @return $this
*/
public function setAutowiringTypes(array $types)
{
$this->autowiringTypes = array();
foreach ($types as $type) {
$this->autowiringTypes[$type] = true;
}
return $this;
}
/**
* Is the definition autowired?
*
* @return bool
*/
public function isAutowired()
{
return $this->autowired;
}
/**
* Sets autowired.
*
* @param bool $autowired
*
* @return $this
*/
public function setAutowired($autowired)
{
$this->autowired = $autowired;
return $this;
}
/**
* Gets autowiring types that will default to this definition.
*
* @return string[]
*/
public function getAutowiringTypes()
{
return array_keys($this->autowiringTypes);
}
/**
* Adds a type that will default to this definition.
*
* @param string $type
*
* @return $this
*/
public function addAutowiringType($type)
{
$this->autowiringTypes[$type] = true;
return $this;
}
/**
* Removes a type.
*
* @param string $type
*
* @return $this
*/
public function removeAutowiringType($type)
{
unset($this->autowiringTypes[$type]);
return $this;
}
/**
* Will this definition default for the given type?
*
* @param string $type
*
* @return bool
*/
public function hasAutowiringType($type)
{
return isset($this->autowiringTypes[$type]);
}
}

View file

@ -0,0 +1,229 @@
<?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;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
/**
* This definition decorates another definition.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class DefinitionDecorator extends Definition
{
private $parent;
private $changes = array();
/**
* @param string $parent The id of Definition instance to decorate
*/
public function __construct($parent)
{
parent::__construct();
$this->parent = $parent;
}
/**
* Returns the Definition being decorated.
*
* @return string
*/
public function getParent()
{
return $this->parent;
}
/**
* Returns all changes tracked for the Definition object.
*
* @return array An array of changes for this Definition
*/
public function getChanges()
{
return $this->changes;
}
/**
* {@inheritdoc}
*/
public function setClass($class)
{
$this->changes['class'] = true;
return parent::setClass($class);
}
/**
* {@inheritdoc}
*/
public function setFactory($callable)
{
$this->changes['factory'] = true;
return parent::setFactory($callable);
}
/**
* {@inheritdoc}
*/
public function setFactoryClass($class)
{
$this->changes['factory_class'] = true;
return parent::setFactoryClass($class);
}
/**
* {@inheritdoc}
*/
public function setFactoryMethod($method)
{
$this->changes['factory_method'] = true;
return parent::setFactoryMethod($method);
}
/**
* {@inheritdoc}
*/
public function setFactoryService($service, $triggerDeprecationError = true)
{
$this->changes['factory_service'] = true;
return parent::setFactoryService($service, $triggerDeprecationError);
}
/**
* {@inheritdoc}
*/
public function setConfigurator($callable)
{
$this->changes['configurator'] = true;
return parent::setConfigurator($callable);
}
/**
* {@inheritdoc}
*/
public function setFile($file)
{
$this->changes['file'] = true;
return parent::setFile($file);
}
/**
* {@inheritdoc}
*/
public function setPublic($boolean)
{
$this->changes['public'] = true;
return parent::setPublic($boolean);
}
/**
* {@inheritdoc}
*/
public function setLazy($boolean)
{
$this->changes['lazy'] = true;
return parent::setLazy($boolean);
}
/**
* {@inheritdoc}
*/
public function setDecoratedService($id, $renamedId = null, $priority = 0)
{
$this->changes['decorated_service'] = true;
return parent::setDecoratedService($id, $renamedId, $priority);
}
/**
* {@inheritdoc}
*/
public function setDeprecated($boolean = true, $template = null)
{
$this->changes['deprecated'] = true;
return parent::setDeprecated($boolean, $template);
}
/**
* {@inheritdoc}
*/
public function setAutowired($autowired)
{
$this->changes['autowire'] = true;
return parent::setAutowired($autowired);
}
/**
* Gets an argument to pass to the service constructor/factory method.
*
* If replaceArgument() has been used to replace an argument, this method
* will return the replacement value.
*
* @param int $index
*
* @return mixed The argument value
*
* @throws OutOfBoundsException When the argument does not exist
*/
public function getArgument($index)
{
if (array_key_exists('index_'.$index, $this->arguments)) {
return $this->arguments['index_'.$index];
}
$lastIndex = count(array_filter(array_keys($this->arguments), 'is_int')) - 1;
if ($index < 0 || $index > $lastIndex) {
throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, $lastIndex));
}
return $this->arguments[$index];
}
/**
* You should always use this method when overwriting existing arguments
* of the parent definition.
*
* If you directly call setArguments() keep in mind that you must follow
* certain conventions when you want to overwrite the arguments of the
* parent definition, otherwise your arguments will only be appended.
*
* @param int $index
* @param mixed $value
*
* @return $this
*
* @throws InvalidArgumentException when $index isn't an integer
*/
public function replaceArgument($index, $value)
{
if (!is_int($index)) {
throw new InvalidArgumentException('$index must be an integer.');
}
$this->arguments['index_'.$index] = $value;
return $this;
}
}

View file

@ -0,0 +1,32 @@
<?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\Dumper;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Dumper is the abstract class for all built-in dumpers.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Dumper implements DumperInterface
{
protected $container;
/**
* @param ContainerBuilder $container The service container to dump
*/
public function __construct(ContainerBuilder $container)
{
$this->container = $container;
}
}

View file

@ -0,0 +1,29 @@
<?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\Dumper;
/**
* DumperInterface is the interface implemented by service container dumper classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface DumperInterface
{
/**
* Dumps the service container.
*
* @param array $options An array of options
*
* @return string The representation of the service container
*/
public function dump(array $options = array());
}

View file

@ -0,0 +1,306 @@
<?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\Dumper;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Scope;
/**
* GraphvizDumper dumps a service container as a graphviz file.
*
* You can convert the generated dot file with the dot utility (http://www.graphviz.org/):
*
* dot -Tpng container.dot > foo.png
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class GraphvizDumper extends Dumper
{
private $nodes;
private $edges;
private $options = array(
'graph' => array('ratio' => 'compress'),
'node' => array('fontsize' => 11, 'fontname' => 'Arial', 'shape' => 'record'),
'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => 0.5),
'node.instance' => array('fillcolor' => '#9999ff', 'style' => 'filled'),
'node.definition' => array('fillcolor' => '#eeeeee'),
'node.missing' => array('fillcolor' => '#ff9999', 'style' => 'filled'),
);
/**
* Dumps the service container as a graphviz graph.
*
* Available options:
*
* * graph: The default options for the whole graph
* * node: The default options for nodes
* * edge: The default options for edges
* * node.instance: The default options for services that are defined directly by object instances
* * node.definition: The default options for services that are defined via service definition instances
* * node.missing: The default options for missing services
*
* @param array $options An array of options
*
* @return string The dot representation of the service container
*/
public function dump(array $options = array())
{
foreach (array('graph', 'node', 'edge', 'node.instance', 'node.definition', 'node.missing') as $key) {
if (isset($options[$key])) {
$this->options[$key] = array_merge($this->options[$key], $options[$key]);
}
}
$this->nodes = $this->findNodes();
$this->edges = array();
foreach ($this->container->getDefinitions() as $id => $definition) {
$this->edges[$id] = array_merge(
$this->findEdges($id, $definition->getArguments(), true, ''),
$this->findEdges($id, $definition->getProperties(), false, '')
);
foreach ($definition->getMethodCalls() as $call) {
$this->edges[$id] = array_merge(
$this->edges[$id],
$this->findEdges($id, $call[1], false, $call[0].'()')
);
}
}
return $this->startDot().$this->addNodes().$this->addEdges().$this->endDot();
}
/**
* Returns all nodes.
*
* @return string A string representation of all nodes
*/
private function addNodes()
{
$code = '';
foreach ($this->nodes as $id => $node) {
$aliases = $this->getAliases($id);
$code .= sprintf(" node_%s [label=\"%s\\n%s\\n\", shape=%s%s];\n", $this->dotize($id), $id.($aliases ? ' ('.implode(', ', $aliases).')' : ''), $node['class'], $this->options['node']['shape'], $this->addAttributes($node['attributes']));
}
return $code;
}
/**
* Returns all edges.
*
* @return string A string representation of all edges
*/
private function addEdges()
{
$code = '';
foreach ($this->edges as $id => $edges) {
foreach ($edges as $edge) {
$code .= sprintf(" node_%s -> node_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], $edge['required'] ? 'filled' : 'dashed');
}
}
return $code;
}
/**
* Finds all edges belonging to a specific service id.
*
* @param string $id The service id used to find edges
* @param array $arguments An array of arguments
* @param bool $required
* @param string $name
*
* @return array An array of edges
*/
private function findEdges($id, array $arguments, $required, $name)
{
$edges = array();
foreach ($arguments as $argument) {
if ($argument instanceof Parameter) {
$argument = $this->container->hasParameter($argument) ? $this->container->getParameter($argument) : null;
} elseif (is_string($argument) && preg_match('/^%([^%]+)%$/', $argument, $match)) {
$argument = $this->container->hasParameter($match[1]) ? $this->container->getParameter($match[1]) : null;
}
if ($argument instanceof Reference) {
if (!$this->container->has((string) $argument)) {
$this->nodes[(string) $argument] = array('name' => $name, 'required' => $required, 'class' => '', 'attributes' => $this->options['node.missing']);
}
$edges[] = array('name' => $name, 'required' => $required, 'to' => $argument);
} elseif (is_array($argument)) {
$edges = array_merge($edges, $this->findEdges($id, $argument, $required, $name));
}
}
return $edges;
}
/**
* Finds all nodes.
*
* @return array An array of all nodes
*/
private function findNodes()
{
$nodes = array();
$container = $this->cloneContainer();
foreach ($container->getDefinitions() as $id => $definition) {
$class = $definition->getClass();
if ('\\' === substr($class, 0, 1)) {
$class = substr($class, 1);
}
try {
$class = $this->container->getParameterBag()->resolveValue($class);
} catch (ParameterNotFoundException $e) {
}
$nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => array_merge($this->options['node.definition'], array('style' => $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope(false) ? 'filled' : 'dotted')));
$container->setDefinition($id, new Definition('stdClass'));
}
foreach ($container->getServiceIds() as $id) {
$service = $container->get($id);
if (array_key_exists($id, $container->getAliases())) {
continue;
}
if (!$container->hasDefinition($id)) {
$class = ('service_container' === $id) ? get_class($this->container) : get_class($service);
$nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => $this->options['node.instance']);
}
}
return $nodes;
}
private function cloneContainer()
{
$parameterBag = new ParameterBag($this->container->getParameterBag()->all());
$container = new ContainerBuilder($parameterBag);
$container->setDefinitions($this->container->getDefinitions());
$container->setAliases($this->container->getAliases());
$container->setResources($this->container->getResources());
foreach ($this->container->getScopes(false) as $scope => $parentScope) {
$container->addScope(new Scope($scope, $parentScope));
}
foreach ($this->container->getExtensions() as $extension) {
$container->registerExtension($extension);
}
return $container;
}
/**
* Returns the start dot.
*
* @return string The string representation of a start dot
*/
private function startDot()
{
return sprintf("digraph sc {\n %s\n node [%s];\n edge [%s];\n\n",
$this->addOptions($this->options['graph']),
$this->addOptions($this->options['node']),
$this->addOptions($this->options['edge'])
);
}
/**
* Returns the end dot.
*
* @return string
*/
private function endDot()
{
return "}\n";
}
/**
* Adds attributes.
*
* @param array $attributes An array of attributes
*
* @return string A comma separated list of attributes
*/
private function addAttributes(array $attributes)
{
$code = array();
foreach ($attributes as $k => $v) {
$code[] = sprintf('%s="%s"', $k, $v);
}
return $code ? ', '.implode(', ', $code) : '';
}
/**
* Adds options.
*
* @param array $options An array of options
*
* @return string A space separated list of options
*/
private function addOptions(array $options)
{
$code = array();
foreach ($options as $k => $v) {
$code[] = sprintf('%s="%s"', $k, $v);
}
return implode(' ', $code);
}
/**
* Dotizes an identifier.
*
* @param string $id The identifier to dotize
*
* @return string A dotized string
*/
private function dotize($id)
{
return strtolower(preg_replace('/\W/i', '_', $id));
}
/**
* Compiles an array of aliases for a specified service id.
*
* @param string $id A service id
*
* @return array An array of aliases
*/
private function getAliases($id)
{
$aliases = array();
foreach ($this->container->getAliases() as $alias => $origin) {
if ($id == $origin) {
$aliases[] = $alias;
}
}
return $aliases;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,380 @@
<?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\Dumper;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\ExpressionLanguage\Expression;
/**
* XmlDumper dumps a service container as an XML string.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Martin Hasoň <martin.hason@gmail.com>
*/
class XmlDumper extends Dumper
{
/**
* @var \DOMDocument
*/
private $document;
/**
* Dumps the service container as an XML string.
*
* @param array $options An array of options
*
* @return string An xml string representing of the service container
*/
public function dump(array $options = array())
{
$this->document = new \DOMDocument('1.0', 'utf-8');
$this->document->formatOutput = true;
$container = $this->document->createElementNS('http://symfony.com/schema/dic/services', 'container');
$container->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
$container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd');
$this->addParameters($container);
$this->addServices($container);
$this->document->appendChild($container);
$xml = $this->document->saveXML();
$this->document = null;
return $xml;
}
/**
* Adds parameters.
*
* @param \DOMElement $parent
*/
private function addParameters(\DOMElement $parent)
{
$data = $this->container->getParameterBag()->all();
if (!$data) {
return;
}
if ($this->container->isFrozen()) {
$data = $this->escape($data);
}
$parameters = $this->document->createElement('parameters');
$parent->appendChild($parameters);
$this->convertParameters($data, 'parameter', $parameters);
}
/**
* Adds method calls.
*
* @param array $methodcalls
* @param \DOMElement $parent
*/
private function addMethodCalls(array $methodcalls, \DOMElement $parent)
{
foreach ($methodcalls as $methodcall) {
$call = $this->document->createElement('call');
$call->setAttribute('method', $methodcall[0]);
if (count($methodcall[1])) {
$this->convertParameters($methodcall[1], 'argument', $call);
}
$parent->appendChild($call);
}
}
/**
* Adds a service.
*
* @param Definition $definition
* @param string $id
* @param \DOMElement $parent
*/
private function addService($definition, $id, \DOMElement $parent)
{
$service = $this->document->createElement('service');
if (null !== $id) {
$service->setAttribute('id', $id);
}
if ($class = $definition->getClass()) {
if ('\\' === substr($class, 0, 1)) {
$class = substr($class, 1);
}
$service->setAttribute('class', $class);
}
if ($definition->getFactoryMethod(false)) {
$service->setAttribute('factory-method', $definition->getFactoryMethod(false));
}
if ($definition->getFactoryClass(false)) {
$service->setAttribute('factory-class', $definition->getFactoryClass(false));
}
if ($definition->getFactoryService(false)) {
$service->setAttribute('factory-service', $definition->getFactoryService(false));
}
if (!$definition->isShared()) {
$service->setAttribute('shared', 'false');
}
if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope(false)) {
$service->setAttribute('scope', $scope);
}
if (!$definition->isPublic()) {
$service->setAttribute('public', 'false');
}
if ($definition->isSynthetic()) {
$service->setAttribute('synthetic', 'true');
}
if ($definition->isSynchronized(false)) {
$service->setAttribute('synchronized', 'true');
}
if ($definition->isLazy()) {
$service->setAttribute('lazy', 'true');
}
if (null !== $decorated = $definition->getDecoratedService()) {
list($decorated, $renamedId, $priority) = $decorated;
$service->setAttribute('decorates', $decorated);
if (null !== $renamedId) {
$service->setAttribute('decoration-inner-name', $renamedId);
}
if (0 !== $priority) {
$service->setAttribute('decoration-priority', $priority);
}
}
foreach ($definition->getTags() as $name => $tags) {
foreach ($tags as $attributes) {
$tag = $this->document->createElement('tag');
$tag->setAttribute('name', $name);
foreach ($attributes as $key => $value) {
$tag->setAttribute($key, $value);
}
$service->appendChild($tag);
}
}
if ($definition->getFile()) {
$file = $this->document->createElement('file');
$file->appendChild($this->document->createTextNode($definition->getFile()));
$service->appendChild($file);
}
if ($parameters = $definition->getArguments()) {
$this->convertParameters($parameters, 'argument', $service);
}
if ($parameters = $definition->getProperties()) {
$this->convertParameters($parameters, 'property', $service, 'name');
}
$this->addMethodCalls($definition->getMethodCalls(), $service);
if ($callable = $definition->getFactory()) {
$factory = $this->document->createElement('factory');
if (is_array($callable) && $callable[0] instanceof Definition) {
$this->addService($callable[0], null, $factory);
$factory->setAttribute('method', $callable[1]);
} elseif (is_array($callable)) {
$factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]);
$factory->setAttribute('method', $callable[1]);
} else {
$factory->setAttribute('function', $callable);
}
$service->appendChild($factory);
}
if ($definition->isDeprecated()) {
$deprecated = $this->document->createElement('deprecated');
$deprecated->appendChild($this->document->createTextNode($definition->getDeprecationMessage('%service_id%')));
$service->appendChild($deprecated);
}
if ($definition->isAutowired()) {
$service->setAttribute('autowire', 'true');
}
foreach ($definition->getAutowiringTypes() as $autowiringTypeValue) {
$autowiringType = $this->document->createElement('autowiring-type');
$autowiringType->appendChild($this->document->createTextNode($autowiringTypeValue));
$service->appendChild($autowiringType);
}
if ($callable = $definition->getConfigurator()) {
$configurator = $this->document->createElement('configurator');
if (is_array($callable) && $callable[0] instanceof Definition) {
$this->addService($callable[0], null, $configurator);
$configurator->setAttribute('method', $callable[1]);
} elseif (is_array($callable)) {
$configurator->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]);
$configurator->setAttribute('method', $callable[1]);
} else {
$configurator->setAttribute('function', $callable);
}
$service->appendChild($configurator);
}
$parent->appendChild($service);
}
/**
* Adds a service alias.
*
* @param string $alias
* @param Alias $id
* @param \DOMElement $parent
*/
private function addServiceAlias($alias, Alias $id, \DOMElement $parent)
{
$service = $this->document->createElement('service');
$service->setAttribute('id', $alias);
$service->setAttribute('alias', $id);
if (!$id->isPublic()) {
$service->setAttribute('public', 'false');
}
$parent->appendChild($service);
}
/**
* Adds services.
*
* @param \DOMElement $parent
*/
private function addServices(\DOMElement $parent)
{
$definitions = $this->container->getDefinitions();
if (!$definitions) {
return;
}
$services = $this->document->createElement('services');
foreach ($definitions as $id => $definition) {
$this->addService($definition, $id, $services);
}
$aliases = $this->container->getAliases();
foreach ($aliases as $alias => $id) {
while (isset($aliases[(string) $id])) {
$id = $aliases[(string) $id];
}
$this->addServiceAlias($alias, $id, $services);
}
$parent->appendChild($services);
}
/**
* Converts parameters.
*
* @param array $parameters
* @param string $type
* @param \DOMElement $parent
* @param string $keyAttribute
*/
private function convertParameters(array $parameters, $type, \DOMElement $parent, $keyAttribute = 'key')
{
$withKeys = array_keys($parameters) !== range(0, count($parameters) - 1);
foreach ($parameters as $key => $value) {
$element = $this->document->createElement($type);
if ($withKeys) {
$element->setAttribute($keyAttribute, $key);
}
if (is_array($value)) {
$element->setAttribute('type', 'collection');
$this->convertParameters($value, $type, $element, 'key');
} elseif ($value instanceof Reference) {
$element->setAttribute('type', 'service');
$element->setAttribute('id', (string) $value);
$behaviour = $value->getInvalidBehavior();
if ($behaviour == ContainerInterface::NULL_ON_INVALID_REFERENCE) {
$element->setAttribute('on-invalid', 'null');
} elseif ($behaviour == ContainerInterface::IGNORE_ON_INVALID_REFERENCE) {
$element->setAttribute('on-invalid', 'ignore');
}
if (!$value->isStrict(false)) {
$element->setAttribute('strict', 'false');
}
} elseif ($value instanceof Definition) {
$element->setAttribute('type', 'service');
$this->addService($value, null, $element);
} elseif ($value instanceof Expression) {
$element->setAttribute('type', 'expression');
$text = $this->document->createTextNode(self::phpToXml((string) $value));
$element->appendChild($text);
} else {
if (in_array($value, array('null', 'true', 'false'), true)) {
$element->setAttribute('type', 'string');
}
$text = $this->document->createTextNode(self::phpToXml($value));
$element->appendChild($text);
}
$parent->appendChild($element);
}
}
/**
* Escapes arguments.
*
* @param array $arguments
*
* @return array
*/
private function escape(array $arguments)
{
$args = array();
foreach ($arguments as $k => $v) {
if (is_array($v)) {
$args[$k] = $this->escape($v);
} elseif (is_string($v)) {
$args[$k] = str_replace('%', '%%', $v);
} else {
$args[$k] = $v;
}
}
return $args;
}
/**
* Converts php types to xml types.
*
* @param mixed $value Value to convert
*
* @return string
*
* @throws RuntimeException When trying to dump object or resource
*/
public static function phpToXml($value)
{
switch (true) {
case null === $value:
return 'null';
case true === $value:
return 'true';
case false === $value:
return 'false';
case $value instanceof Parameter:
return '%'.$value.'%';
case is_object($value) || is_resource($value):
throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
default:
return (string) $value;
}
}
}

View file

@ -0,0 +1,368 @@
<?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\Dumper;
use Symfony\Component\Yaml\Dumper as YmlDumper;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\ExpressionLanguage\Expression;
/**
* YamlDumper dumps a service container as a YAML string.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class YamlDumper extends Dumper
{
private $dumper;
/**
* Dumps the service container as an YAML string.
*
* @param array $options An array of options
*
* @return string A YAML string representing of the service container
*/
public function dump(array $options = array())
{
if (!class_exists('Symfony\Component\Yaml\Dumper')) {
throw new RuntimeException('Unable to dump the container as the Symfony Yaml Component is not installed.');
}
if (null === $this->dumper) {
$this->dumper = new YmlDumper();
}
return $this->addParameters()."\n".$this->addServices();
}
/**
* Adds a service.
*
* @param string $id
* @param Definition $definition
*
* @return string
*/
private function addService($id, $definition)
{
$code = " $id:\n";
if ($class = $definition->getClass()) {
if ('\\' === substr($class, 0, 1)) {
$class = substr($class, 1);
}
$code .= sprintf(" class: %s\n", $this->dumper->dump($class));
}
if (!$definition->isPublic()) {
$code .= " public: false\n";
}
$tagsCode = '';
foreach ($definition->getTags() as $name => $tags) {
foreach ($tags as $attributes) {
$att = array();
foreach ($attributes as $key => $value) {
$att[] = sprintf('%s: %s', $this->dumper->dump($key), $this->dumper->dump($value));
}
$att = $att ? ', '.implode(', ', $att) : '';
$tagsCode .= sprintf(" - { name: %s%s }\n", $this->dumper->dump($name), $att);
}
}
if ($tagsCode) {
$code .= " tags:\n".$tagsCode;
}
if ($definition->getFile()) {
$code .= sprintf(" file: %s\n", $this->dumper->dump($definition->getFile()));
}
if ($definition->isSynthetic()) {
$code .= sprintf(" synthetic: true\n");
}
if ($definition->isSynchronized(false)) {
$code .= sprintf(" synchronized: true\n");
}
if ($definition->isDeprecated()) {
$code .= sprintf(" deprecated: %s\n", $definition->getDeprecationMessage('%service_id%'));
}
if ($definition->isAutowired()) {
$code .= " autowire: true\n";
}
$autowiringTypesCode = '';
foreach ($definition->getAutowiringTypes() as $autowiringType) {
$autowiringTypesCode .= sprintf(" - %s\n", $this->dumper->dump($autowiringType));
}
if ($autowiringTypesCode) {
$code .= sprintf(" autowiring_types:\n%s", $autowiringTypesCode);
}
if ($definition->getFactoryClass(false)) {
$code .= sprintf(" factory_class: %s\n", $this->dumper->dump($definition->getFactoryClass(false)));
}
if ($definition->isLazy()) {
$code .= sprintf(" lazy: true\n");
}
if ($definition->getFactoryMethod(false)) {
$code .= sprintf(" factory_method: %s\n", $this->dumper->dump($definition->getFactoryMethod(false)));
}
if ($definition->getFactoryService(false)) {
$code .= sprintf(" factory_service: %s\n", $this->dumper->dump($definition->getFactoryService(false)));
}
if ($definition->getArguments()) {
$code .= sprintf(" arguments: %s\n", $this->dumper->dump($this->dumpValue($definition->getArguments()), 0));
}
if ($definition->getProperties()) {
$code .= sprintf(" properties: %s\n", $this->dumper->dump($this->dumpValue($definition->getProperties()), 0));
}
if ($definition->getMethodCalls()) {
$code .= sprintf(" calls:\n%s\n", $this->dumper->dump($this->dumpValue($definition->getMethodCalls()), 1, 12));
}
if (!$definition->isShared()) {
$code .= " shared: false\n";
}
if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope(false)) {
$code .= sprintf(" scope: %s\n", $this->dumper->dump($scope));
}
if (null !== $decorated = $definition->getDecoratedService()) {
list($decorated, $renamedId, $priority) = $decorated;
$code .= sprintf(" decorates: %s\n", $decorated);
if (null !== $renamedId) {
$code .= sprintf(" decoration_inner_name: %s\n", $renamedId);
}
if (0 !== $priority) {
$code .= sprintf(" decoration_priority: %s\n", $priority);
}
}
if ($callable = $definition->getFactory()) {
$code .= sprintf(" factory: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0));
}
if ($callable = $definition->getConfigurator()) {
$code .= sprintf(" configurator: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0));
}
return $code;
}
/**
* Adds a service alias.
*
* @param string $alias
* @param Alias $id
*
* @return string
*/
private function addServiceAlias($alias, $id)
{
if ($id->isPublic()) {
return sprintf(" %s: '@%s'\n", $alias, $id);
}
return sprintf(" %s:\n alias: %s\n public: false\n", $alias, $id);
}
/**
* Adds services.
*
* @return string
*/
private function addServices()
{
if (!$this->container->getDefinitions()) {
return '';
}
$code = "services:\n";
foreach ($this->container->getDefinitions() as $id => $definition) {
$code .= $this->addService($id, $definition);
}
$aliases = $this->container->getAliases();
foreach ($aliases as $alias => $id) {
while (isset($aliases[(string) $id])) {
$id = $aliases[(string) $id];
}
$code .= $this->addServiceAlias($alias, $id);
}
return $code;
}
/**
* Adds parameters.
*
* @return string
*/
private function addParameters()
{
if (!$this->container->getParameterBag()->all()) {
return '';
}
$parameters = $this->prepareParameters($this->container->getParameterBag()->all(), $this->container->isFrozen());
return $this->dumper->dump(array('parameters' => $parameters), 2);
}
/**
* Dumps callable to YAML format.
*
* @param callable $callable
*
* @return callable
*/
private function dumpCallable($callable)
{
if (is_array($callable)) {
if ($callable[0] instanceof Reference) {
$callable = array($this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]);
} else {
$callable = array($callable[0], $callable[1]);
}
}
return $callable;
}
/**
* Dumps the value to YAML format.
*
* @param mixed $value
*
* @return mixed
*
* @throws RuntimeException When trying to dump object or resource
*/
private function dumpValue($value)
{
if (is_array($value)) {
$code = array();
foreach ($value as $k => $v) {
$code[$k] = $this->dumpValue($v);
}
return $code;
} elseif ($value instanceof Reference) {
return $this->getServiceCall((string) $value, $value);
} elseif ($value instanceof Parameter) {
return $this->getParameterCall((string) $value);
} elseif ($value instanceof Expression) {
return $this->getExpressionCall((string) $value);
} elseif (is_object($value) || is_resource($value)) {
throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
}
return $value;
}
/**
* Gets the service call.
*
* @param string $id
* @param Reference $reference
*
* @return string
*/
private function getServiceCall($id, Reference $reference = null)
{
if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
return sprintf('@?%s', $id);
}
return sprintf('@%s', $id);
}
/**
* Gets parameter call.
*
* @param string $id
*
* @return string
*/
private function getParameterCall($id)
{
return sprintf('%%%s%%', $id);
}
private function getExpressionCall($expression)
{
return sprintf('@=%s', $expression);
}
/**
* Prepares parameters.
*
* @param array $parameters
* @param bool $escape
*
* @return array
*/
private function prepareParameters(array $parameters, $escape = true)
{
$filtered = array();
foreach ($parameters as $key => $value) {
if (is_array($value)) {
$value = $this->prepareParameters($value, $escape);
} elseif ($value instanceof Reference || is_string($value) && 0 === strpos($value, '@')) {
$value = '@'.$value;
}
$filtered[$key] = $value;
}
return $escape ? $this->escape($filtered) : $filtered;
}
/**
* Escapes arguments.
*
* @param array $arguments
*
* @return array
*/
private function escape(array $arguments)
{
$args = array();
foreach ($arguments as $k => $v) {
if (is_array($v)) {
$args[$k] = $this->escape($v);
} elseif (is_string($v)) {
$args[$k] = str_replace('%', '%%', $v);
} else {
$args[$k] = $v;
}
}
return $args;
}
}

View file

@ -0,0 +1,19 @@
<?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\Exception;
/**
* Base BadMethodCallException for Dependency Injection component.
*/
class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface
{
}

View file

@ -0,0 +1,22 @@
<?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\Exception;
/**
* Base ExceptionInterface for Dependency Injection component.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bulat Shakirzyanov <bulat@theopenskyproject.com>
*/
interface ExceptionInterface
{
}

View file

@ -0,0 +1,41 @@
<?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\Exception;
/**
* This exception is thrown when you try to create a service of an inactive scope.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class InactiveScopeException extends RuntimeException
{
private $serviceId;
private $scope;
public function __construct($serviceId, $scope, \Exception $previous = null)
{
parent::__construct(sprintf('You cannot create a service ("%s") of an inactive scope ("%s").', $serviceId, $scope), 0, $previous);
$this->serviceId = $serviceId;
$this->scope = $scope;
}
public function getServiceId()
{
return $this->serviceId;
}
public function getScope()
{
return $this->scope;
}
}

View file

@ -0,0 +1,21 @@
<?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\Exception;
/**
* Base InvalidArgumentException for Dependency Injection component.
*
* @author Bulat Shakirzyanov <bulat@theopenskyproject.com>
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View file

@ -0,0 +1,19 @@
<?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\Exception;
/**
* Base LogicException for Dependency Injection component.
*/
class LogicException extends \LogicException implements ExceptionInterface
{
}

View file

@ -0,0 +1,19 @@
<?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\Exception;
/**
* Base OutOfBoundsException for Dependency Injection component.
*/
class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
{
}

View file

@ -0,0 +1,34 @@
<?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\Exception;
/**
* This exception is thrown when a circular reference in a parameter is detected.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ParameterCircularReferenceException extends RuntimeException
{
private $parameters;
public function __construct($parameters, \Exception $previous = null)
{
parent::__construct(sprintf('Circular reference detected for parameter "%s" ("%s" > "%s").', $parameters[0], implode('" > "', $parameters), $parameters[0]), 0, $previous);
$this->parameters = $parameters;
}
public function getParameters()
{
return $this->parameters;
}
}

View file

@ -0,0 +1,93 @@
<?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\Exception;
/**
* This exception is thrown when a non-existent parameter is used.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ParameterNotFoundException extends InvalidArgumentException
{
private $key;
private $sourceId;
private $sourceKey;
private $alternatives;
/**
* @param string $key The requested parameter key
* @param string $sourceId The service id that references the non-existent parameter
* @param string $sourceKey The parameter key that references the non-existent parameter
* @param \Exception $previous The previous exception
* @param string[] $alternatives Some parameter name alternatives
*/
public function __construct($key, $sourceId = null, $sourceKey = null, \Exception $previous = null, array $alternatives = array())
{
$this->key = $key;
$this->sourceId = $sourceId;
$this->sourceKey = $sourceKey;
$this->alternatives = $alternatives;
parent::__construct('', 0, $previous);
$this->updateRepr();
}
public function updateRepr()
{
if (null !== $this->sourceId) {
$this->message = sprintf('The service "%s" has a dependency on a non-existent parameter "%s".', $this->sourceId, $this->key);
} elseif (null !== $this->sourceKey) {
$this->message = sprintf('The parameter "%s" has a dependency on a non-existent parameter "%s".', $this->sourceKey, $this->key);
} else {
$this->message = sprintf('You have requested a non-existent parameter "%s".', $this->key);
}
if ($this->alternatives) {
if (1 == count($this->alternatives)) {
$this->message .= ' Did you mean this: "';
} else {
$this->message .= ' Did you mean one of these: "';
}
$this->message .= implode('", "', $this->alternatives).'"?';
}
}
public function getKey()
{
return $this->key;
}
public function getSourceId()
{
return $this->sourceId;
}
public function getSourceKey()
{
return $this->sourceKey;
}
public function setSourceId($sourceId)
{
$this->sourceId = $sourceId;
$this->updateRepr();
}
public function setSourceKey($sourceKey)
{
$this->sourceKey = $sourceKey;
$this->updateRepr();
}
}

View file

@ -0,0 +1,21 @@
<?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\Exception;
/**
* Base RuntimeException for Dependency Injection component.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View file

@ -0,0 +1,65 @@
<?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\Exception;
/**
* This exception is thrown when the a scope crossing injection is detected.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ScopeCrossingInjectionException extends RuntimeException
{
private $sourceServiceId;
private $sourceScope;
private $destServiceId;
private $destScope;
public function __construct($sourceServiceId, $sourceScope, $destServiceId, $destScope, \Exception $previous = null)
{
parent::__construct(sprintf(
'Scope Crossing Injection detected: The definition "%s" references the service "%s" which belongs to another scope hierarchy. '
.'This service might not be available consistently. Generally, it is safer to either move the definition "%s" to scope "%s", or '
.'declare "%s" as a child scope of "%s". If you can be sure that the other scope is always active, you can set the reference to strict=false to get rid of this error.',
$sourceServiceId,
$destServiceId,
$sourceServiceId,
$destScope,
$sourceScope,
$destScope
), 0, $previous);
$this->sourceServiceId = $sourceServiceId;
$this->sourceScope = $sourceScope;
$this->destServiceId = $destServiceId;
$this->destScope = $destScope;
}
public function getSourceServiceId()
{
return $this->sourceServiceId;
}
public function getSourceScope()
{
return $this->sourceScope;
}
public function getDestServiceId()
{
return $this->destServiceId;
}
public function getDestScope()
{
return $this->destScope;
}
}

View file

@ -0,0 +1,64 @@
<?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\Exception;
/**
* Thrown when a scope widening injection is detected.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ScopeWideningInjectionException extends RuntimeException
{
private $sourceServiceId;
private $sourceScope;
private $destServiceId;
private $destScope;
public function __construct($sourceServiceId, $sourceScope, $destServiceId, $destScope, \Exception $previous = null)
{
parent::__construct(sprintf(
'Scope Widening Injection detected: The definition "%s" references the service "%s" which belongs to a narrower scope. '
.'Generally, it is safer to either move "%s" to scope "%s" or alternatively rely on the provider pattern by injecting the container itself, and requesting the service "%s" each time it is needed. '
.'In rare, special cases however that might not be necessary, then you can set the reference to strict=false to get rid of this error.',
$sourceServiceId,
$destServiceId,
$sourceServiceId,
$destScope,
$destServiceId
), 0, $previous);
$this->sourceServiceId = $sourceServiceId;
$this->sourceScope = $sourceScope;
$this->destServiceId = $destServiceId;
$this->destScope = $destScope;
}
public function getSourceServiceId()
{
return $this->sourceServiceId;
}
public function getSourceScope()
{
return $this->sourceScope;
}
public function getDestServiceId()
{
return $this->destServiceId;
}
public function getDestScope()
{
return $this->destScope;
}
}

View file

@ -0,0 +1,41 @@
<?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\Exception;
/**
* This exception is thrown when a circular reference is detected.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ServiceCircularReferenceException extends RuntimeException
{
private $serviceId;
private $path;
public function __construct($serviceId, array $path, \Exception $previous = null)
{
parent::__construct(sprintf('Circular reference detected for service "%s", path: "%s".', $serviceId, implode(' -> ', $path)), 0, $previous);
$this->serviceId = $serviceId;
$this->path = $path;
}
public function getServiceId()
{
return $this->serviceId;
}
public function getPath()
{
return $this->path;
}
}

View file

@ -0,0 +1,56 @@
<?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\Exception;
/**
* This exception is thrown when a non-existent service is requested.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ServiceNotFoundException extends InvalidArgumentException
{
private $id;
private $sourceId;
public function __construct($id, $sourceId = null, \Exception $previous = null, array $alternatives = array())
{
if (null === $sourceId) {
$msg = sprintf('You have requested a non-existent service "%s".', $id);
} else {
$msg = sprintf('The service "%s" has a dependency on a non-existent service "%s".', $sourceId, $id);
}
if ($alternatives) {
if (1 == count($alternatives)) {
$msg .= ' Did you mean this: "';
} else {
$msg .= ' Did you mean one of these: "';
}
$msg .= implode('", "', $alternatives).'"?';
}
parent::__construct($msg, 0, $previous);
$this->id = $id;
$this->sourceId = $sourceId;
}
public function getId()
{
return $this->id;
}
public function getSourceId()
{
return $this->sourceId;
}
}

View file

@ -0,0 +1,33 @@
<?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;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage;
use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface;
/**
* Adds some function to the default ExpressionLanguage.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @see ExpressionLanguageProvider
*/
class ExpressionLanguage extends BaseExpressionLanguage
{
public function __construct(ParserCacheInterface $cache = null, array $providers = array())
{
// prepend the default provider to let users override it easily
array_unshift($providers, new ExpressionLanguageProvider());
parent::__construct($cache, $providers);
}
}

View file

@ -0,0 +1,43 @@
<?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;
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
/**
* Define some ExpressionLanguage functions.
*
* To get a service, use service('request').
* To get a parameter, use parameter('kernel.debug').
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface
{
public function getFunctions()
{
return array(
new ExpressionFunction('service', function ($arg) {
return sprintf('$this->get(%s)', $arg);
}, function (array $variables, $value) {
return $variables['container']->get($value);
}),
new ExpressionFunction('parameter', function ($arg) {
return sprintf('$this->getParameter(%s)', $arg);
}, function (array $variables, $value) {
return $variables['container']->getParameter($value);
}),
);
}
}

View file

@ -0,0 +1,33 @@
<?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\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* ConfigurationExtensionInterface is the interface implemented by container extension classes.
*
* @author Kevin Bond <kevinbond@gmail.com>
*/
interface ConfigurationExtensionInterface
{
/**
* Returns extension configuration.
*
* @param array $config An array of configuration values
* @param ContainerBuilder $container A ContainerBuilder instance
*
* @return ConfigurationInterface|null The configuration or null
*/
public function getConfiguration(array $config, ContainerBuilder $container);
}

View file

@ -0,0 +1,118 @@
<?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\Extension;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* Provides useful features shared by many extensions.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface
{
/**
* {@inheritdoc}
*/
public function getXsdValidationBasePath()
{
return false;
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return 'http://example.org/schema/dic/'.$this->getAlias();
}
/**
* Returns the recommended alias to use in XML.
*
* This alias is also the mandatory prefix to use when using YAML.
*
* This convention is to remove the "Extension" postfix from the class
* name and then lowercase and underscore the result. So:
*
* AcmeHelloExtension
*
* becomes
*
* acme_hello
*
* This can be overridden in a sub-class to specify the alias manually.
*
* @return string The alias
*
* @throws BadMethodCallException When the extension name does not follow conventions
*/
public function getAlias()
{
$className = get_class($this);
if (substr($className, -9) != 'Extension') {
throw new BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.');
}
$classBaseName = substr(strrchr($className, '\\'), 1, -9);
return Container::underscore($classBaseName);
}
/**
* {@inheritdoc}
*/
public function getConfiguration(array $config, ContainerBuilder $container)
{
$reflected = new \ReflectionClass($this);
$namespace = $reflected->getNamespaceName();
$class = $namespace.'\\Configuration';
if (class_exists($class)) {
$r = new \ReflectionClass($class);
$container->addResource(new FileResource($r->getFileName()));
if (!method_exists($class, '__construct')) {
return new $class();
}
}
}
final protected function processConfiguration(ConfigurationInterface $configuration, array $configs)
{
$processor = new Processor();
return $processor->processConfiguration($configuration, $configs);
}
/**
* @param ContainerBuilder $container
* @param array $config
*
* @return bool Whether the configuration is enabled
*
* @throws InvalidArgumentException When the config is not enableable
*/
protected function isConfigEnabled(ContainerBuilder $container, array $config)
{
if (!array_key_exists('enabled', $config)) {
throw new InvalidArgumentException("The config array has no 'enabled' key.");
}
return (bool) $container->getParameterBag()->resolveValue($config['enabled']);
}
}

View file

@ -0,0 +1,55 @@
<?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\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* ExtensionInterface is the interface implemented by container extension classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ExtensionInterface
{
/**
* Loads a specific configuration.
*
* @param array $configs An array of configuration values
* @param ContainerBuilder $container A ContainerBuilder instance
*
* @throws \InvalidArgumentException When provided tag is not defined in this extension
*/
public function load(array $configs, ContainerBuilder $container);
/**
* Returns the namespace to be used for this extension (XML namespace).
*
* @return string The XML namespace
*/
public function getNamespace();
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath();
/**
* Returns the recommended alias to use in XML.
*
* This alias is also the mandatory prefix to use when using YAML.
*
* @return string The alias
*/
public function getAlias();
}

View file

@ -0,0 +1,24 @@
<?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\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
interface PrependExtensionInterface
{
/**
* Allow an extension to prepend the extension configurations.
*
* @param ContainerBuilder $container
*/
public function prepend(ContainerBuilder $container);
}

View file

@ -0,0 +1,32 @@
<?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;
/**
* IntrospectableContainerInterface defines additional introspection functionality
* for containers, allowing logic to be implemented based on a Container's state.
*
* @author Evan Villemez <evillemez@gmail.com>
*
* @deprecated since version 2.8, to be merged with ContainerInterface in 3.0.
*/
interface IntrospectableContainerInterface extends ContainerInterface
{
/**
* Check for whether or not a service has been initialized.
*
* @param string $id
*
* @return bool true if the service has been initialized, false otherwise
*/
public function initialized($id);
}

View file

@ -0,0 +1,19 @@
Copyright (c) 2004-2017 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,37 @@
<?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\LazyProxy\Instantiator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
/**
* Lazy proxy instantiator, capable of instantiating a proxy given a container, the
* service definitions and a callback that produces the real service instance.
*
* @author Marco Pivetta <ocramius@gmail.com>
*/
interface InstantiatorInterface
{
/**
* Instantiates a proxy object.
*
* @param ContainerInterface $container the container from which the service is being requested
* @param Definition $definition the definition of the requested service
* @param string $id identifier of the requested service
* @param callable $realInstantiator zero-argument callback that is capable of producing the real
* service instance
*
* @return object
*/
public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator);
}

View file

@ -0,0 +1,33 @@
<?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\LazyProxy\Instantiator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
/**
* {@inheritdoc}
*
* Noop proxy instantiator - simply produces the real service instead of a proxy instance.
*
* @author Marco Pivetta <ocramius@gmail.com>
*/
class RealServiceInstantiator implements InstantiatorInterface
{
/**
* {@inheritdoc}
*/
public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator)
{
return call_user_func($realInstantiator);
}
}

View file

@ -0,0 +1,50 @@
<?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\LazyProxy\PhpDumper;
use Symfony\Component\DependencyInjection\Definition;
/**
* Lazy proxy dumper capable of generating the instantiation logic PHP code for proxied services.
*
* @author Marco Pivetta <ocramius@gmail.com>
*/
interface DumperInterface
{
/**
* Inspects whether the given definitions should produce proxy instantiation logic in the dumped container.
*
* @param Definition $definition
*
* @return bool
*/
public function isProxyCandidate(Definition $definition);
/**
* Generates the code to be used to instantiate a proxy in the dumped factory code.
*
* @param Definition $definition
* @param string $id service identifier
*
* @return string
*/
public function getProxyFactoryCode(Definition $definition, $id);
/**
* Generates the code for the lazy proxy.
*
* @param Definition $definition
*
* @return string
*/
public function getProxyCode(Definition $definition);
}

View file

@ -0,0 +1,46 @@
<?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\LazyProxy\PhpDumper;
use Symfony\Component\DependencyInjection\Definition;
/**
* Null dumper, negates any proxy code generation for any given service definition.
*
* @author Marco Pivetta <ocramius@gmail.com>
*/
class NullDumper implements DumperInterface
{
/**
* {@inheritdoc}
*/
public function isProxyCandidate(Definition $definition)
{
return false;
}
/**
* {@inheritdoc}
*/
public function getProxyFactoryCode(Definition $definition, $id)
{
return '';
}
/**
* {@inheritdoc}
*/
public function getProxyCode(Definition $definition)
{
return '';
}
}

View file

@ -0,0 +1,51 @@
<?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\Loader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\Loader\Loader;
/**
* ClosureLoader loads service definitions from a PHP closure.
*
* The Closure has access to the container as its first argument.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ClosureLoader extends Loader
{
private $container;
/**
* @param ContainerBuilder $container A ContainerBuilder instance
*/
public function __construct(ContainerBuilder $container)
{
$this->container = $container;
}
/**
* {@inheritdoc}
*/
public function load($resource, $type = null)
{
call_user_func($resource, $this->container);
}
/**
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
return $resource instanceof \Closure;
}
}

View file

@ -0,0 +1,56 @@
<?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\Loader;
use Symfony\Component\Config\Resource\DirectoryResource;
/**
* DirectoryLoader is a recursive loader to go through directories.
*
* @author Sebastien Lavoie <seb@wemakecustom.com>
*/
class DirectoryLoader extends FileLoader
{
/**
* {@inheritdoc}
*/
public function load($file, $type = null)
{
$file = rtrim($file, '/');
$path = $this->locator->locate($file);
$this->container->addResource(new DirectoryResource($path));
foreach (scandir($path) as $dir) {
if ('.' !== $dir[0]) {
if (is_dir($path.'/'.$dir)) {
$dir .= '/'; // append / to allow recursion
}
$this->setCurrentDir($path);
$this->import($dir, null, false, $path);
}
}
}
/**
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
if ('directory' === $type) {
return true;
}
return null === $type && is_string($resource) && '/' === substr($resource, -1);
}
}

View file

@ -0,0 +1,37 @@
<?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\Loader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader;
use Symfony\Component\Config\FileLocatorInterface;
/**
* FileLoader is the abstract class used by all built-in loaders that are file based.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class FileLoader extends BaseFileLoader
{
protected $container;
/**
* @param ContainerBuilder $container A ContainerBuilder instance
* @param FileLocatorInterface $locator A FileLocator instance
*/
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator)
{
$this->container = $container;
parent::__construct($locator);
}
}

View file

@ -0,0 +1,52 @@
<?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\Loader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
/**
* IniFileLoader loads parameters from INI files.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class IniFileLoader extends FileLoader
{
/**
* {@inheritdoc}
*/
public function load($resource, $type = null)
{
$path = $this->locator->locate($resource);
$this->container->addResource(new FileResource($path));
$result = parse_ini_file($path, true);
if (false === $result || array() === $result) {
throw new InvalidArgumentException(sprintf('The "%s" file is not valid.', $resource));
}
if (isset($result['parameters']) && is_array($result['parameters'])) {
foreach ($result['parameters'] as $key => $value) {
$this->container->setParameter($key, $value);
}
}
}
/**
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
return is_string($resource) && 'ini' === pathinfo($resource, PATHINFO_EXTENSION);
}
}

View file

@ -0,0 +1,49 @@
<?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\Loader;
use Symfony\Component\Config\Resource\FileResource;
/**
* PhpFileLoader loads service definitions from a PHP file.
*
* The PHP file is required and the $container variable can be
* used within the file to change the container.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class PhpFileLoader extends FileLoader
{
/**
* {@inheritdoc}
*/
public function load($resource, $type = null)
{
// the container and loader variables are exposed to the included file below
$container = $this->container;
$loader = $this;
$path = $this->locator->locate($resource);
$this->setCurrentDir(dirname($path));
$this->container->addResource(new FileResource($path));
include $path;
}
/**
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION);
}
}

View file

@ -0,0 +1,595 @@
<?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\Loader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\ExpressionLanguage\Expression;
/**
* XmlFileLoader loads XML files service definitions.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class XmlFileLoader extends FileLoader
{
const NS = 'http://symfony.com/schema/dic/services';
/**
* {@inheritdoc}
*/
public function load($resource, $type = null)
{
$path = $this->locator->locate($resource);
$xml = $this->parseFileToDOM($path);
$this->container->addResource(new FileResource($path));
// anonymous services
$this->processAnonymousServices($xml, $path);
// imports
$this->parseImports($xml, $path);
// parameters
$this->parseParameters($xml);
// extensions
$this->loadFromExtensions($xml);
// services
$this->parseDefinitions($xml, $path);
}
/**
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION);
}
/**
* Parses parameters.
*
* @param \DOMDocument $xml
*/
private function parseParameters(\DOMDocument $xml)
{
if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) {
$this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter'));
}
}
/**
* Parses imports.
*
* @param \DOMDocument $xml
* @param string $file
*/
private function parseImports(\DOMDocument $xml, $file)
{
$xpath = new \DOMXPath($xml);
$xpath->registerNamespace('container', self::NS);
if (false === $imports = $xpath->query('//container:imports/container:import')) {
return;
}
$defaultDirectory = dirname($file);
foreach ($imports as $import) {
$this->setCurrentDir($defaultDirectory);
$this->import($import->getAttribute('resource'), null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file);
}
}
/**
* Parses multiple definitions.
*
* @param \DOMDocument $xml
* @param string $file
*/
private function parseDefinitions(\DOMDocument $xml, $file)
{
$xpath = new \DOMXPath($xml);
$xpath->registerNamespace('container', self::NS);
if (false === $services = $xpath->query('//container:services/container:service')) {
return;
}
foreach ($services as $service) {
if (null !== $definition = $this->parseDefinition($service, $file)) {
$this->container->setDefinition((string) $service->getAttribute('id'), $definition);
}
}
}
/**
* Parses an individual Definition.
*
* @param \DOMElement $service
* @param string $file
*
* @return Definition|null
*/
private function parseDefinition(\DOMElement $service, $file)
{
if ($alias = $service->getAttribute('alias')) {
$public = true;
if ($publicAttr = $service->getAttribute('public')) {
$public = XmlUtils::phpize($publicAttr);
}
$this->container->setAlias((string) $service->getAttribute('id'), new Alias($alias, $public));
return;
}
if ($parent = $service->getAttribute('parent')) {
$definition = new DefinitionDecorator($parent);
} else {
$definition = new Definition();
}
foreach (array('class', 'shared', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'lazy', 'abstract') as $key) {
if ($value = $service->getAttribute($key)) {
if (in_array($key, array('factory-class', 'factory-method', 'factory-service'))) {
@trigger_error(sprintf('The "%s" attribute of service "%s" in file "%s" is deprecated since version 2.6 and will be removed in 3.0. Use the "factory" element instead.', $key, (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED);
}
$method = 'set'.str_replace('-', '', $key);
$definition->$method(XmlUtils::phpize($value));
}
}
if ($value = $service->getAttribute('autowire')) {
$definition->setAutowired(XmlUtils::phpize($value));
}
if ($value = $service->getAttribute('scope')) {
$triggerDeprecation = 'request' !== (string) $service->getAttribute('id');
if ($triggerDeprecation) {
@trigger_error(sprintf('The "scope" attribute of service "%s" in file "%s" is deprecated since version 2.8 and will be removed in 3.0.', (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED);
}
$definition->setScope(XmlUtils::phpize($value), false);
}
if ($value = $service->getAttribute('synchronized')) {
$triggerDeprecation = 'request' !== (string) $service->getAttribute('id');
if ($triggerDeprecation) {
@trigger_error(sprintf('The "synchronized" attribute of service "%s" in file "%s" is deprecated since version 2.7 and will be removed in 3.0.', (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED);
}
$definition->setSynchronized(XmlUtils::phpize($value), $triggerDeprecation);
}
if ($files = $this->getChildren($service, 'file')) {
$definition->setFile($files[0]->nodeValue);
}
if ($deprecated = $this->getChildren($service, 'deprecated')) {
$definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null);
}
$definition->setArguments($this->getArgumentsAsPhp($service, 'argument'));
$definition->setProperties($this->getArgumentsAsPhp($service, 'property'));
if ($factories = $this->getChildren($service, 'factory')) {
$factory = $factories[0];
if ($function = $factory->getAttribute('function')) {
$definition->setFactory($function);
} else {
$factoryService = $this->getChildren($factory, 'service');
if (isset($factoryService[0])) {
$class = $this->parseDefinition($factoryService[0], $file);
} elseif ($childService = $factory->getAttribute('service')) {
$class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false);
} else {
$class = $factory->getAttribute('class');
}
$definition->setFactory(array($class, $factory->getAttribute('method')));
}
}
if ($configurators = $this->getChildren($service, 'configurator')) {
$configurator = $configurators[0];
if ($function = $configurator->getAttribute('function')) {
$definition->setConfigurator($function);
} else {
$configuratorService = $this->getChildren($configurator, 'service');
if (isset($configuratorService[0])) {
$class = $this->parseDefinition($configuratorService[0], $file);
} elseif ($childService = $configurator->getAttribute('service')) {
$class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false);
} else {
$class = $configurator->getAttribute('class');
}
$definition->setConfigurator(array($class, $configurator->getAttribute('method')));
}
}
foreach ($this->getChildren($service, 'call') as $call) {
$definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument'));
}
foreach ($this->getChildren($service, 'tag') as $tag) {
$parameters = array();
foreach ($tag->attributes as $name => $node) {
if ('name' === $name) {
continue;
}
if (false !== strpos($name, '-') && false === strpos($name, '_') && !array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) {
$parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue);
}
// keep not normalized key for BC too
$parameters[$name] = XmlUtils::phpize($node->nodeValue);
}
if ('' === $tag->getAttribute('name')) {
throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', (string) $service->getAttribute('id'), $file));
}
$definition->addTag($tag->getAttribute('name'), $parameters);
}
foreach ($this->getChildren($service, 'autowiring-type') as $type) {
$definition->addAutowiringType($type->textContent);
}
if ($value = $service->getAttribute('decorates')) {
$renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null;
$priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0;
$definition->setDecoratedService($value, $renameId, $priority);
}
return $definition;
}
/**
* Parses a XML file to a \DOMDocument.
*
* @param string $file Path to a file
*
* @return \DOMDocument
*
* @throws InvalidArgumentException When loading of XML file returns error
*/
private function parseFileToDOM($file)
{
try {
$dom = XmlUtils::loadFile($file, array($this, 'validateSchema'));
} catch (\InvalidArgumentException $e) {
throw new InvalidArgumentException(sprintf('Unable to parse file "%s".', $file), $e->getCode(), $e);
}
$this->validateExtensions($dom, $file);
return $dom;
}
/**
* Processes anonymous services.
*
* @param \DOMDocument $xml
* @param string $file
*/
private function processAnonymousServices(\DOMDocument $xml, $file)
{
$definitions = array();
$count = 0;
$xpath = new \DOMXPath($xml);
$xpath->registerNamespace('container', self::NS);
// anonymous services as arguments/properties
if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) {
foreach ($nodes as $node) {
// give it a unique name
$id = sprintf('%s_%d', hash('sha256', $file), ++$count);
$node->setAttribute('id', $id);
if ($services = $this->getChildren($node, 'service')) {
$definitions[$id] = array($services[0], $file, false);
$services[0]->setAttribute('id', $id);
// anonymous services are always private
// we could not use the constant false here, because of XML parsing
$services[0]->setAttribute('public', 'false');
}
}
}
// anonymous services "in the wild"
if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) {
foreach ($nodes as $node) {
// give it a unique name
$id = sprintf('%s_%d', hash('sha256', $file), ++$count);
$node->setAttribute('id', $id);
$definitions[$id] = array($node, $file, true);
}
}
// resolve definitions
krsort($definitions);
foreach ($definitions as $id => $def) {
list($domElement, $file, $wild) = $def;
if (null !== $definition = $this->parseDefinition($domElement, $file)) {
$this->container->setDefinition($id, $definition);
}
if (true === $wild) {
$tmpDomElement = new \DOMElement('_services', null, self::NS);
$domElement->parentNode->replaceChild($tmpDomElement, $domElement);
$tmpDomElement->setAttribute('id', $id);
} else {
$domElement->parentNode->removeChild($domElement);
}
}
}
/**
* Returns arguments as valid php types.
*
* @param \DOMElement $node
* @param string $name
* @param bool $lowercase
*
* @return mixed
*/
private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true)
{
$arguments = array();
foreach ($this->getChildren($node, $name) as $arg) {
if ($arg->hasAttribute('name')) {
$arg->setAttribute('key', $arg->getAttribute('name'));
}
// this is used by DefinitionDecorator to overwrite a specific
// argument of the parent definition
if ($arg->hasAttribute('index')) {
$key = 'index_'.$arg->getAttribute('index');
} elseif (!$arg->hasAttribute('key')) {
// Append an empty argument, then fetch its key to overwrite it later
$arguments[] = null;
$keys = array_keys($arguments);
$key = array_pop($keys);
} else {
$key = $arg->getAttribute('key');
// parameter keys are case insensitive
if ('parameter' == $name && $lowercase) {
$key = strtolower($key);
}
}
switch ($arg->getAttribute('type')) {
case 'service':
$onInvalid = $arg->getAttribute('on-invalid');
$invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
if ('ignore' == $onInvalid) {
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
} elseif ('null' == $onInvalid) {
$invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
}
if ($strict = $arg->getAttribute('strict')) {
$strict = XmlUtils::phpize($strict);
} else {
$strict = true;
}
$arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior, $strict);
break;
case 'expression':
$arguments[$key] = new Expression($arg->nodeValue);
break;
case 'collection':
$arguments[$key] = $this->getArgumentsAsPhp($arg, $name, false);
break;
case 'string':
$arguments[$key] = $arg->nodeValue;
break;
case 'constant':
$arguments[$key] = constant(trim($arg->nodeValue));
break;
default:
$arguments[$key] = XmlUtils::phpize($arg->nodeValue);
}
}
return $arguments;
}
/**
* Get child elements by name.
*
* @param \DOMNode $node
* @param mixed $name
*
* @return array
*/
private function getChildren(\DOMNode $node, $name)
{
$children = array();
foreach ($node->childNodes as $child) {
if ($child instanceof \DOMElement && $child->localName === $name && $child->namespaceURI === self::NS) {
$children[] = $child;
}
}
return $children;
}
/**
* Validates a documents XML schema.
*
* @param \DOMDocument $dom
*
* @return bool
*
* @throws RuntimeException When extension references a non-existent XSD file
*/
public function validateSchema(\DOMDocument $dom)
{
$schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd'));
if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) {
$items = preg_split('/\s+/', $element);
for ($i = 0, $nb = count($items); $i < $nb; $i += 2) {
if (!$this->container->hasExtension($items[$i])) {
continue;
}
if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) {
$path = str_replace($extension->getNamespace(), str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]);
if (!is_file($path)) {
throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s"', get_class($extension), $path));
}
$schemaLocations[$items[$i]] = $path;
}
}
}
$tmpfiles = array();
$imports = '';
foreach ($schemaLocations as $namespace => $location) {
$parts = explode('/', $location);
if (0 === stripos($location, 'phar://')) {
$tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
if ($tmpfile) {
copy($location, $tmpfile);
$tmpfiles[] = $tmpfile;
$parts = explode('/', str_replace('\\', '/', $tmpfile));
}
}
$drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
$location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts));
$imports .= sprintf(' <xsd:import namespace="%s" schemaLocation="%s" />'."\n", $namespace, $location);
}
$source = <<<EOF
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns="http://symfony.com/schema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://symfony.com/schema"
elementFormDefault="qualified">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
$imports
</xsd:schema>
EOF
;
$disableEntities = libxml_disable_entity_loader(false);
$valid = @$dom->schemaValidateSource($source);
libxml_disable_entity_loader($disableEntities);
foreach ($tmpfiles as $tmpfile) {
@unlink($tmpfile);
}
return $valid;
}
/**
* Validates an extension.
*
* @param \DOMDocument $dom
* @param string $file
*
* @throws InvalidArgumentException When no extension is found corresponding to a tag
*/
private function validateExtensions(\DOMDocument $dom, $file)
{
foreach ($dom->documentElement->childNodes as $node) {
if (!$node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node->namespaceURI) {
continue;
}
// can it be handled by an extension?
if (!$this->container->hasExtension($node->namespaceURI)) {
$extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getNamespace(); }, $this->container->getExtensions()));
throw new InvalidArgumentException(sprintf(
'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s',
$node->tagName,
$file,
$node->namespaceURI,
$extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'
));
}
}
}
/**
* Loads from an extension.
*
* @param \DOMDocument $xml
*/
private function loadFromExtensions(\DOMDocument $xml)
{
foreach ($xml->documentElement->childNodes as $node) {
if (!$node instanceof \DOMElement || $node->namespaceURI === self::NS) {
continue;
}
$values = static::convertDomElementToArray($node);
if (!is_array($values)) {
$values = array();
}
$this->container->loadFromExtension($node->namespaceURI, $values);
}
}
/**
* Converts a \DomElement object to a PHP array.
*
* The following rules applies during the conversion:
*
* * Each tag is converted to a key value or an array
* if there is more than one "value"
*
* * The content of a tag is set under a "value" key (<foo>bar</foo>)
* if the tag also has some nested tags
*
* * The attributes are converted to keys (<foo foo="bar"/>)
*
* * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>)
*
* @param \DomElement $element A \DomElement instance
*
* @return array A PHP array
*/
public static function convertDomElementToArray(\DOMElement $element)
{
return XmlUtils::convertDomElementToArray($element);
}
}

View file

@ -0,0 +1,471 @@
<?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\Loader;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Parser as YamlParser;
use Symfony\Component\ExpressionLanguage\Expression;
/**
* YamlFileLoader loads YAML files service definitions.
*
* The YAML format does not support anonymous services (cf. the XML loader).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class YamlFileLoader extends FileLoader
{
private $yamlParser;
/**
* {@inheritdoc}
*/
public function load($resource, $type = null)
{
$path = $this->locator->locate($resource);
$content = $this->loadFile($path);
$this->container->addResource(new FileResource($path));
// empty file
if (null === $content) {
return;
}
// imports
$this->parseImports($content, $path);
// parameters
if (isset($content['parameters'])) {
if (!is_array($content['parameters'])) {
throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $resource));
}
foreach ($content['parameters'] as $key => $value) {
$this->container->setParameter($key, $this->resolveServices($value));
}
}
// extensions
$this->loadFromExtensions($content);
// services
$this->parseDefinitions($content, $resource);
}
/**
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
return is_string($resource) && in_array(pathinfo($resource, PATHINFO_EXTENSION), array('yml', 'yaml'), true);
}
/**
* Parses all imports.
*
* @param array $content
* @param string $file
*/
private function parseImports($content, $file)
{
if (!isset($content['imports'])) {
return;
}
if (!is_array($content['imports'])) {
throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in %s. Check your YAML syntax.', $file));
}
$defaultDirectory = dirname($file);
foreach ($content['imports'] as $import) {
if (!is_array($import)) {
throw new InvalidArgumentException(sprintf('The values in the "imports" key should be arrays in %s. Check your YAML syntax.', $file));
}
$this->setCurrentDir($defaultDirectory);
$this->import($import['resource'], null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
}
}
/**
* Parses definitions.
*
* @param array $content
* @param string $file
*/
private function parseDefinitions($content, $file)
{
if (!isset($content['services'])) {
return;
}
if (!is_array($content['services'])) {
throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file));
}
foreach ($content['services'] as $id => $service) {
$this->parseDefinition($id, $service, $file);
}
}
/**
* Parses a definition.
*
* @param string $id
* @param array $service
* @param string $file
*
* @throws InvalidArgumentException When tags are invalid
*/
private function parseDefinition($id, $service, $file)
{
if (is_string($service) && 0 === strpos($service, '@')) {
$this->container->setAlias($id, substr($service, 1));
return;
}
if (!is_array($service)) {
throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', gettype($service), $id, $file));
}
if (isset($service['alias'])) {
$public = !array_key_exists('public', $service) || (bool) $service['public'];
$this->container->setAlias($id, new Alias($service['alias'], $public));
return;
}
if (isset($service['parent'])) {
$definition = new DefinitionDecorator($service['parent']);
} else {
$definition = new Definition();
}
if (isset($service['class'])) {
$definition->setClass($service['class']);
}
if (isset($service['shared'])) {
$definition->setShared($service['shared']);
}
if (isset($service['scope'])) {
if ('request' !== $id) {
@trigger_error(sprintf('The "scope" key of service "%s" in file "%s" is deprecated since version 2.8 and will be removed in 3.0.', $id, $file), E_USER_DEPRECATED);
}
$definition->setScope($service['scope'], false);
}
if (isset($service['synthetic'])) {
$definition->setSynthetic($service['synthetic']);
}
if (isset($service['synchronized'])) {
@trigger_error(sprintf('The "synchronized" key of service "%s" in file "%s" is deprecated since version 2.7 and will be removed in 3.0.', $id, $file), E_USER_DEPRECATED);
$definition->setSynchronized($service['synchronized'], 'request' !== $id);
}
if (isset($service['lazy'])) {
$definition->setLazy($service['lazy']);
}
if (isset($service['public'])) {
$definition->setPublic($service['public']);
}
if (isset($service['abstract'])) {
$definition->setAbstract($service['abstract']);
}
if (array_key_exists('deprecated', $service)) {
$definition->setDeprecated(true, $service['deprecated']);
}
if (isset($service['factory'])) {
if (is_string($service['factory'])) {
if (strpos($service['factory'], ':') !== false && strpos($service['factory'], '::') === false) {
$parts = explode(':', $service['factory']);
$definition->setFactory(array($this->resolveServices('@'.$parts[0]), $parts[1]));
} else {
$definition->setFactory($service['factory']);
}
} else {
$definition->setFactory(array($this->resolveServices($service['factory'][0]), $service['factory'][1]));
}
}
if (isset($service['factory_class'])) {
@trigger_error(sprintf('The "factory_class" key of service "%s" in file "%s" is deprecated since version 2.6 and will be removed in 3.0. Use "factory" instead.', $id, $file), E_USER_DEPRECATED);
$definition->setFactoryClass($service['factory_class']);
}
if (isset($service['factory_method'])) {
@trigger_error(sprintf('The "factory_method" key of service "%s" in file "%s" is deprecated since version 2.6 and will be removed in 3.0. Use "factory" instead.', $id, $file), E_USER_DEPRECATED);
$definition->setFactoryMethod($service['factory_method']);
}
if (isset($service['factory_service'])) {
@trigger_error(sprintf('The "factory_service" key of service "%s" in file "%s" is deprecated since version 2.6 and will be removed in 3.0. Use "factory" instead.', $id, $file), E_USER_DEPRECATED);
$definition->setFactoryService($service['factory_service']);
}
if (isset($service['file'])) {
$definition->setFile($service['file']);
}
if (isset($service['arguments'])) {
$definition->setArguments($this->resolveServices($service['arguments']));
}
if (isset($service['properties'])) {
$definition->setProperties($this->resolveServices($service['properties']));
}
if (isset($service['configurator'])) {
if (is_string($service['configurator'])) {
$definition->setConfigurator($service['configurator']);
} else {
$definition->setConfigurator(array($this->resolveServices($service['configurator'][0]), $service['configurator'][1]));
}
}
if (isset($service['calls'])) {
if (!is_array($service['calls'])) {
throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
foreach ($service['calls'] as $call) {
if (isset($call['method'])) {
$method = $call['method'];
$args = isset($call['arguments']) ? $this->resolveServices($call['arguments']) : array();
} else {
$method = $call[0];
$args = isset($call[1]) ? $this->resolveServices($call[1]) : array();
}
$definition->addMethodCall($method, $args);
}
}
if (isset($service['tags'])) {
if (!is_array($service['tags'])) {
throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
foreach ($service['tags'] as $tag) {
if (!is_array($tag)) {
throw new InvalidArgumentException(sprintf('A "tags" entry must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
if (!isset($tag['name'])) {
throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file));
}
if (!is_string($tag['name']) || '' === $tag['name']) {
throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', $id, $file));
}
$name = $tag['name'];
unset($tag['name']);
foreach ($tag as $attribute => $value) {
if (!is_scalar($value) && null !== $value) {
throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.', $id, $name, $attribute, $file));
}
}
$definition->addTag($name, $tag);
}
}
if (isset($service['decorates'])) {
if ('' !== $service['decorates'] && '@' === $service['decorates'][0]) {
throw new InvalidArgumentException(sprintf('The value of the "decorates" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['decorates'], substr($service['decorates'], 1)));
}
$renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null;
$priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0;
$definition->setDecoratedService($service['decorates'], $renameId, $priority);
}
if (isset($service['autowire'])) {
$definition->setAutowired($service['autowire']);
}
if (isset($service['autowiring_types'])) {
if (is_string($service['autowiring_types'])) {
$definition->addAutowiringType($service['autowiring_types']);
} else {
if (!is_array($service['autowiring_types'])) {
throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
foreach ($service['autowiring_types'] as $autowiringType) {
if (!is_string($autowiringType)) {
throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
$definition->addAutowiringType($autowiringType);
}
}
}
$this->container->setDefinition($id, $definition);
}
/**
* Loads a YAML file.
*
* @param string $file
*
* @return array The file content
*
* @throws InvalidArgumentException when the given file is not a local file or when it does not exist
*/
protected function loadFile($file)
{
if (!class_exists('Symfony\Component\Yaml\Parser')) {
throw new RuntimeException('Unable to load YAML config files as the Symfony Yaml Component is not installed.');
}
if (!stream_is_local($file)) {
throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file));
}
if (!file_exists($file)) {
throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file));
}
if (null === $this->yamlParser) {
$this->yamlParser = new YamlParser();
}
try {
$configuration = $this->yamlParser->parse(file_get_contents($file));
} catch (ParseException $e) {
throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $file), 0, $e);
}
return $this->validate($configuration, $file);
}
/**
* Validates a YAML file.
*
* @param mixed $content
* @param string $file
*
* @return array
*
* @throws InvalidArgumentException When service file is not valid
*/
private function validate($content, $file)
{
if (null === $content) {
return $content;
}
if (!is_array($content)) {
throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file));
}
foreach ($content as $namespace => $data) {
if (in_array($namespace, array('imports', 'parameters', 'services'))) {
continue;
}
if (!$this->container->hasExtension($namespace)) {
$extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions()));
throw new InvalidArgumentException(sprintf(
'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s',
$namespace,
$file,
$namespace,
$extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'
));
}
}
return $content;
}
/**
* Resolves services.
*
* @param string|array $value
*
* @return array|string|Reference
*/
private function resolveServices($value)
{
if (is_array($value)) {
$value = array_map(array($this, 'resolveServices'), $value);
} elseif (is_string($value) && 0 === strpos($value, '@=')) {
return new Expression(substr($value, 2));
} elseif (is_string($value) && 0 === strpos($value, '@')) {
if (0 === strpos($value, '@@')) {
$value = substr($value, 1);
$invalidBehavior = null;
} elseif (0 === strpos($value, '@?')) {
$value = substr($value, 2);
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
} else {
$value = substr($value, 1);
$invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
}
if ('=' === substr($value, -1)) {
$value = substr($value, 0, -1);
$strict = false;
} else {
$strict = true;
}
if (null !== $invalidBehavior) {
$value = new Reference($value, $invalidBehavior, $strict);
}
}
return $value;
}
/**
* Loads from Extensions.
*
* @param array $content
*/
private function loadFromExtensions($content)
{
foreach ($content as $namespace => $values) {
if (in_array($namespace, array('imports', 'parameters', 'services'))) {
continue;
}
if (!is_array($values)) {
$values = array();
}
$this->container->loadFromExtension($namespace, $values);
}
}
}

View file

@ -0,0 +1,212 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://symfony.com/schema/dic/services"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://symfony.com/schema/dic/services"
elementFormDefault="qualified">
<xsd:annotation>
<xsd:documentation><![CDATA[
Symfony XML Services Schema, version 1.0
Authors: Fabien Potencier
This defines a way to describe PHP objects (services) and their
dependencies.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="container" type="container" />
<xsd:complexType name="container">
<xsd:annotation>
<xsd:documentation><![CDATA[
The root element of a service file.
]]></xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:group ref="foreign" />
<xsd:sequence minOccurs="0">
<xsd:element name="imports" type="imports" />
<xsd:group ref="foreign" />
</xsd:sequence>
<xsd:sequence minOccurs="0">
<xsd:element name="parameters" type="parameters" />
<xsd:group ref="foreign" />
</xsd:sequence>
<xsd:sequence minOccurs="0">
<xsd:element name="services" type="services" />
<xsd:group ref="foreign" />
</xsd:sequence>
</xsd:sequence>
</xsd:complexType>
<xsd:group name="foreign">
<xsd:sequence>
<xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:group>
<xsd:complexType name="services">
<xsd:annotation>
<xsd:documentation><![CDATA[
Enclosing element for the definition of all services
]]></xsd:documentation>
</xsd:annotation>
<xsd:choice minOccurs="1" maxOccurs="unbounded">
<xsd:element name="service" type="service" />
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="imports">
<xsd:annotation>
<xsd:documentation><![CDATA[
Enclosing element for the import elements
]]></xsd:documentation>
</xsd:annotation>
<xsd:choice minOccurs="1" maxOccurs="unbounded">
<xsd:element name="import" type="import" />
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="import">
<xsd:annotation>
<xsd:documentation><![CDATA[
Import an external resource defining other services or parameters
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="resource" type="xsd:string" use="required" />
<xsd:attribute name="ignore-errors" type="boolean" />
</xsd:complexType>
<xsd:complexType name="callable">
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element name="service" type="service" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="service" type="xsd:string" />
<xsd:attribute name="class" type="xsd:string" />
<xsd:attribute name="method" type="xsd:string" />
<xsd:attribute name="function" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="service">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="file" type="xsd:string" minOccurs="0" maxOccurs="1" />
<xsd:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" />
<xsd:element name="factory" type="callable" minOccurs="0" maxOccurs="1" />
<xsd:element name="deprecated" type="xsd:string" minOccurs="0" maxOccurs="1" />
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="autowiring-type" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="class" type="xsd:string" />
<xsd:attribute name="shared" type="boolean" />
<xsd:attribute name="scope" type="xsd:string" />
<xsd:attribute name="public" type="boolean" />
<xsd:attribute name="synthetic" type="boolean" />
<xsd:attribute name="synchronized" type="boolean" />
<xsd:attribute name="lazy" type="boolean" />
<xsd:attribute name="abstract" type="boolean" />
<xsd:attribute name="factory-class" type="xsd:string" />
<xsd:attribute name="factory-method" type="xsd:string" />
<xsd:attribute name="factory-service" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="parent" type="xsd:string" />
<xsd:attribute name="decorates" type="xsd:string" />
<xsd:attribute name="decoration-inner-name" type="xsd:string" />
<xsd:attribute name="decoration-priority" type="xsd:integer" />
<xsd:attribute name="autowire" type="boolean" />
</xsd:complexType>
<xsd:complexType name="tag">
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:anyAttribute namespace="##any" processContents="lax" />
</xsd:complexType>
<xsd:complexType name="parameters">
<xsd:choice minOccurs="1" maxOccurs="unbounded">
<xsd:element name="parameter" type="parameter" />
</xsd:choice>
<xsd:attribute name="type" type="parameter_type" />
<xsd:attribute name="key" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="parameter" mixed="true">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="parameter" type="parameter" />
</xsd:choice>
<xsd:attribute name="type" type="parameter_type" />
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="key" type="xsd:string" />
<xsd:attribute name="on-invalid" type="invalid_sequence" />
</xsd:complexType>
<xsd:complexType name="property" mixed="true">
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="service" type="service" />
</xsd:choice>
<xsd:attribute name="type" type="argument_type" />
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="key" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="on-invalid" type="invalid_sequence" />
<xsd:attribute name="strict" type="boolean" />
</xsd:complexType>
<xsd:complexType name="argument" mixed="true">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="service" type="service" />
</xsd:choice>
<xsd:attribute name="type" type="argument_type" />
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="key" type="xsd:string" />
<xsd:attribute name="index" type="xsd:integer" />
<xsd:attribute name="on-invalid" type="invalid_sequence" />
<xsd:attribute name="strict" type="boolean" />
</xsd:complexType>
<xsd:complexType name="call" mixed="true">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="service" type="service" />
</xsd:choice>
<xsd:attribute name="method" type="xsd:string" />
</xsd:complexType>
<xsd:simpleType name="parameter_type">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="collection" />
<xsd:enumeration value="string" />
<xsd:enumeration value="constant" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="argument_type">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="collection" />
<xsd:enumeration value="service" />
<xsd:enumeration value="expression" />
<xsd:enumeration value="string" />
<xsd:enumeration value="constant" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="invalid_sequence">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="null" />
<xsd:enumeration value="ignore" />
<xsd:enumeration value="exception" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="boolean">
<xsd:restriction base="xsd:string">
<xsd:pattern value="(%.+%|true|false)" />
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>

View file

@ -0,0 +1,38 @@
<?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;
/**
* Parameter represents a parameter reference.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Parameter
{
private $id;
/**
* @param string $id The parameter key
*/
public function __construct($id)
{
$this->id = $id;
}
/**
* @return string The parameter key
*/
public function __toString()
{
return (string) $this->id;
}
}

View file

@ -0,0 +1,68 @@
<?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\ParameterBag;
use Symfony\Component\DependencyInjection\Exception\LogicException;
/**
* Holds read-only parameters.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FrozenParameterBag extends ParameterBag
{
/**
* For performance reasons, the constructor assumes that
* all keys are already lowercased.
*
* This is always the case when used internally.
*
* @param array $parameters An array of parameters
*/
public function __construct(array $parameters = array())
{
$this->parameters = $parameters;
$this->resolved = true;
}
/**
* {@inheritdoc}
*/
public function clear()
{
throw new LogicException('Impossible to call clear() on a frozen ParameterBag.');
}
/**
* {@inheritdoc}
*/
public function add(array $parameters)
{
throw new LogicException('Impossible to call add() on a frozen ParameterBag.');
}
/**
* {@inheritdoc}
*/
public function set($name, $value)
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
/**
* {@inheritdoc}
*/
public function remove($name)
{
throw new LogicException('Impossible to call remove() on a frozen ParameterBag.');
}
}

View file

@ -0,0 +1,275 @@
<?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\ParameterBag;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/**
* Holds parameters.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ParameterBag implements ParameterBagInterface
{
protected $parameters = array();
protected $resolved = false;
/**
* @param array $parameters An array of parameters
*/
public function __construct(array $parameters = array())
{
$this->add($parameters);
}
/**
* Clears all parameters.
*/
public function clear()
{
$this->parameters = array();
}
/**
* Adds parameters to the service container parameters.
*
* @param array $parameters An array of parameters
*/
public function add(array $parameters)
{
foreach ($parameters as $key => $value) {
$this->parameters[strtolower($key)] = $value;
}
}
/**
* {@inheritdoc}
*/
public function all()
{
return $this->parameters;
}
/**
* {@inheritdoc}
*/
public function get($name)
{
$name = strtolower($name);
if (!array_key_exists($name, $this->parameters)) {
if (!$name) {
throw new ParameterNotFoundException($name);
}
$alternatives = array();
foreach ($this->parameters as $key => $parameterValue) {
$lev = levenshtein($name, $key);
if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) {
$alternatives[] = $key;
}
}
throw new ParameterNotFoundException($name, null, null, null, $alternatives);
}
return $this->parameters[$name];
}
/**
* Sets a service container parameter.
*
* @param string $name The parameter name
* @param mixed $value The parameter value
*/
public function set($name, $value)
{
$this->parameters[strtolower($name)] = $value;
}
/**
* {@inheritdoc}
*/
public function has($name)
{
return array_key_exists(strtolower($name), $this->parameters);
}
/**
* Removes a parameter.
*
* @param string $name The parameter name
*/
public function remove($name)
{
unset($this->parameters[strtolower($name)]);
}
/**
* {@inheritdoc}
*/
public function resolve()
{
if ($this->resolved) {
return;
}
$parameters = array();
foreach ($this->parameters as $key => $value) {
try {
$value = $this->resolveValue($value);
$parameters[$key] = $this->unescapeValue($value);
} catch (ParameterNotFoundException $e) {
$e->setSourceKey($key);
throw $e;
}
}
$this->parameters = $parameters;
$this->resolved = true;
}
/**
* Replaces parameter placeholders (%name%) by their values.
*
* @param mixed $value A value
* @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
*
* @return mixed The resolved value
*
* @throws ParameterNotFoundException if a placeholder references a parameter that does not exist
* @throws ParameterCircularReferenceException if a circular reference if detected
* @throws RuntimeException when a given parameter has a type problem.
*/
public function resolveValue($value, array $resolving = array())
{
if (is_array($value)) {
$args = array();
foreach ($value as $k => $v) {
$args[$this->resolveValue($k, $resolving)] = $this->resolveValue($v, $resolving);
}
return $args;
}
if (!is_string($value)) {
return $value;
}
return $this->resolveString($value, $resolving);
}
/**
* Resolves parameters inside a string.
*
* @param string $value The string to resolve
* @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
*
* @return string The resolved string
*
* @throws ParameterNotFoundException if a placeholder references a parameter that does not exist
* @throws ParameterCircularReferenceException if a circular reference if detected
* @throws RuntimeException when a given parameter has a type problem.
*/
public function resolveString($value, array $resolving = array())
{
// we do this to deal with non string values (Boolean, integer, ...)
// as the preg_replace_callback throw an exception when trying
// a non-string in a parameter value
if (preg_match('/^%([^%\s]+)%$/', $value, $match)) {
$key = strtolower($match[1]);
if (isset($resolving[$key])) {
throw new ParameterCircularReferenceException(array_keys($resolving));
}
$resolving[$key] = true;
return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving);
}
$self = $this;
return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($self, $resolving, $value) {
// skip %%
if (!isset($match[1])) {
return '%%';
}
$key = strtolower($match[1]);
if (isset($resolving[$key])) {
throw new ParameterCircularReferenceException(array_keys($resolving));
}
$resolved = $self->get($key);
if (!is_string($resolved) && !is_numeric($resolved)) {
throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type %s inside string value "%s".', $key, gettype($resolved), $value));
}
$resolved = (string) $resolved;
$resolving[$key] = true;
return $self->isResolved() ? $resolved : $self->resolveString($resolved, $resolving);
}, $value);
}
public function isResolved()
{
return $this->resolved;
}
/**
* {@inheritdoc}
*/
public function escapeValue($value)
{
if (is_string($value)) {
return str_replace('%', '%%', $value);
}
if (is_array($value)) {
$result = array();
foreach ($value as $k => $v) {
$result[$k] = $this->escapeValue($v);
}
return $result;
}
return $value;
}
/**
* {@inheritdoc}
*/
public function unescapeValue($value)
{
if (is_string($value)) {
return str_replace('%%', '%', $value);
}
if (is_array($value)) {
$result = array();
foreach ($value as $k => $v) {
$result[$k] = $this->unescapeValue($v);
}
return $result;
}
return $value;
}
}

View file

@ -0,0 +1,108 @@
<?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\ParameterBag;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
/**
* ParameterBagInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ParameterBagInterface
{
/**
* Clears all parameters.
*
* @throws LogicException if the ParameterBagInterface can not be cleared
*/
public function clear();
/**
* Adds parameters to the service container parameters.
*
* @param array $parameters An array of parameters
*
* @throws LogicException if the parameter can not be added
*/
public function add(array $parameters);
/**
* Gets the service container parameters.
*
* @return array An array of parameters
*/
public function all();
/**
* Gets a service container parameter.
*
* @param string $name The parameter name
*
* @return mixed The parameter value
*
* @throws ParameterNotFoundException if the parameter is not defined
*/
public function get($name);
/**
* Sets a service container parameter.
*
* @param string $name The parameter name
* @param mixed $value The parameter value
*
* @throws LogicException if the parameter can not be set
*/
public function set($name, $value);
/**
* Returns true if a parameter name is defined.
*
* @param string $name The parameter name
*
* @return bool true if the parameter name is defined, false otherwise
*/
public function has($name);
/**
* Replaces parameter placeholders (%name%) by their values for all parameters.
*/
public function resolve();
/**
* Replaces parameter placeholders (%name%) by their values.
*
* @param mixed $value A value
*
* @throws ParameterNotFoundException if a placeholder references a parameter that does not exist
*/
public function resolveValue($value);
/**
* Escape parameter placeholders %.
*
* @param mixed $value
*
* @return mixed
*/
public function escapeValue($value);
/**
* Unescape parameter placeholders %.
*
* @param mixed $value
*
* @return mixed
*/
public function unescapeValue($value);
}

View file

@ -0,0 +1,14 @@
DependencyInjection Component
=============================
The DependencyInjection component allows you to standardize and centralize the
way objects are constructed in your application.
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/dependency_injection/index.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)

View file

@ -0,0 +1,74 @@
<?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;
/**
* Reference represents a service reference.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Reference
{
private $id;
private $invalidBehavior;
private $strict;
/**
* Note: The $strict parameter is deprecated since version 2.8 and will be removed in 3.0.
*
* @param string $id The service identifier
* @param int $invalidBehavior The behavior when the service does not exist
* @param bool $strict Sets how this reference is validated
*
* @see Container
*/
public function __construct($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $strict = true)
{
$this->id = strtolower($id);
$this->invalidBehavior = $invalidBehavior;
$this->strict = $strict;
}
/**
* @return string The service identifier
*/
public function __toString()
{
return $this->id;
}
/**
* Returns the behavior to be used when the service does not exist.
*
* @return int
*/
public function getInvalidBehavior()
{
return $this->invalidBehavior;
}
/**
* Returns true when this Reference is strict.
*
* @return bool
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function isStrict($triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return $this->strict;
}
}

View file

@ -0,0 +1,31 @@
<?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;
/**
* ResettableContainerInterface defines additional resetting functionality
* for containers, allowing to release shared services when the container is
* not needed anymore.
*
* @author Christophe Coevoet <stof@notk.org>
*/
interface ResettableContainerInterface extends ContainerInterface
{
/**
* Resets shared services from the container.
*
* The container is not intended to be used again after being reset in a normal workflow. This method is
* meant as a way to release references for ref-counting.
* A subsequent call to ContainerInterface::get will recreate a new instance of the shared service.
*/
public function reset();
}

View file

@ -0,0 +1,41 @@
<?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;
/**
* Scope class.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
class Scope implements ScopeInterface
{
private $name;
private $parentName;
public function __construct($name, $parentName = ContainerInterface::SCOPE_CONTAINER)
{
$this->name = $name;
$this->parentName = $parentName;
}
public function getName()
{
return $this->name;
}
public function getParentName()
{
return $this->parentName;
}
}

View file

@ -0,0 +1,26 @@
<?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;
/**
* Scope Interface.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
interface ScopeInterface
{
public function getName();
public function getParentName();
}

View file

@ -0,0 +1,116 @@
<?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;
@trigger_error('The '.__NAMESPACE__.'\SimpleXMLElement class is deprecated since version 2.5 and will be removed in 3.0.', E_USER_DEPRECATED);
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\ExpressionLanguage\Expression;
/**
* SimpleXMLElement class.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since version 2.5, to be removed in 3.0.
*/
class SimpleXMLElement extends \SimpleXMLElement
{
/**
* Converts an attribute as a PHP type.
*
* @param string $name
*
* @return mixed
*/
public function getAttributeAsPhp($name)
{
return self::phpize($this[$name]);
}
/**
* Returns arguments as valid PHP types.
*
* @param string $name
* @param bool $lowercase
*
* @return mixed
*/
public function getArgumentsAsPhp($name, $lowercase = true)
{
$arguments = array();
foreach ($this->$name as $arg) {
if (isset($arg['name'])) {
$arg['key'] = (string) $arg['name'];
}
$key = isset($arg['key']) ? (string) $arg['key'] : (!$arguments ? 0 : max(array_keys($arguments)) + 1);
// parameter keys are case insensitive
if ('parameter' == $name && $lowercase) {
$key = strtolower($key);
}
// this is used by DefinitionDecorator to overwrite a specific
// argument of the parent definition
if (isset($arg['index'])) {
$key = 'index_'.$arg['index'];
}
switch ($arg['type']) {
case 'service':
$invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
if (isset($arg['on-invalid']) && 'ignore' == $arg['on-invalid']) {
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
} elseif (isset($arg['on-invalid']) && 'null' == $arg['on-invalid']) {
$invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
}
if (isset($arg['strict'])) {
$strict = self::phpize($arg['strict']);
} else {
$strict = true;
}
$arguments[$key] = new Reference((string) $arg['id'], $invalidBehavior, $strict);
break;
case 'expression':
$arguments[$key] = new Expression((string) $arg);
break;
case 'collection':
$arguments[$key] = $arg->getArgumentsAsPhp($name, false);
break;
case 'string':
$arguments[$key] = (string) $arg;
break;
case 'constant':
$arguments[$key] = constant((string) $arg);
break;
default:
$arguments[$key] = self::phpize($arg);
}
}
return $arguments;
}
/**
* Converts an xml value to a PHP type.
*
* @param mixed $value
*
* @return mixed
*/
public static function phpize($value)
{
return XmlUtils::phpize($value);
}
}

View file

@ -0,0 +1,29 @@
<?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;
/**
* TaggedContainerInterface is the interface implemented when a container knows how to deals with tags.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface TaggedContainerInterface extends ContainerInterface
{
/**
* Returns service ids for a given tag.
*
* @param string $name The tag name
*
* @return array An array of tags
*/
public function findTaggedServiceIds($name);
}

View file

@ -0,0 +1,48 @@
<?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;
/**
* Represents a variable.
*
* $var = new Variable('a');
*
* will be dumped as
*
* $a
*
* by the PHP dumper.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class Variable
{
private $name;
/**
* @param string $name
*/
public function __construct($name)
{
$this->name = $name;
}
/**
* Converts the object to a string.
*
* @return string
*/
public function __toString()
{
return $this->name;
}
}

View file

@ -0,0 +1,47 @@
{
"name": "symfony/dependency-injection",
"type": "library",
"description": "Symfony DependencyInjection Component",
"keywords": [],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/yaml": "~2.3.42|~2.7.14|~2.8.7|~3.0.7",
"symfony/config": "~2.2|~3.0.0",
"symfony/expression-language": "~2.6|~3.0.0"
},
"conflict": {
"symfony/expression-language": "<2.6"
},
"suggest": {
"symfony/yaml": "",
"symfony/config": "",
"symfony/expression-language": "For using expressions in service container configuration",
"symfony/proxy-manager-bridge": "Generate service proxies to lazy load them"
},
"autoload": {
"psr-4": { "Symfony\\Component\\DependencyInjection\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
}
}

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Symfony DependencyInjection Component Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>