From edd705716aec97d1db1c3603196102bf852e5113 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 30 Nov 2014 16:07:59 +0100 Subject: [PATCH] [DependencyInjection] make paths relative to __DIR__ in the generated container --- .../DependencyInjection/Dumper/PhpDumper.php | 55 ++++++++- .../Tests/Dumper/PhpDumperTest.php | 22 +++- .../Tests/Fixtures/php/services12.php | 110 ++++++++++++++++++ src/Symfony/Component/HttpKernel/Kernel.php | 2 +- 4 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 9189c45ca576..01fbcd85cf86 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -51,6 +51,8 @@ class PhpDumper extends Dumper private $referenceVariables; private $variableCount; private $reservedVariables = array('instance', 'class'); + private $targetDirRegex; + private $targetDirMaxMatches; /** * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface @@ -95,11 +97,35 @@ public function setProxyDumper(ProxyDumper $proxyDumper) */ public function dump(array $options = array()) { + $this->targetDirRegex = null; $options = array_merge(array( 'class' => 'ProjectServiceContainer', 'base_class' => 'Container', ), $options); + if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) { + // Build a regexp where the first two root dirs are mandatory, + // but every other sub-dir is optional up to the full path in $dir + + $dir = explode(DIRECTORY_SEPARATOR, realpath($dir)); + $i = count($dir); + + if (3 <= $i) { + $regex = ''; + $this->targetDirMaxMatches = $i - 3; + + while (2 < --$i) { + $regex = sprintf('(%s%s)?', preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex); + } + + do { + $regex = preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#').$regex; + } while (0 < --$i); + + $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#'; + } + } + $code = $this->startClass($options['class'], $options['base_class']); if ($this->container->isFrozen()) { @@ -114,6 +140,7 @@ public function dump(array $options = array()) $this->endClass(). $this->addProxyClasses() ; + $this->targetDirRegex = null; return $code; } @@ -979,7 +1006,7 @@ private function exportParameters($parameters, $path = '', $indent = 12) } elseif ($value instanceof Reference) { throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain references to other services (reference to service "%s" found in "%s").', $value, $path.'/'.$key)); } else { - $value = var_export($value, true); + $value = $this->export($value); } $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value); @@ -1214,14 +1241,14 @@ private function dumpValue($value, $interpolate = true) return "'.".$that->dumpParameter(strtolower($match[2])).".'"; }; - $code = str_replace('%%', '%', preg_replace_callback('/(?export($value))); return $code; } } 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.'); } else { - return var_export($value, true); + return $this->export($value); } } @@ -1323,4 +1350,26 @@ private function getNextVariableName() return $name; } } + + 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).'.' : ''; + $suffix = $matches[0][1] + strlen($matches[0][0]); + $suffix = isset($value[$suffix]) ? '.'.var_export(substr($value, $suffix), true) : ''; + $dirname = '__DIR__'; + + for ($i = $this->targetDirMaxMatches - count($matches); 0 <= $i; --$i) { + $dirname = sprintf('dirname(%s)', $dirname); + } + + if ($prefix || $suffix) { + return sprintf('(%s%s%s)', $prefix, $dirname, $suffix); + } + + return $dirname; + } + + return var_export($value, true); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 1900af185161..cd3ad9b72756 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -80,6 +80,24 @@ public function testDumpOptimizationString() $this->assertStringEqualsFile(self::$fixturesPath.'/php/services10.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class'); } + public function testDumpRelativeDir() + { + $definition = new Definition(); + $definition->setClass('stdClass'); + $definition->addArgument('%foo%'); + $definition->addArgument(array('%foo%' => '%foo%')); + + $container = new ContainerBuilder(); + $container->setDefinition('test', $definition); + $container->setParameter('foo', 'wiz'.dirname(dirname(__FILE__))); + $container->setParameter('bar', dirname(__FILE__)); + $container->setParameter('baz', '%bar%/PhpDumperTest.php'); + $container->compile(); + + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services12.php', $dumper->dump(array('file' => __FILE__)), '->dump() dumps __DIR__ relative strings'); + } + /** * @expectedException \InvalidArgumentException */ @@ -101,13 +119,13 @@ public function testAddService() // without compilation $container = include self::$fixturesPath.'/containers/container9.php'; $dumper = new PhpDumper($container); - $this->assertEquals(str_replace('%path%', str_replace('\\','\\\\',self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9.php')), $dumper->dump(), '->dump() dumps services'); + $this->assertEquals(str_replace('%path%', str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9.php')), $dumper->dump(), '->dump() dumps services'); // with compilation $container = include self::$fixturesPath.'/containers/container9.php'; $container->compile(); $dumper = new PhpDumper($container); - $this->assertEquals(str_replace('%path%', str_replace('\\','\\\\',self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9_compiled.php')), $dumper->dump(), '->dump() dumps services'); + $this->assertEquals(str_replace('%path%', str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9_compiled.php')), $dumper->dump(), '->dump() dumps services'); $dumper = new PhpDumper($container = new ContainerBuilder()); $container->register('foo', 'FooClass')->addArgument(new \stdClass()); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php new file mode 100644 index 000000000000..52d2702afd75 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php @@ -0,0 +1,110 @@ +parameters = $this->getDefaultParameters(); + + $this->services = + $this->scopedServices = + $this->scopeStacks = array(); + + $this->set('service_container', $this); + + $this->scopes = array(); + $this->scopeChildren = array(); + $this->methodMap = array( + 'test' => 'getTestService', + ); + + $this->aliases = array(); + } + + /** + * Gets the 'test' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getTestService() + { + return $this->services['test'] = new \stdClass(('wiz'.dirname(__DIR__)), array(('wiz'.dirname(__DIR__)) => ('wiz'.dirname(__DIR__)))); + } + + /** + * {@inheritdoc} + */ + public function getParameter($name) + { + $name = strtolower($name); + + if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + + return $this->parameters[$name]; + } + + /** + * {@inheritdoc} + */ + public function hasParameter($name) + { + $name = strtolower($name); + + return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters); + } + + /** + * {@inheritdoc} + */ + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function getParameterBag() + { + if (null === $this->parameterBag) { + $this->parameterBag = new FrozenParameterBag($this->parameters); + } + + return $this->parameterBag; + } + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'foo' => ('wiz'.dirname(__DIR__)), + 'bar' => __DIR__, + 'baz' => (__DIR__.'/PhpDumperTest.php'), + ); + } +} diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 08a2a6ac56b9..6a598cf9d8f2 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -710,7 +710,7 @@ protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container $dumper->setProxyDumper(new ProxyDumper()); } - $content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass)); + $content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass, 'file' => (string) $cache)); if (!$this->debug) { $content = static::stripComments($content); }