use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Parameter;
+use Symfony\Component\DependencyInjection\Exception\EnvParameterException;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
use Symfony\Component\DependencyInjection\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\Expression;
-use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
use Symfony\Component\HttpKernel\Kernel;
/**
private $targetDirRegex;
private $targetDirMaxMatches;
private $docStar;
-
- /**
- * @var ExpressionFunctionProviderInterface[]
- */
- private $expressionLanguageProviders = array();
+ private $serviceIdToMethodNameMap;
+ private $usedMethodNames;
/**
* @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
* @param array $options An array of options
*
* @return string A PHP class representing of the service container
+ *
+ * @throws EnvParameterException When an env var exists but has not been dumped
*/
public function dump(array $options = array())
{
'namespace' => '',
'debug' => true,
), $options);
+
+ $this->initializeMethodNamesMap($options['base_class']);
+
$this->docStar = $options['debug'] ? '*' : '';
if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) {
;
$this->targetDirRegex = null;
+ $unusedEnvs = array();
+ foreach ($this->container->getEnvCounters() as $env => $use) {
+ if (!$use) {
+ $unusedEnvs[] = $env;
+ }
+ }
+ if ($unusedEnvs) {
+ throw new EnvParameterException($unusedEnvs, null, 'Environment variables "%s" are never used. Please, check your container\'s configuration.');
+ }
+
return $code;
}
/**
* Generates the require_once statement for service includes.
*
- * @param string $id The service id
* @param Definition $definition
*
* @return string
*/
- private function addServiceInclude($id, $definition)
+ private function addServiceInclude($definition)
{
$template = " require_once %s;\n";
$code = '';
throw new ServiceCircularReferenceException($id, array($id));
}
- $code .= $this->addNewInstance($id, $sDefinition, '$'.$name, ' = ');
+ $code .= $this->addNewInstance($sDefinition, '$'.$name, ' = ', $id);
if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) {
- $code .= $this->addServiceProperties(null, $sDefinition, $name);
- $code .= $this->addServiceMethodCalls(null, $sDefinition, $name);
- $code .= $this->addServiceConfigurator(null, $sDefinition, $name);
+ $code .= $this->addServiceProperties($sDefinition, $name);
+ $code .= $this->addServiceMethodCalls($sDefinition, $name);
+ $code .= $this->addServiceConfigurator($sDefinition, $name);
}
$code .= "\n";
*/
private function addServiceInstance($id, Definition $definition)
{
- $class = $definition->getClass();
-
- if ('\\' === substr($class, 0, 1)) {
- $class = substr($class, 1);
- }
-
- $class = $this->dumpValue($class);
+ $class = $this->dumpValue($definition->getClass());
- if (0 === strpos($class, "'") && !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
+ if (0 === strpos($class, "'") && false === strpos($class, '$') && !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
}
$isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
$instantiation = '';
- if (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_CONTAINER === $definition->getScope(false)) {
+ if (!$isProxyCandidate && $definition->isShared()) {
$instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
- } elseif (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) {
- $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance');
} elseif (!$simple) {
$instantiation = '$instance';
}
$instantiation .= ' = ';
}
- $code = $this->addNewInstance($id, $definition, $return, $instantiation);
+ $code = $this->addNewInstance($definition, $return, $instantiation, $id);
if (!$simple) {
$code .= "\n";
/**
* Adds method calls to a service definition.
*
- * @param string $id
* @param Definition $definition
* @param string $variableName
*
* @return string
*/
- private function addServiceMethodCalls($id, Definition $definition, $variableName = 'instance')
+ private function addServiceMethodCalls(Definition $definition, $variableName = 'instance')
{
$calls = '';
foreach ($definition->getMethodCalls() as $call) {
return $calls;
}
- private function addServiceProperties($id, Definition $definition, $variableName = 'instance')
+ private function addServiceProperties(Definition $definition, $variableName = 'instance')
{
$code = '';
foreach ($definition->getProperties() as $name => $value) {
}
$name = (string) $this->definitionVariables->offsetGet($iDefinition);
- $code .= $this->addServiceProperties(null, $iDefinition, $name);
- $code .= $this->addServiceMethodCalls(null, $iDefinition, $name);
- $code .= $this->addServiceConfigurator(null, $iDefinition, $name);
+ $code .= $this->addServiceProperties($iDefinition, $name);
+ $code .= $this->addServiceMethodCalls($iDefinition, $name);
+ $code .= $this->addServiceConfigurator($iDefinition, $name);
}
if ('' !== $code) {
/**
* Adds configurator definition.
*
- * @param string $id
* @param Definition $definition
* @param string $variableName
*
* @return string
*/
- private function addServiceConfigurator($id, Definition $definition, $variableName = 'instance')
+ private function addServiceConfigurator(Definition $definition, $variableName = 'instance')
{
if (!$callable = $definition->getConfigurator()) {
return '';
$class = $this->dumpValue($callable[0]);
// If the class is a string we can optimize call_user_func away
- if (strpos($class, "'") === 0) {
+ if (0 === strpos($class, "'") && false === strpos($class, '$')) {
return sprintf(" %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName);
}
+ if (0 === strpos($class, 'new ')) {
+ return sprintf(" (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
+ }
+
return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
}
if ($definition->isSynthetic()) {
$return[] = '@throws RuntimeException always since this service is expected to be injected dynamically';
} elseif ($class = $definition->getClass()) {
- $return[] = sprintf('@return %s A %s instance', 0 === strpos($class, '%') ? 'object' : '\\'.ltrim($class, '\\'), ltrim($class, '\\'));
+ $class = $this->container->resolveEnvPlaceholders($class);
+ $return[] = sprintf(0 === strpos($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\'));
} elseif ($definition->getFactory()) {
$factory = $definition->getFactory();
if (is_string($factory)) {
$return[] = sprintf('@return object An instance returned by %s::%s()', $factory[0]->getClass(), $factory[1]);
}
}
- } elseif ($definition->getFactoryClass(false)) {
- $return[] = sprintf('@return object An instance returned by %s::%s()', $definition->getFactoryClass(false), $definition->getFactoryMethod(false));
- } elseif ($definition->getFactoryService(false)) {
- $return[] = sprintf('@return object An instance returned by %s::%s()', $definition->getFactoryService(false), $definition->getFactoryMethod(false));
- }
-
- $scope = $definition->getScope(false);
- if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
- if ($return && 0 === strpos($return[count($return) - 1], '@return')) {
- $return[] = '';
- }
- $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope);
}
if ($definition->isDeprecated()) {
}
$return = str_replace("\n * \n", "\n *\n", implode("\n * ", $return));
+ $return = $this->container->resolveEnvPlaceholders($return);
- $doc = '';
- if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
- $doc .= <<<'EOF'
-
- *
- * This service is shared.
- * This method always returns the same instance of the service.
-EOF;
- }
-
- if (!$definition->isPublic()) {
- $doc .= <<<'EOF'
-
- *
- * This service is private.
- * If you want to be able to request this service from the container directly,
- * make it public, otherwise you might end up with broken code.
-EOF;
- }
-
- if ($definition->isAutowired()) {
- $doc .= <<<EOF
-
- *
- * This service is autowired.
-EOF;
- }
+ $shared = $definition->isShared() ? ' shared' : '';
+ $public = $definition->isPublic() ? 'public' : 'private';
+ $autowired = $definition->isAutowired() ? ' autowired' : '';
if ($definition->isLazy()) {
$lazyInitialization = '$lazyLoad = true';
- $lazyInitializationDoc = "\n * @param bool \$lazyLoad whether to try lazy-loading the service with a proxy\n *";
} else {
$lazyInitialization = '';
- $lazyInitializationDoc = '';
}
// with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer
$isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
$visibility = $isProxyCandidate ? 'public' : 'protected';
+ $methodName = $this->generateMethodName($id);
$code = <<<EOF
/*{$this->docStar}
- * Gets the '$id' service.$doc
- *$lazyInitializationDoc
+ * Gets the $public '$id'$shared$autowired service.
+ *
* $return
*/
- {$visibility} function get{$this->camelize($id)}Service($lazyInitialization)
+ {$visibility} function {$methodName}($lazyInitialization)
{
EOF;
- $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id) : '';
-
- if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
- $code .= <<<EOF
- if (!isset(\$this->scopedServices['$scope'])) {
- throw new InactiveScopeException('$id', '$scope');
- }
-
-
-EOF;
- }
+ $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id, $methodName) : '';
if ($definition->isSynthetic()) {
$code .= sprintf(" throw new RuntimeException('You have requested a synthetic service (\"%s\"). The DIC does not know how to construct this service.');\n }\n", $id);
} else {
if ($definition->isDeprecated()) {
- $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", var_export($definition->getDeprecationMessage($id), true));
+ $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
}
$code .=
- $this->addServiceInclude($id, $definition).
+ $this->addServiceInclude($definition).
$this->addServiceLocalTempVariables($id, $definition).
$this->addServiceInlinedDefinitions($id, $definition).
$this->addServiceInstance($id, $definition).
$this->addServiceInlinedDefinitionsSetup($id, $definition).
- $this->addServiceProperties($id, $definition).
- $this->addServiceMethodCalls($id, $definition).
- $this->addServiceConfigurator($id, $definition).
+ $this->addServiceProperties($definition).
+ $this->addServiceMethodCalls($definition).
+ $this->addServiceConfigurator($definition).
$this->addServiceReturn($id, $definition)
;
}
*/
private function addServices()
{
- $publicServices = $privateServices = $synchronizers = '';
+ $publicServices = $privateServices = '';
$definitions = $this->container->getDefinitions();
ksort($definitions);
foreach ($definitions as $id => $definition) {
} else {
$privateServices .= $this->addService($id, $definition);
}
-
- $synchronizers .= $this->addServiceSynchronizer($id, $definition);
}
- return $publicServices.$synchronizers.$privateServices;
+ return $publicServices.$privateServices;
}
- /**
- * Adds synchronizer methods.
- *
- * @param string $id A service identifier
- * @param Definition $definition A Definition instance
- *
- * @return string|null
- *
- * @deprecated since version 2.7, will be removed in 3.0.
- */
- private function addServiceSynchronizer($id, Definition $definition)
- {
- if (!$definition->isSynchronized(false)) {
- return;
- }
-
- if ('request' !== $id) {
- @trigger_error('Synchronized services were deprecated in version 2.7 and won\'t work anymore in 3.0.', E_USER_DEPRECATED);
- }
-
- $code = '';
- foreach ($this->container->getDefinitions() as $definitionId => $definition) {
- foreach ($definition->getMethodCalls() as $call) {
- foreach ($call[1] as $argument) {
- if ($argument instanceof Reference && $id == (string) $argument) {
- $arguments = array();
- foreach ($call[1] as $value) {
- $arguments[] = $this->dumpValue($value);
- }
-
- $call = $this->wrapServiceConditionals($call[1], sprintf("\$this->get('%s')->%s(%s);", $definitionId, $call[0], implode(', ', $arguments)));
-
- $code .= <<<EOF
- if (\$this->initialized('$definitionId')) {
- $call
- }
-
-EOF;
- }
- }
- }
- }
-
- if (!$code) {
- return;
- }
-
- return <<<EOF
-
- /*{$this->docStar}
- * Updates the '$id' service.
- */
- protected function synchronize{$this->camelize($id)}Service()
- {
-$code }
-
-EOF;
- }
-
- private function addNewInstance($id, Definition $definition, $return, $instantiation)
+ private function addNewInstance(Definition $definition, $return, $instantiation, $id)
{
$class = $this->dumpValue($definition->getClass());
$class = $this->dumpValue($callable[0]);
// If the class is a string we can optimize call_user_func away
- if (strpos($class, "'") === 0) {
+ if (0 === strpos($class, "'") && false === strpos($class, '$')) {
+ if ("''" === $class) {
+ throw new RuntimeException(sprintf('Cannot dump definition: The "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id));
+ }
+
return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '');
}
- return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
- }
-
- return sprintf(" $return{$instantiation}\\%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : '');
- } elseif (null !== $definition->getFactoryMethod(false)) {
- if (null !== $definition->getFactoryClass(false)) {
- $class = $this->dumpValue($definition->getFactoryClass(false));
-
- // If the class is a string we can optimize call_user_func away
- if (strpos($class, "'") === 0) {
- return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $definition->getFactoryMethod(false), $arguments ? implode(', ', $arguments) : '');
+ if (0 === strpos($class, 'new ')) {
+ return sprintf(" $return{$instantiation}(%s)->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '');
}
- return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass(false)), $definition->getFactoryMethod(false), $arguments ? ', '.implode(', ', $arguments) : '');
- }
-
- if (null !== $definition->getFactoryService(false)) {
- return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService(false)), $definition->getFactoryMethod(false), implode(', ', $arguments));
+ return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
}
- throw new RuntimeException(sprintf('Factory method requires a factory service or factory class in service definition for %s', $id));
+ return sprintf(" $return{$instantiation}%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : '');
}
if (false !== strpos($class, '$')) {
$namespaceLine
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Container;
-use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
EOF;
- if (count($scopes = $this->container->getScopes(false)) > 0) {
- $code .= "\n";
- $code .= ' $this->scopes = '.$this->dumpValue($scopes).";\n";
- $code .= ' $this->scopeChildren = '.$this->dumpValue($this->container->getScopeChildren(false)).";\n";
- }
-
$code .= $this->addMethodMap();
+ $code .= $this->addPrivateServices();
$code .= $this->addAliases();
$code .= <<<'EOF'
$code .= "\n \$this->parameters = \$this->getDefaultParameters();\n";
}
- $code .= <<<'EOF'
-
- $this->services =
- $this->scopedServices =
- $this->scopeStacks = array();
-EOF;
-
- $code .= "\n";
- if (count($scopes = $this->container->getScopes(false)) > 0) {
- $code .= ' $this->scopes = '.$this->dumpValue($scopes).";\n";
- $code .= ' $this->scopeChildren = '.$this->dumpValue($this->container->getScopeChildren(false)).";\n";
- } else {
- $code .= " \$this->scopes = array();\n";
- $code .= " \$this->scopeChildren = array();\n";
- }
-
+ $code .= "\n \$this->services = array();\n";
$code .= $this->addMethodMap();
+ $code .= $this->addPrivateServices();
$code .= $this->addAliases();
$code .= <<<'EOF'
$code = " \$this->methodMap = array(\n";
ksort($definitions);
foreach ($definitions as $id => $definition) {
- $code .= ' '.var_export($id, true).' => '.var_export('get'.$this->camelize($id).'Service', true).",\n";
+ $code .= ' '.$this->export($id).' => '.$this->export($this->generateMethodName($id)).",\n";
}
return $code." );\n";
}
+ /**
+ * Adds the privates property definition.
+ *
+ * @return string
+ */
+ private function addPrivateServices()
+ {
+ if (!$definitions = $this->container->getDefinitions()) {
+ return '';
+ }
+
+ $code = '';
+ ksort($definitions);
+ foreach ($definitions as $id => $definition) {
+ if (!$definition->isPublic()) {
+ $code .= ' '.$this->export($id)." => true,\n";
+ }
+ }
+
+ if (empty($code)) {
+ return '';
+ }
+
+ $out = " \$this->privates = array(\n";
+ $out .= $code;
+ $out .= " );\n";
+
+ return $out;
+ }
+
/**
* Adds the aliases property definition.
*
while (isset($aliases[$id])) {
$id = (string) $aliases[$id];
}
- $code .= ' '.var_export($alias, true).' => '.var_export($id, true).",\n";
+ $code .= ' '.$this->export($alias).' => '.$this->export($id).",\n";
}
return $code." );\n";
return '';
}
- $parameters = $this->exportParameters($this->container->getParameterBag()->all());
+ $php = array();
+ $dynamicPhp = array();
+
+ foreach ($this->container->getParameterBag()->all() as $key => $value) {
+ if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) {
+ throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: %s.', $resolvedKey));
+ }
+ $export = $this->exportParameters(array($value));
+ $export = explode('0 => ', substr(rtrim($export, " )\n"), 7, -1), 2);
+
+ if (preg_match("/\\\$this->(?:getEnv\('\w++'\)|targetDirs\[\d++\])/", $export[1])) {
+ $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]);
+ } else {
+ $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]);
+ }
+ }
+ $parameters = sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', 8));
$code = '';
if ($this->container->isFrozen()) {
{
$name = strtolower($name);
- if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
+ if (isset($this->loadedDynamicParameters[$name])) {
+ return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
+ }
return $this->parameters[$name];
}
{
$name = strtolower($name);
- return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]);
}
/**
public function getParameterBag()
{
if (null === $this->parameterBag) {
- $this->parameterBag = new FrozenParameterBag($this->parameters);
+ $parameters = $this->parameters;
+ foreach ($this->loadedDynamicParameters as $name => $loaded) {
+ $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
+ }
+ $this->parameterBag = new FrozenParameterBag($parameters);
}
return $this->parameterBag;
if ('' === $this->docStar) {
$code = str_replace('/**', '/*', $code);
}
+
+ if ($dynamicPhp) {
+ $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, count($dynamicPhp), false)), '', 8);
+ $getDynamicParameter = <<<'EOF'
+ switch ($name) {
+%s
+ default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%%s" must be defined.', $name));
+ }
+ $this->loadedDynamicParameters[$name] = true;
+
+ return $this->dynamicParameters[$name] = $value;
+EOF;
+ $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp));
+ } else {
+ $loadedDynamicParameters = 'array()';
+ $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));';
+ }
+
+ $code .= <<<EOF
+
+ private \$loadedDynamicParameters = {$loadedDynamicParameters};
+ private \$dynamicParameters = array();
+
+ /*{$this->docStar}
+ * Computes a dynamic parameter.
+ *
+ * @param string The name of the dynamic parameter to load
+ *
+ * @return mixed The value of the dynamic parameter
+ *
+ * @throws InvalidArgumentException When the dynamic parameter does not exist
+ */
+ private function getDynamicParameter(\$name)
+ {
+{$getDynamicParameter}
+ }
+
+EOF;
+ } elseif ($dynamicPhp) {
+ throw new RuntimeException('You cannot dump a not-frozen container with dynamic parameters.');
}
$code .= <<<EOF
$value = $this->export($value);
}
- $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
+ $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), $this->export($key), $value);
}
return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
$conditions = array();
foreach ($services as $service) {
+ if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) {
+ continue;
+ }
+
$conditions[] = sprintf("\$this->has('%s')", $service);
}
+ if (!$conditions) {
+ return $code;
+ }
+
// re-indent the wrapped code
$code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code)));
$factory = $value->getFactory();
if (is_string($factory)) {
- return sprintf('\\%s(%s)', $factory, implode(', ', $arguments));
+ return sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory)), implode(', ', $arguments));
}
if (is_array($factory)) {
throw new RuntimeException('Cannot dump definition because of invalid factory');
}
- if (null !== $value->getFactoryMethod(false)) {
- if (null !== $value->getFactoryClass(false)) {
- return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass(false)), $value->getFactoryMethod(false), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
- } elseif (null !== $value->getFactoryService(false)) {
- $service = $this->dumpValue($value->getFactoryService(false));
-
- return sprintf('%s->%s(%s)', 0 === strpos($service, '$') ? sprintf('$this->get(%s)', $service) : $this->getServiceCall($value->getFactoryService(false)), $value->getFactoryMethod(false), implode(', ', $arguments));
- }
-
- throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
- }
-
$class = $value->getClass();
if (null === $class) {
throw new RuntimeException('Cannot dump definitions which have no class nor factory.');
// the preg_replace_callback converts them to strings
return $this->dumpParameter(strtolower($match[1]));
} else {
- $that = $this;
- $replaceParameters = function ($match) use ($that) {
- return "'.".$that->dumpParameter(strtolower($match[2])).".'";
+ $replaceParameters = function ($match) {
+ return "'.".$this->dumpParameter(strtolower($match[2])).".'";
};
$code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value)));
private function dumpLiteralClass($class)
{
if (false !== strpos($class, '$')) {
- throw new RuntimeException('Cannot dump definitions which have a variable class name.');
+ return sprintf('${($_ = %s) && false ?: "_"}', $class);
}
- if (0 !== strpos($class, "'") || !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
+ if (0 !== strpos($class, "'") || !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s)', $class ?: 'n/a'));
}
- return '\\'.substr(str_replace('\\\\', '\\', $class), 1, -1);
+ $class = substr(str_replace('\\\\', '\\', $class), 1, -1);
+
+ return 0 === strpos($class, '\\') ? $class : '\\'.$class;
}
/**
*
* @return string
*/
- public function dumpParameter($name)
+ private function dumpParameter($name)
{
if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
return $this->dumpValue($this->container->getParameter($name), false);
return sprintf("\$this->getParameter('%s')", strtolower($name));
}
- /**
- * @deprecated since version 2.6.2, to be removed in 3.0.
- * Use \Symfony\Component\DependencyInjection\ContainerBuilder::addExpressionLanguageProvider instead.
- *
- * @param ExpressionFunctionProviderInterface $provider
- */
- public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
- {
- @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6.2 and will be removed in 3.0. Use the Symfony\Component\DependencyInjection\ContainerBuilder::addExpressionLanguageProvider method instead.', E_USER_DEPRECATED);
-
- $this->expressionLanguageProviders[] = $provider;
- }
-
/**
* Gets a service call.
*
*/
private function getServiceCall($id, Reference $reference = null)
{
+ while ($this->container->hasAlias($id)) {
+ $id = (string) $this->container->getAlias($id);
+ }
+
if ('service_container' === $id) {
return '$this';
}
+ if ($this->container->hasDefinition($id) && !$this->container->getDefinition($id)->isPublic()) {
+ // The following is PHP 5.5 syntax for what could be written as "(\$this->services['$id'] ?? \$this->{$this->generateMethodName($id)}())" on PHP>=7.0
+
+ return "\${(\$_ = isset(\$this->services['$id']) ? \$this->services['$id'] : \$this->{$this->generateMethodName($id)}()) && false ?: '_'}";
+ }
if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
}
- if ($this->container->hasAlias($id)) {
- $id = (string) $this->container->getAlias($id);
- }
-
return sprintf('$this->get(\'%s\')', $id);
}
+ /**
+ * Initializes the method names map to avoid conflicts with the Container methods.
+ *
+ * @param string $class the container base class
+ */
+ private function initializeMethodNamesMap($class)
+ {
+ $this->serviceIdToMethodNameMap = array();
+ $this->usedMethodNames = array();
+
+ try {
+ $reflectionClass = new \ReflectionClass($class);
+ foreach ($reflectionClass->getMethods() as $method) {
+ $this->usedMethodNames[strtolower($method->getName())] = true;
+ }
+ } catch (\ReflectionException $e) {
+ }
+ }
+
/**
* Convert a service id to a valid PHP method name.
*
*
* @throws InvalidArgumentException
*/
- private function camelize($id)
+ private function generateMethodName($id)
{
+ if (isset($this->serviceIdToMethodNameMap[$id])) {
+ return $this->serviceIdToMethodNameMap[$id];
+ }
+
$name = Container::camelize($id);
+ $name = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $name);
+ $methodName = 'get'.$name.'Service';
+ $suffix = 1;
- if (!preg_match('/^[a-zA-Z0-9_\x7f-\xff]+$/', $name)) {
- throw new InvalidArgumentException(sprintf('Service id "%s" cannot be converted to a valid PHP method name.', $id));
+ while (isset($this->usedMethodNames[strtolower($methodName)])) {
+ ++$suffix;
+ $methodName = 'get'.$name.$suffix.'Service';
}
- return $name;
+ $this->serviceIdToMethodNameMap[$id] = $methodName;
+ $this->usedMethodNames[strtolower($methodName)] = true;
+
+ return $methodName;
}
/**
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
}
- $providers = array_merge($this->container->getExpressionLanguageProviders(), $this->expressionLanguageProviders);
- $this->expressionLanguage = new ExpressionLanguage(null, $providers);
+ $providers = $this->container->getExpressionLanguageProviders();
+ $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) {
+ $id = '""' === substr_replace($arg, '', 1, -1) ? stripcslashes(substr($arg, 1, -1)) : null;
+
+ if (null !== $id && ($this->container->hasAlias($id) || $this->container->hasDefinition($id))) {
+ return $this->getServiceCall($id);
+ }
+
+ return sprintf('$this->get(%s)', $arg);
+ });
if ($this->container->isTrackingResources()) {
foreach ($providers as $provider) {
private function export($value)
{
if (null !== $this->targetDirRegex && is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) {
- $prefix = $matches[0][1] ? var_export(substr($value, 0, $matches[0][1]), true).'.' : '';
+ $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1])).'.' : '';
$suffix = $matches[0][1] + strlen($matches[0][0]);
- $suffix = isset($value[$suffix]) ? '.'.var_export(substr($value, $suffix), true) : '';
+ $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix)) : '';
$dirname = '__DIR__';
if (0 < $offset = 1 + $this->targetDirMaxMatches - count($matches)) {
return $dirname;
}
- return var_export($value, true);
+ return $this->doExport($value);
+ }
+
+ private function doExport($value)
+ {
+ $export = var_export($value, true);
+
+ if ("'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('%s').'")) {
+ $export = $resolvedExport;
+ if ("'" === $export[1]) {
+ $export = substr($export, 3);
+ }
+ if (".''" === substr($export, -3)) {
+ $export = substr($export, 0, -3);
+ }
+ }
+
+ return $export;
}
}