Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions
core/vendor/symfony/dependency-injection
Alias.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.phpScope.phpScopeInterface.phpSimpleXMLElement.phpTaggedContainerInterface.php
Tests/Compiler

View file

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

View file

@ -0,0 +1,45 @@
CHANGELOG
=========
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,154 @@
<?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;
/**
* Constructor.
*
* @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,77 @@
<?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])) {
$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,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\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
*
* @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()) {
throw new RuntimeException(sprintf('A synthetic 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,166 @@
<?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();
$ancestors = array();
$scopes = $this->container->getScopes();
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->currentDefinition = $definition;
$this->currentScope = $scope = $definition->getScope();
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()) {
return;
}
if (null === $definition) {
return;
}
if ($this->currentScope === $scope = $definition->getScope()) {
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,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\ContainerBuilder;
/**
* This class is used to remove circular dependencies between individual passes.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @api
*/
class Compiler
{
private $passConfig;
private $log = array();
private $loggingFormatter;
private $serviceReferenceGraph;
/**
* Constructor.
*/
public function __construct()
{
$this->passConfig = new PassConfig();
$this->serviceReferenceGraph = new ServiceReferenceGraph();
$this->loggingFormatter = new LoggingFormatter();
}
/**
* Returns the PassConfig.
*
* @return PassConfig The PassConfig instance
*
* @api
*/
public function getPassConfig()
{
return $this->passConfig;
}
/**
* Returns the ServiceReferenceGraph.
*
* @return ServiceReferenceGraph The ServiceReferenceGraph instance
*
* @api
*/
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
*
* @api
*/
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
*
* @api
*/
public function compile(ContainerBuilder $container)
{
foreach ($this->passConfig->getPasses() as $pass) {
$pass->process($container);
}
}
}

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

View file

@ -0,0 +1,54 @@
<?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>
*/
class DecoratorServicePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
foreach ($container->getDefinitions() as $id => $definition) {
if (!$decorated = $definition->getDecoratedService()) {
continue;
}
$definition->setDecoratedService(null);
list($inner, $renamedId) = $decorated;
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 {
$definition = $container->getDefinition($inner);
$public = $definition->isPublic();
$definition->setPublic(false);
$container->setDefinition($renamedId, $definition);
}
$container->setAlias($inner, new Alias($id, $public));
}
}
}

View file

@ -0,0 +1,157 @@
<?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();
foreach ($container->getDefinitions() as $id => $definition) {
$this->currentId = $id;
$definition->setArguments(
$this->inlineArguments($container, $definition->getArguments())
);
$definition->setMethodCalls(
$this->inlineArguments($container, $definition->getMethodCalls())
);
$definition->setProperties(
$this->inlineArguments($container, $definition->getProperties())
);
$configurator = $this->inlineArguments($container, array($definition->getConfigurator()));
$definition->setConfigurator($configurator[0]);
$factory = $this->inlineArguments($container, array($definition->getFactory()));
$definition->setFactory($factory[0]);
}
}
/**
* Processes inline arguments.
*
* @param ContainerBuilder $container The ContainerBuilder
* @param array $arguments An array of arguments
*
* @return array
*/
private function inlineArguments(ContainerBuilder $container, array $arguments)
{
foreach ($arguments as $k => $argument) {
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 (ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope()) {
$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()));
}
}
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 (ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) {
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() === $definition->getScope();
}
}

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,256 @@
<?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>
*
* @api
*/
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;
/**
* Constructor.
*/
public function __construct()
{
$this->mergePass = new MergeExtensionConfigurationPass();
$this->optimizationPasses = array(
new ResolveDefinitionTemplatesPass(),
new DecoratorServicePass(),
new ResolveParameterPlaceHoldersPass(),
new CheckDefinitionValidityPass(),
new ResolveReferencesToAliasesPass(),
new ResolveInvalidReferencesPass(),
new AnalyzeServiceReferencesPass(true),
new CheckCircularReferencesPass(),
new CheckReferenceValidityPass(),
);
$this->removingPasses = array(
new RemovePrivateAliasesPass(),
new RemoveAbstractDefinitionsPass(),
new ReplaceAliasByActualDefinitionPass(),
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
*
* @api
*/
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
*
* @api
*/
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));
}
$passes = &$this->$property;
$passes[] = $pass;
}
/**
* Gets all passes for the AfterRemoving pass.
*
* @return array An array of passes
*
* @api
*/
public function getAfterRemovingPasses()
{
return $this->afterRemovingPasses;
}
/**
* Gets all passes for the BeforeOptimization pass.
*
* @return array An array of passes
*
* @api
*/
public function getBeforeOptimizationPasses()
{
return $this->beforeOptimizationPasses;
}
/**
* Gets all passes for the BeforeRemoving pass.
*
* @return array An array of passes
*
* @api
*/
public function getBeforeRemovingPasses()
{
return $this->beforeRemovingPasses;
}
/**
* Gets all passes for the Optimization pass.
*
* @return array An array of passes
*
* @api
*/
public function getOptimizationPasses()
{
return $this->optimizationPasses;
}
/**
* Gets all passes for the Removing pass.
*
* @return array An array of passes
*
* @api
*/
public function getRemovingPasses()
{
return $this->removingPasses;
}
/**
* Gets all passes for the Merge pass.
*
* @return array An array of passes
*
* @api
*/
public function getMergePass()
{
return $this->mergePass;
}
/**
* Sets the Merge Pass.
*
* @param CompilerPassInterface $pass The merge pass
*
* @api
*/
public function setMergePass(CompilerPassInterface $pass)
{
$this->mergePass = $pass;
}
/**
* Sets the AfterRemoving passes.
*
* @param array $passes An array of passes
*
* @api
*/
public function setAfterRemovingPasses(array $passes)
{
$this->afterRemovingPasses = $passes;
}
/**
* Sets the BeforeOptimization passes.
*
* @param array $passes An array of passes
*
* @api
*/
public function setBeforeOptimizationPasses(array $passes)
{
$this->beforeOptimizationPasses = $passes;
}
/**
* Sets the BeforeRemoving passes.
*
* @param array $passes An array of passes
*
* @api
*/
public function setBeforeRemovingPasses(array $passes)
{
$this->beforeRemovingPasses = $passes;
}
/**
* Sets the Optimization passes.
*
* @param array $passes An array of passes
*
* @api
*/
public function setOptimizationPasses(array $passes)
{
$this->optimizationPasses = $passes;
}
/**
* Sets the Removing passes.
*
* @param array $passes An array of passes
*
* @api
*/
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,88 @@
<?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;
/**
* Constructor.
*
* @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)
{
$this->repeat = false;
foreach ($this->passes as $pass) {
$pass->process($container);
}
if ($this->repeat) {
$this->process($container);
}
}
/**
* 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,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\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;
private $sourceId;
/**
* 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)
{
$this->compiler = $container->getCompiler();
$this->formatter = $this->compiler->getLoggingFormatter();
foreach ($container->getAliases() as $id => $alias) {
$aliasId = (string) $alias;
try {
$definition = $container->getDefinition($aliasId);
} catch (InvalidArgumentException $e) {
throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with "%s".', $alias, $id), null, $e);
}
if ($definition->isPublic()) {
continue;
}
$definition->setPublic(true);
$container->setDefinition($id, $definition);
$container->removeDefinition($aliasId);
$this->updateReferences($container, $aliasId, $id);
// we have to restart the process due to concurrent modification of
// the container
$this->process($container);
break;
}
}
/**
* Updates references to remove aliases.
*
* @param ContainerBuilder $container The container
* @param string $currentId The alias identifier being replaced
* @param string $newId The id of the service the alias points to
*/
private function updateReferences($container, $currentId, $newId)
{
foreach ($container->getAliases() as $id => $alias) {
if ($currentId === (string) $alias) {
$container->setAlias($id, $newId);
}
}
foreach ($container->getDefinitions() as $id => $definition) {
$this->sourceId = $id;
$definition->setArguments(
$this->updateArgumentReferences($definition->getArguments(), $currentId, $newId)
);
$definition->setMethodCalls(
$this->updateArgumentReferences($definition->getMethodCalls(), $currentId, $newId)
);
$definition->setProperties(
$this->updateArgumentReferences($definition->getProperties(), $currentId, $newId)
);
}
}
/**
* Updates argument references.
*
* @param array $arguments An array of Arguments
* @param string $currentId The alias identifier
* @param string $newId The identifier the alias points to
*
* @return array
*/
private function updateArgumentReferences(array $arguments, $currentId, $newId)
{
foreach ($arguments as $k => $argument) {
if (is_array($argument)) {
$arguments[$k] = $this->updateArgumentReferences($argument, $currentId, $newId);
} elseif ($argument instanceof Reference) {
if ($currentId === (string) $argument) {
$arguments[$k] = new Reference($newId, $argument->getInvalidBehavior());
$this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $this->sourceId, $currentId, $newId));
}
}
}
return $arguments;
}
}

View file

@ -0,0 +1,164 @@
<?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>
*/
class ResolveDefinitionTemplatesPass implements CompilerPassInterface
{
private $container;
private $compiler;
private $formatter;
/**
* Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$this->container = $container;
$this->compiler = $container->getCompiler();
$this->formatter = $this->compiler->getLoggingFormatter();
foreach ($container->getDefinitions() as $id => $definition) {
// yes, we are specifically fetching the definition from the
// container to ensure we are not operating on stale data
$definition = $container->getDefinition($id);
if (!$definition instanceof DefinitionDecorator || $definition->isAbstract()) {
continue;
}
$this->resolveDefinition($id, $definition);
}
}
/**
* Resolves the definition.
*
* @param string $id The definition identifier
* @param DefinitionDecorator $definition
*
* @return Definition
*
* @throws \RuntimeException When the definition is invalid
*/
private function resolveDefinition($id, DefinitionDecorator $definition)
{
if (!$this->container->hasDefinition($parent = $definition->getParent())) {
throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $id));
}
$parentDef = $this->container->getDefinition($parent);
if ($parentDef instanceof DefinitionDecorator) {
$parentDef = $this->resolveDefinition($parent, $parentDef);
}
$this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $id, $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());
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));
}
$def->setFactory($parentDef->getFactory());
$def->setConfigurator($parentDef->getConfigurator());
$def->setFile($parentDef->getFile());
$def->setPublic($parentDef->isPublic());
$def->setLazy($parentDef->isLazy());
// 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());
}
// 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));
}
// these attributes are always taken from the child
$def->setAbstract($definition->isAbstract());
$def->setScope($definition->getScope());
$def->setTags($definition->getTags());
// set new definition on container
$this->container->setDefinition($id, $def);
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 $ignore) {
// 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 $ignore) {
// 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()));
}
$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,99 @@
<?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()));
}
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());
}
}
}
return $arguments;
}
/**
* 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 The node matching the supplied identifier
*
* @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[] An array of all ServiceReferenceGraphNode objects
*/
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,70 @@
<?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;
/**
* Constructor.
*
* @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 ServiceReferenceGraphNode
*/
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,122 @@
<?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;
/**
* Constructor.
*
* @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,574 @@
<?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\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>
*
* @api
*/
class Container implements IntrospectableContainerInterface
{
/**
* @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('_' => '', '.' => '_', '\\' => '_');
/**
* Constructor.
*
* @param ParameterBagInterface $parameterBag A ParameterBagInterface instance
*
* @api
*/
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.
*
* @api
*/
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
*
* @api
*/
public function isFrozen()
{
return $this->parameterBag instanceof FrozenParameterBag;
}
/**
* Gets the service container parameter bag.
*
* @return ParameterBagInterface A ParameterBagInterface instance
*
* @api
*/
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
*
* @api
*/
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
*
* @api
*/
public function hasParameter($name)
{
return $this->parameterBag->has($name);
}
/**
* Sets a parameter.
*
* @param string $name The parameter name
* @param mixed $value The parameter value
*
* @api
*/
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.
*
* @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
*
* @api
*/
public function set($id, $service, $scope = self::SCOPE_CONTAINER)
{
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;
}
$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
*
* @api
*/
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
*
* @api
*/
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->services as $key => $associatedService) {
$lev = levenshtein($id, $key);
if ($lev <= strlen($id) / 3 || false !== strpos($key, $id)) {
$alternatives[] = $key;
}
}
throw new ServiceNotFoundException($id, null, null, $alternatives);
}
return;
}
$this->loading[$id] = true;
try {
$service = $this->$method();
} catch (\Exception $e) {
unset($this->loading[$id]);
if (array_key_exists($id, $this->services)) {
unset($this->services[$id]);
}
if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
return;
}
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);
}
/**
* Gets all service ids.
*
* @return array An array of all defined service ids
*/
public function getServiceIds()
{
$ids = array();
$r = new \ReflectionClass($this);
foreach ($r->getMethods() as $method) {
if (preg_match('/^get(.+)Service$/', $method->name, $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
*
* @api
*/
public function enterScope($name)
{
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
*
* @api
*/
public function leaveScope($name)
{
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
*
* @api
*/
public function addScope(ScopeInterface $scope)
{
$name = $scope->getName();
$parentScope = $scope->getParentName();
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
*
* @api
*/
public function hasScope($name)
{
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
*
* @api
*/
public function isScopeActive($name)
{
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'), strtr($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;
/**
* A simple implementation of ContainerAwareInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
abstract class ContainerAware implements ContainerAwareInterface
{
/**
* @var ContainerInterface
*
* @api
*/
protected $container;
/**
* Sets the Container associated with this Controller.
*
* @param ContainerInterface $container A ContainerInterface instance
*
* @api
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
}

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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,157 @@
<?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>
*
* @api
*/
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.
*
* @param string $id The service identifier
* @param object $service The service instance
* @param string $scope The scope of the service
*
* @api
*/
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
*
* @api
*/
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
*
* @api
*/
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
*
* @api
*/
public function getParameter($name);
/**
* Checks if a parameter exists.
*
* @param string $name The parameter name
*
* @return bool The presence of parameter in container
*
* @api
*/
public function hasParameter($name);
/**
* Sets a parameter.
*
* @param string $name The parameter name
* @param mixed $value The parameter value
*
* @api
*/
public function setParameter($name, $value);
/**
* Enters the given scope.
*
* @param string $name
*
* @api
*/
public function enterScope($name);
/**
* Leaves the current scope, and re-enters the parent scope.
*
* @param string $name
*
* @api
*/
public function leaveScope($name);
/**
* Adds a scope to the container.
*
* @param ScopeInterface $scope
*
* @api
*/
public function addScope(ScopeInterface $scope);
/**
* Whether this container has the given scope.
*
* @param string $name
*
* @return bool
*
* @api
*/
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
*
* @api
*/
public function isScopeActive($name);
}

View file

@ -0,0 +1,807 @@
<?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>
*
* @api
*/
class Definition
{
private $class;
private $file;
private $factory;
private $factoryClass;
private $factoryMethod;
private $factoryService;
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;
protected $arguments;
/**
* Constructor.
*
* @param string|null $class The service class
* @param array $arguments An array of arguments to pass to the service constructor
*
* @api
*/
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 Definition The current instance
*/
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 Definition The current instance
*
* @api
* @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
*
* @api
* @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 Definition The current instance
*
* @api
* @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
*
* @return Definition The current instance
*
* @throws InvalidArgumentException In case the decorated service id and the new decorated service id are equals.
*/
public function setDecoratedService($id, $renamedId = null)
{
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);
}
return $this;
}
/**
* Gets the service that decorates this service.
*
* @return null|array An array composed of the decorated service id and the new id for it, null if no service is decorated
*/
public function getDecoratedService()
{
return $this->decoratedService;
}
/**
* Gets the factory method.
*
* @return string|null The factory method name
*
* @api
* @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 Definition The current instance
*
* @api
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function setFactoryService($factoryService)
{
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
*
* @api
* @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 Definition The current instance
*
* @api
*/
public function setClass($class)
{
$this->class = $class;
return $this;
}
/**
* Gets the service class.
*
* @return string|null The service class
*
* @api
*/
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 Definition The current instance
*
* @api
*/
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
return $this;
}
/**
* @api
*/
public function setProperties(array $properties)
{
$this->properties = $properties;
return $this;
}
/**
* @api
*/
public function getProperties()
{
return $this->properties;
}
/**
* @api
*/
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 Definition The current instance
*
* @api
*/
public function addArgument($argument)
{
$this->arguments[] = $argument;
return $this;
}
/**
* Sets a specific argument.
*
* @param int $index
* @param mixed $argument
*
* @return Definition The current instance
*
* @throws OutOfBoundsException When the replaced argument does not exist
*
* @api
*/
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
*
* @api
*/
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
*
* @api
*/
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 Definition The current instance
*
* @api
*/
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 Definition The current instance
*
* @throws InvalidArgumentException on empty $method param
*
* @api
*/
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 Definition The current instance
*
* @api
*/
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
*
* @api
*/
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
*
* @api
*/
public function getMethodCalls()
{
return $this->calls;
}
/**
* Sets tags for this definition.
*
* @param array $tags
*
* @return Definition the current instance
*
* @api
*/
public function setTags(array $tags)
{
$this->tags = $tags;
return $this;
}
/**
* Returns all tags.
*
* @return array An array of tags
*
* @api
*/
public function getTags()
{
return $this->tags;
}
/**
* Gets a tag by name.
*
* @param string $name The tag name
*
* @return array An array of attributes
*
* @api
*/
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 Definition The current instance
*
* @api
*/
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
*
* @api
*/
public function hasTag($name)
{
return isset($this->tags[$name]);
}
/**
* Clears all tags for a given name.
*
* @param string $name The tag name
*
* @return Definition
*/
public function clearTag($name)
{
if (isset($this->tags[$name])) {
unset($this->tags[$name]);
}
return $this;
}
/**
* Clears the tags for this definition.
*
* @return Definition The current instance
*
* @api
*/
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 Definition The current instance
*
* @api
*/
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
*
* @api
*/
public function getFile()
{
return $this->file;
}
/**
* Sets the scope of the service.
*
* @param string $scope Whether the service must be shared or not
*
* @return Definition The current instance
*
* @api
*/
public function setScope($scope)
{
$this->scope = $scope;
return $this;
}
/**
* Returns the scope of the service.
*
* @return string
*
* @api
*/
public function getScope()
{
return $this->scope;
}
/**
* Sets the visibility of this service.
*
* @param bool $boolean
*
* @return Definition The current instance
*
* @api
*/
public function setPublic($boolean)
{
$this->public = (bool) $boolean;
return $this;
}
/**
* Whether this service is public facing.
*
* @return bool
*
* @api
*/
public function isPublic()
{
return $this->public;
}
/**
* Sets the synchronized flag of this service.
*
* @param bool $boolean
*
* @return Definition The current instance
*
* @api
*
* @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
*
* @api
*
* @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 Definition The current instance
*/
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 Definition the current instance
*
* @api
*/
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
*
* @api
*/
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 Definition the current instance
*
* @api
*/
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
*
* @api
*/
public function isAbstract()
{
return $this->abstract;
}
/**
* Sets a configurator to call after the service is fully initialized.
*
* @param callable $callable A PHP callable
*
* @return Definition The current instance
*
* @api
*/
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
*
* @api
*/
public function getConfigurator()
{
return $this->configurator;
}
}

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>
*
* @api
*/
class DefinitionDecorator extends Definition
{
private $parent;
private $changes = array();
/**
* Constructor.
*
* @param string $parent The id of Definition instance to decorate.
*
* @api
*/
public function __construct($parent)
{
parent::__construct();
$this->parent = $parent;
}
/**
* Returns the Definition being decorated.
*
* @return string
*
* @api
*/
public function getParent()
{
return $this->parent;
}
/**
* Returns all changes tracked for the Definition object.
*
* @return array An array of changes for this Definition
*
* @api
*/
public function getChanges()
{
return $this->changes;
}
/**
* {@inheritdoc}
*
* @api
*/
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}
*
* @api
*/
public function setFactoryClass($class)
{
$this->changes['factory_class'] = true;
return parent::setFactoryClass($class);
}
/**
* {@inheritdoc}
*
* @api
*/
public function setFactoryMethod($method)
{
$this->changes['factory_method'] = true;
return parent::setFactoryMethod($method);
}
/**
* {@inheritdoc}
*
* @api
*/
public function setFactoryService($service)
{
$this->changes['factory_service'] = true;
return parent::setFactoryService($service);
}
/**
* {@inheritdoc}
*
* @api
*/
public function setConfigurator($callable)
{
$this->changes['configurator'] = true;
return parent::setConfigurator($callable);
}
/**
* {@inheritdoc}
*
* @api
*/
public function setFile($file)
{
$this->changes['file'] = true;
return parent::setFile($file);
}
/**
* {@inheritdoc}
*
* @api
*/
public function setPublic($boolean)
{
$this->changes['public'] = true;
return parent::setPublic($boolean);
}
/**
* {@inheritdoc}
*
* @api
*/
public function setLazy($boolean)
{
$this->changes['lazy'] = true;
return parent::setLazy($boolean);
}
/**
* 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
*
* @api
*/
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 DefinitionDecorator the current instance
*
* @throws InvalidArgumentException when $index isn't an integer
*
* @api
*/
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,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\Dumper;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Dumper is the abstract class for all built-in dumpers.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
abstract class Dumper implements DumperInterface
{
protected $container;
/**
* Constructor.
*
* @param ContainerBuilder $container The service container to dump
*
* @api
*/
public function __construct(ContainerBuilder $container)
{
$this->container = $container;
}
}

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

View file

@ -0,0 +1,302 @@
<?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, $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) {
$className = $definition->getClass();
try {
$className = $this->container->getParameterBag()->resolveValue($className);
} catch (ParameterNotFoundException $e) {
}
$nodes[$id] = array('class' => str_replace('\\', '\\\\', $className), 'attributes' => array_merge($this->options['node.definition'], array('style' => ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope() ? '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() 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($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($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,356 @@
<?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>
*
* @api
*/
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
*
* @api
*/
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 ($definition->getClass()) {
$service->setAttribute('class', $definition->getClass());
}
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 (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope()) {
$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) = $decorated;
$service->setAttribute('decorates', $decorated);
if (null !== $renamedId) {
$service->setAttribute('decoration-inner-name', $renamedId);
}
}
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 ($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($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()) {
$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($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,345 @@
<?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>
*
* @api
*/
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
*
* @api
*/
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 ($definition->getClass()) {
$code .= sprintf(" class: %s\n", $definition->getClass());
}
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", $definition->getFile());
}
if ($definition->isSynthetic()) {
$code .= sprintf(" synthetic: true\n");
}
if ($definition->isSynchronized(false)) {
$code .= sprintf(" synchronized: true\n");
}
if ($definition->getFactoryClass(false)) {
$code .= sprintf(" factory_class: %s\n", $definition->getFactoryClass(false));
}
if ($definition->isLazy()) {
$code .= sprintf(" lazy: true\n");
}
if ($definition->getFactoryMethod(false)) {
$code .= sprintf(" factory_method: %s\n", $definition->getFactoryMethod(false));
}
if ($definition->getFactoryService(false)) {
$code .= sprintf(" factory_service: %s\n", $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 (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope()) {
$code .= sprintf(" scope: %s\n", $scope);
}
if (null !== $decorated = $definition->getDecoratedService()) {
list($decorated, $renamedId) = $decorated;
$code .= sprintf(" decorates: %s\n", $decorated);
if (null !== $renamedId) {
$code .= sprintf(" decoration_inner_name: %s\n", $renamedId);
}
}
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);
} else {
return sprintf(" %s:\n alias: %s\n public: false", $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($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($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,95 @@
<?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;
/**
* Constructor.
*
* @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,124 @@
<?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
{
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return false;
}
/**
* Returns the namespace to be used for this extension (XML namespace).
*
* @return string The XML namespace
*/
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')) {
$configuration = new $class();
return $configuration;
}
}
}
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,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\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* ExtensionInterface is the interface implemented by container extension classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
interface ExtensionInterface
{
/**
* Loads a specific configuration.
*
* @param array $config An array of configuration values
* @param ContainerBuilder $container A ContainerBuilder instance
*
* @throws \InvalidArgumentException When provided tag is not defined in this extension
*
* @api
*/
public function load(array $config, ContainerBuilder $container);
/**
* Returns the namespace to be used for this extension (XML namespace).
*
* @return string The XML namespace
*
* @api
*/
public function getNamespace();
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*
* @api
*/
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
*
* @api
*/
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,30 @@
<?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>
*/
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-2015 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,53 @@
<?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;
/**
* Constructor.
*
* @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,39 @@
<?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;
/**
* Constructor.
*
* @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,559 @@
<?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, $path);
// 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
* @param string $file
*/
private function parseParameters(\DOMDocument $xml, $file)
{
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;
}
foreach ($imports as $import) {
$this->setCurrentDir(dirname($file));
$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
*
* @return Definition|null
*/
private function parseDefinition(\DOMElement $service)
{
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', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'lazy', 'abstract') as $key) {
if ($value = $service->getAttribute($key)) {
$method = 'set'.str_replace('-', '', $key);
$definition->$method(XmlUtils::phpize($value));
}
}
if ($value = $service->getAttribute('synchronized')) {
$definition->setSynchronized(XmlUtils::phpize($value), 'request' !== (string) $service->getAttribute('id'));
}
if ($files = $this->getChildren($service, 'file')) {
$definition->setFile($files[0]->nodeValue);
}
$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]);
} 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]);
} 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);
}
$definition->addTag($tag->getAttribute('name'), $parameters);
}
if ($value = $service->getAttribute('decorates')) {
$renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null;
$definition->setDecoratedService($value, $renameId);
}
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 "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);
if ($services = $this->getChildren($node, 'service')) {
$definitions[$id] = array($node, $file, true);
$services[0]->setAttribute('id', $id);
}
}
}
// resolve definitions
krsort($definitions);
foreach ($definitions as $id => $def) {
list($domElement, $file, $wild) = $def;
// anonymous services are always private
// we could not use the constant false here, because of XML parsing
$domElement->setAttribute('public', 'false');
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'));
}
if (!$arg->hasAttribute('key')) {
$key = !$arguments ? 0 : max(array_keys($arguments)) + 1;
} else {
$key = $arg->getAttribute('key');
}
// 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 ($arg->hasAttribute('index')) {
$key = 'index_'.$arg->getAttribute('index');
}
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($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
;
$valid = @$dom->schemaValidateSource($source);
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,417 @@
<?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\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));
}
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(dirname($file));
$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['scope'])) {
$definition->setScope($service['scope']);
}
if (isset($service['synthetic'])) {
$definition->setSynthetic($service['synthetic']);
}
if (isset($service['synchronized'])) {
$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 (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'])) {
$definition->setFactoryClass($service['factory_class']);
}
if (isset($service['factory_method'])) {
$definition->setFactoryMethod($service['factory_method']);
}
if (isset($service['factory_service'])) {
$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));
}
$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'])) {
$renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null;
$definition->setDecoratedService($service['decorates'], $renameId);
}
$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();
}
return $this->validate($this->yamlParser->parse(file_get_contents($file)), $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,193 @@
<?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:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="imports" type="imports" minOccurs="0" maxOccurs="1" />
<xsd:element name="parameters" type="parameters" minOccurs="0" maxOccurs="1" />
<xsd:element name="services" type="services" minOccurs="0" maxOccurs="1" />
<xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<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="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:choice>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="class" type="xsd:string" />
<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:complexType>
<xsd:complexType name="tag">
<xsd:attribute name="name" type="xsd:string" />
<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="xsd:string" />
<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="xsd:string" />
<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,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;
/**
* Parameter represents a parameter reference.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class Parameter
{
private $id;
/**
* Constructor.
*
* @param string $id The parameter key
*/
public function __construct($id)
{
$this->id = $id;
}
/**
* __toString.
*
* @return string The parameter key
*/
public function __toString()
{
return (string) $this->id;
}
}

View file

@ -0,0 +1,72 @@
<?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>
*
* @api
*/
class FrozenParameterBag extends ParameterBag
{
/**
* Constructor.
*
* 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
*
* @api
*/
public function __construct(array $parameters = array())
{
$this->parameters = $parameters;
$this->resolved = true;
}
/**
* {@inheritdoc}
*
* @api
*/
public function clear()
{
throw new LogicException('Impossible to call clear() on a frozen ParameterBag.');
}
/**
* {@inheritdoc}
*
* @api
*/
public function add(array $parameters)
{
throw new LogicException('Impossible to call add() on a frozen ParameterBag.');
}
/**
* {@inheritdoc}
*
* @api
*/
public function set($name, $value)
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
}

View file

@ -0,0 +1,304 @@
<?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>
*
* @api
*/
class ParameterBag implements ParameterBagInterface
{
protected $parameters = array();
protected $resolved = false;
/**
* Constructor.
*
* @param array $parameters An array of parameters
*
* @api
*/
public function __construct(array $parameters = array())
{
$this->add($parameters);
}
/**
* Clears all parameters.
*
* @api
*/
public function clear()
{
$this->parameters = array();
}
/**
* Adds parameters to the service container parameters.
*
* @param array $parameters An array of parameters
*
* @api
*/
public function add(array $parameters)
{
foreach ($parameters as $key => $value) {
$this->parameters[strtolower($key)] = $value;
}
}
/**
* Gets the service container parameters.
*
* @return array An array of parameters
*
* @api
*/
public function all()
{
return $this->parameters;
}
/**
* Gets a service container parameter.
*
* @param string $name The parameter name
*
* @return mixed The parameter value
*
* @throws ParameterNotFoundException if the parameter is not defined
*
* @api
*/
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
*
* @api
*/
public function set($name, $value)
{
$this->parameters[strtolower($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
*
* @api
*/
public function has($name)
{
return array_key_exists(strtolower($name), $this->parameters);
}
/**
* Removes a parameter.
*
* @param string $name The parameter name
*
* @api
*/
public function remove($name)
{
unset($this->parameters[strtolower($name)]);
}
/**
* Replaces parameter placeholders (%name%) by their values for all parameters.
*/
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;
}
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,115 @@
<?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;
/**
* ParameterBagInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
interface ParameterBagInterface
{
/**
* Clears all parameters.
*
* @api
*/
public function clear();
/**
* Adds parameters to the service container parameters.
*
* @param array $parameters An array of parameters
*
* @api
*/
public function add(array $parameters);
/**
* Gets the service container parameters.
*
* @return array An array of parameters
*
* @api
*/
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
*
* @api
*/
public function get($name);
/**
* Sets a service container parameter.
*
* @param string $name The parameter name
* @param mixed $value The parameter value
*
* @api
*/
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
*
* @api
*/
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,80 @@
DependencyInjection Component
=============================
DependencyInjection manages your services via a robust and flexible Dependency
Injection Container.
Here is a simple example that shows how to register services and parameters:
```php
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
$sc = new ContainerBuilder();
$sc
->register('foo', '%foo.class%')
->addArgument(new Reference('bar'))
;
$sc->setParameter('foo.class', 'Foo');
$sc->get('foo');
```
Method Calls (Setter Injection):
```php
$sc = new ContainerBuilder();
$sc
->register('bar', '%bar.class%')
->addMethodCall('setFoo', array(new Reference('foo')))
;
$sc->setParameter('bar.class', 'Bar');
$sc->get('bar');
```
Factory Class:
If your service is retrieved by calling a static method:
```php
$sc = new ContainerBuilder();
$sc
->register('bar', '%bar.class%')
->setFactory(array('%bar.class%', 'getInstance'))
->addArgument('Aarrg!!!')
;
$sc->setParameter('bar.class', 'Bar');
$sc->get('bar');
```
File Include:
For some services, especially those that are difficult or impossible to
autoload, you may need the container to include a file before
instantiating your class.
```php
$sc = new ContainerBuilder();
$sc
->register('bar', '%bar.class%')
->setFile('/path/to/file')
->addArgument('Aarrg!!!')
;
$sc->setParameter('bar.class', 'Bar');
$sc->get('bar');
```
Resources
---------
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/DependencyInjection/
$ composer install
$ phpunit

View file

@ -0,0 +1,72 @@
<?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>
*
* @api
*/
class Reference
{
private $id;
private $invalidBehavior;
private $strict;
/**
* Constructor.
*
* @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;
}
/**
* __toString.
*
* @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
*/
public function isStrict()
{
return $this->strict;
}
}

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;
/**
* Scope class.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @api
*/
class Scope implements ScopeInterface
{
private $name;
private $parentName;
/**
* @api
*/
public function __construct($name, $parentName = ContainerInterface::SCOPE_CONTAINER)
{
$this->name = $name;
$this->parentName = $parentName;
}
/**
* @api
*/
public function getName()
{
return $this->name;
}
/**
* @api
*/
public function getParentName()
{
return $this->parentName;
}
}

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;
/**
* Scope Interface.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @api
*/
interface ScopeInterface
{
/**
* @api
*/
public function getName();
/**
* @api
*/
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 method 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,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;
/**
* TaggedContainerInterface is the interface implemented when a container knows how to deals with tags.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
interface TaggedContainerInterface extends ContainerInterface
{
/**
* Returns service ids for a given tag.
*
* @param string $name The tag name
*
* @return array An array of tags
*
* @api
*/
public function findTaggedServiceIds($name);
}

View file

@ -0,0 +1,146 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
use Symfony\Component\DependencyInjection\Compiler\RepeatedPass;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AnalyzeServiceReferencesPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$a = $container
->register('a')
->addArgument($ref1 = new Reference('b'))
;
$b = $container
->register('b')
->addMethodCall('setA', array($ref2 = new Reference('a')))
;
$c = $container
->register('c')
->addArgument($ref3 = new Reference('a'))
->addArgument($ref4 = new Reference('b'))
;
$d = $container
->register('d')
->setProperty('foo', $ref5 = new Reference('b'))
;
$e = $container
->register('e')
->setConfigurator(array($ref6 = new Reference('b'), 'methodName'))
;
$graph = $this->process($container);
$this->assertCount(4, $edges = $graph->getNode('b')->getInEdges());
$this->assertSame($ref1, $edges[0]->getValue());
$this->assertSame($ref4, $edges[1]->getValue());
$this->assertSame($ref5, $edges[2]->getValue());
$this->assertSame($ref6, $edges[3]->getValue());
}
public function testProcessDetectsReferencesFromInlinedDefinitions()
{
$container = new ContainerBuilder();
$container
->register('a')
;
$container
->register('b')
->addArgument(new Definition(null, array($ref = new Reference('a'))))
;
$graph = $this->process($container);
$this->assertCount(1, $refs = $graph->getNode('a')->getInEdges());
$this->assertSame($ref, $refs[0]->getValue());
}
public function testProcessDetectsReferencesFromInlinedFactoryDefinitions()
{
$container = new ContainerBuilder();
$container
->register('a')
;
$factory = new Definition();
$factory->setFactory(array(new Reference('a'), 'a'));
$container
->register('b')
->addArgument($factory)
;
$graph = $this->process($container);
$this->assertTrue($graph->hasNode('a'));
$this->assertCount(1, $refs = $graph->getNode('a')->getInEdges());
}
public function testProcessDoesNotSaveDuplicateReferences()
{
$container = new ContainerBuilder();
$container
->register('a')
;
$container
->register('b')
->addArgument(new Definition(null, array($ref1 = new Reference('a'))))
->addArgument(new Definition(null, array($ref2 = new Reference('a'))))
;
$graph = $this->process($container);
$this->assertCount(2, $graph->getNode('a')->getInEdges());
}
public function testProcessDetectsFactoryReferences()
{
$container = new ContainerBuilder();
$container
->register('foo', 'stdClass')
->setFactory(array('stdClass', 'getInstance'));
$container
->register('bar', 'stdClass')
->setFactory(array(new Reference('foo'), 'getInstance'));
$graph = $this->process($container);
$this->assertTrue($graph->hasNode('foo'));
$this->assertCount(1, $graph->getNode('foo')->getInEdges());
}
protected function process(ContainerBuilder $container)
{
$pass = new RepeatedPass(array(new AnalyzeServiceReferencesPass()));
$pass->process($container);
return $container->getCompiler()->getServiceReferenceGraph();
}
}

View file

@ -0,0 +1,103 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Compiler\AutoAliasServicePass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AutoAliasServicePassTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException
*/
public function testProcessWithMissingParameter()
{
$container = new ContainerBuilder();
$container->register('example')
->addTag('auto_alias', array('format' => '%non_existing%.example'));
$pass = new AutoAliasServicePass();
$pass->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
*/
public function testProcessWithMissingFormat()
{
$container = new ContainerBuilder();
$container->register('example')
->addTag('auto_alias', array());
$container->setParameter('existing', 'mysql');
$pass = new AutoAliasServicePass();
$pass->process($container);
}
public function testProcessWithNonExistingAlias()
{
$container = new ContainerBuilder();
$container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault')
->addTag('auto_alias', array('format' => '%existing%.example'));
$container->setParameter('existing', 'mysql');
$pass = new AutoAliasServicePass();
$pass->process($container);
$this->assertEquals('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault', $container->getDefinition('example')->getClass());
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault', $container->get('example'));
}
public function testProcessWithExistingAlias()
{
$container = new ContainerBuilder();
$container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault')
->addTag('auto_alias', array('format' => '%existing%.example'));
$container->register('mysql.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql');
$container->setParameter('existing', 'mysql');
$pass = new AutoAliasServicePass();
$pass->process($container);
$this->assertTrue($container->hasAlias('example'));
$this->assertEquals('mysql.example', $container->getAlias('example'));
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql', $container->get('example'));
}
public function testProcessWithManualAlias()
{
$container = new ContainerBuilder();
$container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault')
->addTag('auto_alias', array('format' => '%existing%.example'));
$container->register('mysql.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql');
$container->register('mariadb.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariadb');
$container->setAlias('example', 'mariadb.example');
$container->setParameter('existing', 'mysql');
$pass = new AutoAliasServicePass();
$pass->process($container);
$this->assertTrue($container->hasAlias('example'));
$this->assertEquals('mariadb.example', $container->getAlias('example'));
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariaDb', $container->get('example'));
}
}
class ServiceClassDefault
{
}
class ServiceClassMysql extends ServiceClassDefault
{
}
class ServiceClassMariaDb extends ServiceClassMysql
{
}

View file

@ -0,0 +1,130 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass;
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
use Symfony\Component\DependencyInjection\Compiler\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class CheckCircularReferencesPassTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
*/
public function testProcess()
{
$container = new ContainerBuilder();
$container->register('a')->addArgument(new Reference('b'));
$container->register('b')->addArgument(new Reference('a'));
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
*/
public function testProcessWithAliases()
{
$container = new ContainerBuilder();
$container->register('a')->addArgument(new Reference('b'));
$container->setAlias('b', 'c');
$container->setAlias('c', 'a');
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
*/
public function testProcessWithFactory()
{
$container = new ContainerBuilder();
$container
->register('a', 'stdClass')
->setFactory(array(new Reference('b'), 'getInstance'));
$container
->register('b', 'stdClass')
->setFactory(array(new Reference('a'), 'getInstance'));
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
*/
public function testProcessDetectsIndirectCircularReference()
{
$container = new ContainerBuilder();
$container->register('a')->addArgument(new Reference('b'));
$container->register('b')->addArgument(new Reference('c'));
$container->register('c')->addArgument(new Reference('a'));
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
*/
public function testProcessDetectsIndirectCircularReferenceWithFactory()
{
$container = new ContainerBuilder();
$container->register('a')->addArgument(new Reference('b'));
$container
->register('b', 'stdClass')
->setFactory(array(new Reference('c'), 'getInstance'));
$container->register('c')->addArgument(new Reference('a'));
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
*/
public function testDeepCircularReference()
{
$container = new ContainerBuilder();
$container->register('a')->addArgument(new Reference('b'));
$container->register('b')->addArgument(new Reference('c'));
$container->register('c')->addArgument(new Reference('b'));
$this->process($container);
}
public function testProcessIgnoresMethodCalls()
{
$container = new ContainerBuilder();
$container->register('a')->addArgument(new Reference('b'));
$container->register('b')->addMethodCall('setA', array(new Reference('a')));
$this->process($container);
}
protected function process(ContainerBuilder $container)
{
$compiler = new Compiler();
$passConfig = $compiler->getPassConfig();
$passConfig->setOptimizationPasses(array(
new AnalyzeServiceReferencesPass(true),
new CheckCircularReferencesPass(),
));
$passConfig->setRemovingPasses(array());
$compiler->compile($container);
}
}

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\Tests\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CheckDefinitionValidityPass;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class CheckDefinitionValidityPassTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
*/
public function testProcessDetectsSyntheticNonPublicDefinitions()
{
$container = new ContainerBuilder();
$container->register('a')->setSynthetic(true)->setPublic(false);
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
*/
public function testProcessDetectsSyntheticPrototypeDefinitions()
{
$container = new ContainerBuilder();
$container->register('a')->setSynthetic(true)->setScope(ContainerInterface::SCOPE_PROTOTYPE);
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
*/
public function testProcessDetectsNonSyntheticNonAbstractDefinitionWithoutClass()
{
$container = new ContainerBuilder();
$container->register('a')->setSynthetic(false)->setAbstract(false);
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @group legacy
*/
public function testLegacyProcessDetectsBothFactorySyntaxesUsed()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$container = new ContainerBuilder();
$container->register('a')->setFactory(array('a', 'b'))->setFactoryClass('a');
$this->process($container);
}
public function testProcess()
{
$container = new ContainerBuilder();
$container->register('a', 'class');
$container->register('b', 'class')->setSynthetic(true)->setPublic(true);
$container->register('c', 'class')->setAbstract(true);
$container->register('d', 'class')->setSynthetic(true);
$this->process($container);
}
public function testValidTags()
{
$container = new ContainerBuilder();
$container->register('a', 'class')->addTag('foo', array('bar' => 'baz'));
$container->register('b', 'class')->addTag('foo', array('bar' => null));
$container->register('c', 'class')->addTag('foo', array('bar' => 1));
$container->register('d', 'class')->addTag('foo', array('bar' => 1.1));
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
*/
public function testInvalidTags()
{
$container = new ContainerBuilder();
$container->register('a', 'class')->addTag('foo', array('bar' => array('baz' => 'baz')));
$this->process($container);
}
protected function process(ContainerBuilder $container)
{
$pass = new CheckDefinitionValidityPass();
$pass->process($container);
}
}

View file

@ -0,0 +1,70 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class CheckExceptionOnInvalidReferenceBehaviorPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container
->register('a', '\stdClass')
->addArgument(new Reference('b'))
;
$container->register('b', '\stdClass');
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException
*/
public function testProcessThrowsExceptionOnInvalidReference()
{
$container = new ContainerBuilder();
$container
->register('a', '\stdClass')
->addArgument(new Reference('b'))
;
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException
*/
public function testProcessThrowsExceptionOnInvalidReferenceFromInlinedDefinition()
{
$container = new ContainerBuilder();
$def = new Definition();
$def->addArgument(new Reference('b'));
$container
->register('a', '\stdClass')
->addArgument($def)
;
$this->process($container);
}
private function process(ContainerBuilder $container)
{
$pass = new CheckExceptionOnInvalidReferenceBehaviorPass();
$pass->process($container);
}
}

View file

@ -0,0 +1,97 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Scope;
use Symfony\Component\DependencyInjection\Compiler\CheckReferenceValidityPass;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class CheckReferenceValidityPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcessIgnoresScopeWideningIfNonStrictReference()
{
$container = new ContainerBuilder();
$container->register('a')->addArgument(new Reference('b', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false));
$container->register('b')->setScope('prototype');
$this->process($container);
}
/**
* @expectedException \RuntimeException
*/
public function testProcessDetectsScopeWidening()
{
$container = new ContainerBuilder();
$container->register('a')->addArgument(new Reference('b'));
$container->register('b')->setScope('prototype');
$this->process($container);
}
public function testProcessIgnoresCrossScopeHierarchyReferenceIfNotStrict()
{
$container = new ContainerBuilder();
$container->addScope(new Scope('a'));
$container->addScope(new Scope('b'));
$container->register('a')->setScope('a')->addArgument(new Reference('b', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false));
$container->register('b')->setScope('b');
$this->process($container);
}
/**
* @expectedException \RuntimeException
*/
public function testProcessDetectsCrossScopeHierarchyReference()
{
$container = new ContainerBuilder();
$container->addScope(new Scope('a'));
$container->addScope(new Scope('b'));
$container->register('a')->setScope('a')->addArgument(new Reference('b'));
$container->register('b')->setScope('b');
$this->process($container);
}
/**
* @expectedException \RuntimeException
*/
public function testProcessDetectsReferenceToAbstractDefinition()
{
$container = new ContainerBuilder();
$container->register('a')->setAbstract(true);
$container->register('b')->addArgument(new Reference('a'));
$this->process($container);
}
public function testProcess()
{
$container = new ContainerBuilder();
$container->register('a')->addArgument(new Reference('b'));
$container->register('b');
$this->process($container);
}
protected function process(ContainerBuilder $container)
{
$pass = new CheckReferenceValidityPass();
$pass->process($container);
}
}

View file

@ -0,0 +1,81 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass;
class DecoratorServicePassTest extends \PHPUnit_Framework_TestCase
{
public function testProcessWithoutAlias()
{
$container = new ContainerBuilder();
$fooDefinition = $container
->register('foo')
->setPublic(false)
;
$fooExtendedDefinition = $container
->register('foo.extended')
->setPublic(true)
->setDecoratedService('foo')
;
$barDefinition = $container
->register('bar')
->setPublic(true)
;
$barExtendedDefinition = $container
->register('bar.extended')
->setPublic(true)
->setDecoratedService('bar', 'bar.yoo')
;
$this->process($container);
$this->assertEquals('foo.extended', $container->getAlias('foo'));
$this->assertFalse($container->getAlias('foo')->isPublic());
$this->assertEquals('bar.extended', $container->getAlias('bar'));
$this->assertTrue($container->getAlias('bar')->isPublic());
$this->assertSame($fooDefinition, $container->getDefinition('foo.extended.inner'));
$this->assertFalse($container->getDefinition('foo.extended.inner')->isPublic());
$this->assertSame($barDefinition, $container->getDefinition('bar.yoo'));
$this->assertFalse($container->getDefinition('bar.yoo')->isPublic());
$this->assertNull($fooExtendedDefinition->getDecoratedService());
$this->assertNull($barExtendedDefinition->getDecoratedService());
}
public function testProcessWithAlias()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(true)
;
$container->setAlias('foo.alias', new Alias('foo', false));
$fooExtendedDefinition = $container
->register('foo.extended')
->setPublic(true)
->setDecoratedService('foo.alias')
;
$this->process($container);
$this->assertEquals('foo.extended', $container->getAlias('foo.alias'));
$this->assertFalse($container->getAlias('foo.alias')->isPublic());
$this->assertEquals('foo', $container->getAlias('foo.extended.inner'));
$this->assertFalse($container->getAlias('foo.extended.inner')->isPublic());
$this->assertNull($fooExtendedDefinition->getDecoratedService());
}
protected function process(ContainerBuilder $container)
{
$repeatedPass = new DecoratorServicePass();
$repeatedPass->process($container);
}
}

View file

@ -0,0 +1,245 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Scope;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
use Symfony\Component\DependencyInjection\Compiler\RepeatedPass;
use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class InlineServiceDefinitionsPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container
->register('inlinable.service')
->setPublic(false)
;
$container
->register('service')
->setArguments(array(new Reference('inlinable.service')))
;
$this->process($container);
$arguments = $container->getDefinition('service')->getArguments();
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $arguments[0]);
$this->assertSame($container->getDefinition('inlinable.service'), $arguments[0]);
}
public function testProcessDoesNotInlineWhenAliasedServiceIsNotOfPrototypeScope()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
;
$container->setAlias('moo', 'foo');
$container
->register('service')
->setArguments(array($ref = new Reference('foo')))
;
$this->process($container);
$arguments = $container->getDefinition('service')->getArguments();
$this->assertSame($ref, $arguments[0]);
}
public function testProcessDoesInlineServiceOfPrototypeScope()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setScope('prototype')
;
$container
->register('bar')
->setPublic(false)
->setScope('prototype')
;
$container->setAlias('moo', 'bar');
$container
->register('service')
->setArguments(array(new Reference('foo'), $ref = new Reference('moo'), new Reference('bar')))
;
$this->process($container);
$arguments = $container->getDefinition('service')->getArguments();
$this->assertEquals($container->getDefinition('foo'), $arguments[0]);
$this->assertNotSame($container->getDefinition('foo'), $arguments[0]);
$this->assertSame($ref, $arguments[1]);
$this->assertEquals($container->getDefinition('bar'), $arguments[2]);
$this->assertNotSame($container->getDefinition('bar'), $arguments[2]);
}
public function testProcessInlinesIfMultipleReferencesButAllFromTheSameDefinition()
{
$container = new ContainerBuilder();
$a = $container->register('a')->setPublic(false);
$b = $container
->register('b')
->addArgument(new Reference('a'))
->addArgument(new Definition(null, array(new Reference('a'))))
;
$this->process($container);
$arguments = $b->getArguments();
$this->assertSame($a, $arguments[0]);
$inlinedArguments = $arguments[1]->getArguments();
$this->assertSame($a, $inlinedArguments[0]);
}
public function testProcessInlinesPrivateFactoryReference()
{
$container = new ContainerBuilder();
$container->register('a')->setPublic(false);
$b = $container
->register('b')
->setPublic(false)
->setFactory(array(new Reference('a'), 'a'))
;
$container
->register('foo')
->setArguments(array(
$ref = new Reference('b'),
));
$this->process($container);
$inlinedArguments = $container->getDefinition('foo')->getArguments();
$this->assertSame($b, $inlinedArguments[0]);
}
public function testProcessDoesNotInlinePrivateFactoryIfReferencedMultipleTimesWithinTheSameDefinition()
{
$container = new ContainerBuilder();
$container
->register('a')
;
$container
->register('b')
->setPublic(false)
->setFactory(array(new Reference('a'), 'a'))
;
$container
->register('foo')
->setArguments(array(
$ref1 = new Reference('b'),
$ref2 = new Reference('b'),
))
;
$this->process($container);
$args = $container->getDefinition('foo')->getArguments();
$this->assertSame($ref1, $args[0]);
$this->assertSame($ref2, $args[1]);
}
public function testProcessDoesNotInlineReferenceWhenUsedByInlineFactory()
{
$container = new ContainerBuilder();
$container
->register('a')
;
$container
->register('b')
->setPublic(false)
->setFactory(array(new Reference('a'), 'a'))
;
$inlineFactory = new Definition();
$inlineFactory->setPublic(false);
$inlineFactory->setFactory(array(new Reference('b'), 'b'));
$container
->register('foo')
->setArguments(array(
$ref = new Reference('b'),
$inlineFactory,
))
;
$this->process($container);
$args = $container->getDefinition('foo')->getArguments();
$this->assertSame($ref, $args[0]);
}
public function testProcessInlinesOnlyIfSameScope()
{
$container = new ContainerBuilder();
$container->addScope(new Scope('foo'));
$a = $container->register('a')->setPublic(false)->setScope('foo');
$b = $container->register('b')->addArgument(new Reference('a'));
$this->process($container);
$arguments = $b->getArguments();
$this->assertEquals(new Reference('a'), $arguments[0]);
$this->assertTrue($container->hasDefinition('a'));
}
public function testProcessDoesNotInlineWhenServiceIsPrivateButLazy()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
->setLazy(true)
;
$container
->register('service')
->setArguments(array($ref = new Reference('foo')))
;
$this->process($container);
$arguments = $container->getDefinition('service')->getArguments();
$this->assertSame($ref, $arguments[0]);
}
public function testProcessDoesNotInlineWhenServiceReferencesItself()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
->addMethodCall('foo', array($ref = new Reference('foo')))
;
$this->process($container);
$calls = $container->getDefinition('foo')->getMethodCalls();
$this->assertSame($ref, $calls[0][1][0]);
}
protected function process(ContainerBuilder $container)
{
$repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass()));
$repeatedPass->process($container);
}
}

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\Tests\Compiler;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* This class tests the integration of the different compiler passes.
*/
class IntegrationTest extends \PHPUnit_Framework_TestCase
{
/**
* This tests that dependencies are correctly processed.
*
* We're checking that:
*
* * A is public, B/C are private
* * A -> C
* * B -> C
*/
public function testProcessRemovesAndInlinesRecursively()
{
$container = new ContainerBuilder();
$container->setResourceTracking(false);
$a = $container
->register('a', '\stdClass')
->addArgument(new Reference('c'))
;
$b = $container
->register('b', '\stdClass')
->addArgument(new Reference('c'))
->setPublic(false)
;
$c = $container
->register('c', '\stdClass')
->setPublic(false)
;
$container->compile();
$this->assertTrue($container->hasDefinition('a'));
$arguments = $a->getArguments();
$this->assertSame($c, $arguments[0]);
$this->assertFalse($container->hasDefinition('b'));
$this->assertFalse($container->hasDefinition('c'));
}
public function testProcessInlinesReferencesToAliases()
{
$container = new ContainerBuilder();
$container->setResourceTracking(false);
$a = $container
->register('a', '\stdClass')
->addArgument(new Reference('b'))
;
$container->setAlias('b', new Alias('c', false));
$c = $container
->register('c', '\stdClass')
->setPublic(false)
;
$container->compile();
$this->assertTrue($container->hasDefinition('a'));
$arguments = $a->getArguments();
$this->assertSame($c, $arguments[0]);
$this->assertFalse($container->hasAlias('b'));
$this->assertFalse($container->hasDefinition('c'));
}
public function testProcessInlinesWhenThereAreMultipleReferencesButFromTheSameDefinition()
{
$container = new ContainerBuilder();
$container->setResourceTracking(false);
$container
->register('a', '\stdClass')
->addArgument(new Reference('b'))
->addMethodCall('setC', array(new Reference('c')))
;
$container
->register('b', '\stdClass')
->addArgument(new Reference('c'))
->setPublic(false)
;
$container
->register('c', '\stdClass')
->setPublic(false)
;
$container->compile();
$this->assertTrue($container->hasDefinition('a'));
$this->assertFalse($container->hasDefinition('b'));
$this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.');
}
}

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\Tests\Compiler;
use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @group legacy
*/
class LegacyResolveParameterPlaceHoldersPassTest extends \PHPUnit_Framework_TestCase
{
public function testFactoryClassParametersShouldBeResolved()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$compilerPass = new ResolveParameterPlaceHoldersPass();
$container = new ContainerBuilder();
$container->setParameter('foo.factory.class', 'FooFactory');
$fooDefinition = $container->register('foo', '%foo.factory.class%');
$fooDefinition->setFactoryClass('%foo.factory.class%');
$compilerPass->process($container);
$fooDefinition = $container->getDefinition('foo');
$this->assertSame('FooFactory', $fooDefinition->getFactoryClass());
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
class MergeExtensionConfigurationPassTest extends \PHPUnit_Framework_TestCase
{
public function testExpressionLanguageProviderForwarding()
{
if (true !== class_exists('Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage')) {
$this->markTestSkipped('The ExpressionLanguage component isn\'t available!');
}
$tmpProviders = array();
$extension = $this->getMock('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface');
$extension->expects($this->any())
->method('getXsdValidationBasePath')
->will($this->returnValue(false));
$extension->expects($this->any())
->method('getNamespace')
->will($this->returnValue('http://example.org/schema/dic/foo'));
$extension->expects($this->any())
->method('getAlias')
->will($this->returnValue('foo'));
$extension->expects($this->once())
->method('load')
->will($this->returnCallback(function (array $config, ContainerBuilder $container) use (&$tmpProviders) {
$tmpProviders = $container->getExpressionLanguageProviders();
}));
$provider = $this->getMock('Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface');
$container = new ContainerBuilder(new ParameterBag());
$container->registerExtension($extension);
$container->prependExtensionConfig('foo', array('bar' => true ));
$container->addExpressionLanguageProvider($provider);
$pass = new MergeExtensionConfigurationPass();
$pass->process($container);
$this->assertEquals(array($provider), $tmpProviders);
}
}

View file

@ -0,0 +1,113 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
use Symfony\Component\DependencyInjection\Compiler\RepeatedPass;
use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class RemoveUnusedDefinitionsPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
;
$container
->register('bar')
->setPublic(false)
;
$container
->register('moo')
->setArguments(array(new Reference('bar')))
;
$this->process($container);
$this->assertFalse($container->hasDefinition('foo'));
$this->assertTrue($container->hasDefinition('bar'));
$this->assertTrue($container->hasDefinition('moo'));
}
public function testProcessRemovesUnusedDefinitionsRecursively()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
;
$container
->register('bar')
->setArguments(array(new Reference('foo')))
->setPublic(false)
;
$this->process($container);
$this->assertFalse($container->hasDefinition('foo'));
$this->assertFalse($container->hasDefinition('bar'));
}
public function testProcessWorksWithInlinedDefinitions()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
;
$container
->register('bar')
->setArguments(array(new Definition(null, array(new Reference('foo')))))
;
$this->process($container);
$this->assertTrue($container->hasDefinition('foo'));
$this->assertTrue($container->hasDefinition('bar'));
}
public function testProcessWontRemovePrivateFactory()
{
$container = new ContainerBuilder();
$container
->register('foo', 'stdClass')
->setFactory(array('stdClass', 'getInstance'))
->setPublic(false);
$container
->register('bar', 'stdClass')
->setFactory(array(new Reference('foo'), 'getInstance'))
->setPublic(false);
$container
->register('foobar')
->addArgument(new Reference('bar'));
$this->process($container);
$this->assertTrue($container->hasDefinition('foo'));
$this->assertTrue($container->hasDefinition('bar'));
$this->assertTrue($container->hasDefinition('foobar'));
}
protected function process(ContainerBuilder $container)
{
$repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new RemoveUnusedDefinitionsPass()));
$repeatedPass->process($container);
}
}

View file

@ -0,0 +1,59 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
class ReplaceAliasByActualDefinitionPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container->register('a', '\stdClass');
$bDefinition = new Definition('\stdClass');
$bDefinition->setPublic(false);
$container->setDefinition('b', $bDefinition);
$container->setAlias('a_alias', 'a');
$container->setAlias('b_alias', 'b');
$this->process($container);
$this->assertTrue($container->has('a'), '->process() does nothing to public definitions.');
$this->assertTrue($container->hasAlias('a_alias'));
$this->assertFalse($container->has('b'), '->process() removes non-public definitions.');
$this->assertTrue(
$container->has('b_alias') && !$container->hasAlias('b_alias'),
'->process() replaces alias to actual.'
);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testProcessWithInvalidAlias()
{
$container = new ContainerBuilder();
$container->setAlias('a_alias', 'a');
$this->process($container);
}
protected function process(ContainerBuilder $container)
{
$pass = new ReplaceAliasByActualDefinitionPass();
$pass->process($container);
}
}

View file

@ -0,0 +1,181 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container->register('parent', 'foo')->setArguments(array('moo', 'b'))->setProperty('foo', 'moo');
$container->setDefinition('child', new DefinitionDecorator('parent'))
->replaceArgument(0, 'a')
->setProperty('foo', 'bar')
->setClass('bar')
;
$this->process($container);
$def = $container->getDefinition('child');
$this->assertNotInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $def);
$this->assertEquals('bar', $def->getClass());
$this->assertEquals(array('a', 'b'), $def->getArguments());
$this->assertEquals(array('foo' => 'bar'), $def->getProperties());
}
public function testProcessAppendsMethodCallsAlways()
{
$container = new ContainerBuilder();
$container
->register('parent')
->addMethodCall('foo', array('bar'))
;
$container
->setDefinition('child', new DefinitionDecorator('parent'))
->addMethodCall('bar', array('foo'))
;
$this->process($container);
$def = $container->getDefinition('child');
$this->assertEquals(array(
array('foo', array('bar')),
array('bar', array('foo')),
), $def->getMethodCalls());
}
public function testProcessDoesNotCopyAbstract()
{
$container = new ContainerBuilder();
$container
->register('parent')
->setAbstract(true)
;
$container
->setDefinition('child', new DefinitionDecorator('parent'))
;
$this->process($container);
$def = $container->getDefinition('child');
$this->assertFalse($def->isAbstract());
}
public function testProcessDoesNotCopyScope()
{
$container = new ContainerBuilder();
$container
->register('parent')
->setScope('foo')
;
$container
->setDefinition('child', new DefinitionDecorator('parent'))
;
$this->process($container);
$def = $container->getDefinition('child');
$this->assertEquals(ContainerInterface::SCOPE_CONTAINER, $def->getScope());
}
public function testProcessDoesNotCopyTags()
{
$container = new ContainerBuilder();
$container
->register('parent')
->addTag('foo')
;
$container
->setDefinition('child', new DefinitionDecorator('parent'))
;
$this->process($container);
$def = $container->getDefinition('child');
$this->assertEquals(array(), $def->getTags());
}
public function testProcessHandlesMultipleInheritance()
{
$container = new ContainerBuilder();
$container
->register('parent', 'foo')
->setArguments(array('foo', 'bar', 'c'))
;
$container
->setDefinition('child2', new DefinitionDecorator('child1'))
->replaceArgument(1, 'b')
;
$container
->setDefinition('child1', new DefinitionDecorator('parent'))
->replaceArgument(0, 'a')
;
$this->process($container);
$def = $container->getDefinition('child2');
$this->assertEquals(array('a', 'b', 'c'), $def->getArguments());
$this->assertEquals('foo', $def->getClass());
}
public function testSetLazyOnServiceHasParent()
{
$container = new ContainerBuilder();
$container->register('parent', 'stdClass');
$container->setDefinition('child1', new DefinitionDecorator('parent'))
->setLazy(true)
;
$this->process($container);
$this->assertTrue($container->getDefinition('child1')->isLazy());
}
public function testSetLazyOnServiceIsParent()
{
$container = new ContainerBuilder();
$container->register('parent', 'stdClass')
->setLazy(true)
;
$container->setDefinition('child1', new DefinitionDecorator('parent'));
$this->process($container);
$this->assertTrue($container->getDefinition('child1')->isLazy());
}
protected function process(ContainerBuilder $container)
{
$pass = new ResolveDefinitionTemplatesPass();
$pass->process($container);
}
}

View file

@ -0,0 +1,83 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Compiler\ResolveInvalidReferencesPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ResolveInvalidReferencesPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$def = $container
->register('foo')
->setArguments(array(new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE)))
->addMethodCall('foo', array(new Reference('moo', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))
;
$this->process($container);
$arguments = $def->getArguments();
$this->assertNull($arguments[0]);
$this->assertCount(0, $def->getMethodCalls());
}
public function testProcessIgnoreNonExistentServices()
{
$container = new ContainerBuilder();
$def = $container
->register('foo')
->setArguments(array(new Reference('bar')))
;
$this->process($container);
$arguments = $def->getArguments();
$this->assertEquals('bar', (string) $arguments[0]);
}
public function testProcessRemovesPropertiesOnInvalid()
{
$container = new ContainerBuilder();
$def = $container
->register('foo')
->setProperty('foo', new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))
;
$this->process($container);
$this->assertEquals(array(), $def->getProperties());
}
public function testStrictFlagIsPreserved()
{
$container = new ContainerBuilder();
$container->register('bar');
$def = $container
->register('foo')
->addArgument(new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE, false))
;
$this->process($container);
$this->assertFalse($def->getArgument(0)->isStrict());
}
protected function process(ContainerBuilder $container)
{
$pass = new ResolveInvalidReferencesPass();
$pass->process($container);
}
}

View file

@ -0,0 +1,91 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ResolveParameterPlaceHoldersPassTest extends \PHPUnit_Framework_TestCase
{
private $compilerPass;
private $container;
private $fooDefinition;
protected function setUp()
{
$this->compilerPass = new ResolveParameterPlaceHoldersPass();
$this->container = $this->createContainerBuilder();
$this->compilerPass->process($this->container);
$this->fooDefinition = $this->container->getDefinition('foo');
}
public function testClassParametersShouldBeResolved()
{
$this->assertSame('Foo', $this->fooDefinition->getClass());
}
public function testFactoryParametersShouldBeResolved()
{
$this->assertSame(array('FooFactory', 'getFoo'), $this->fooDefinition->getFactory());
}
public function testArgumentParametersShouldBeResolved()
{
$this->assertSame(array('bar', 'baz'), $this->fooDefinition->getArguments());
}
public function testMethodCallParametersShouldBeResolved()
{
$this->assertSame(array(array('foobar', array('bar', 'baz'))), $this->fooDefinition->getMethodCalls());
}
public function testPropertyParametersShouldBeResolved()
{
$this->assertSame(array('bar' => 'baz'), $this->fooDefinition->getProperties());
}
public function testFileParametersShouldBeResolved()
{
$this->assertSame('foo.php', $this->fooDefinition->getFile());
}
public function testAliasParametersShouldBeResolved()
{
$this->assertSame('foo', $this->container->getAlias('bar')->__toString());
}
private function createContainerBuilder()
{
$containerBuilder = new ContainerBuilder();
$containerBuilder->setParameter('foo.class', 'Foo');
$containerBuilder->setParameter('foo.factory.class', 'FooFactory');
$containerBuilder->setParameter('foo.arg1', 'bar');
$containerBuilder->setParameter('foo.arg2', 'baz');
$containerBuilder->setParameter('foo.method', 'foobar');
$containerBuilder->setParameter('foo.property.name', 'bar');
$containerBuilder->setParameter('foo.property.value', 'baz');
$containerBuilder->setParameter('foo.file', 'foo.php');
$containerBuilder->setParameter('alias.id', 'bar');
$fooDefinition = $containerBuilder->register('foo', '%foo.class%');
$fooDefinition->setFactory(array('%foo.factory.class%', 'getFoo'));
$fooDefinition->setArguments(array('%foo.arg1%', '%foo.arg2%'));
$fooDefinition->addMethodCall('%foo.method%', array('%foo.arg1%', '%foo.arg2%'));
$fooDefinition->setProperty('%foo.property.name%', '%foo.property.value%');
$fooDefinition->setFile('%foo.file%');
$containerBuilder->setAlias('%alias.id%', 'foo');
return $containerBuilder;
}
}

Some files were not shown because too many files have changed in this diff Show more