Skip to content

Commit

Permalink
bug #12784 [DependencyInjection] make paths relative to __DIR__ in th…
Browse files Browse the repository at this point in the history
…e generated container (nicolas-grekas)

This PR was merged into the 2.3 branch.

Discussion
----------

[DependencyInjection] make paths relative to __DIR__ in the generated container

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #6484, #3079, partially #9238, #10894, #10999
| License       | MIT
| Doc PR        | n/a

This is an alternative approach to #10999 for removing absolute paths from the generated container:
instead of trying to fix the container file after it has been dumped, telling to the PhpDumper where its output will be written allows it to replace parts of strings by an equivalent value based on `__DIR__`.
This should be safe, thus the PR is on 2.3.

Commits
-------

edd7057 [DependencyInjection] make paths relative to __DIR__ in the generated container
  • Loading branch information
fabpot committed Dec 2, 2014
2 parents 9e0dff5 + edd7057 commit b604b0a
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 6 deletions.
55 changes: 52 additions & 3 deletions src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Expand Up @@ -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
Expand Down Expand Up @@ -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()) {
Expand All @@ -114,6 +140,7 @@ public function dump(array $options = array())
$this->endClass().
$this->addProxyClasses()
;
$this->targetDirRegex = null;

return $code;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1214,14 +1241,14 @@ private function dumpValue($value, $interpolate = true)
return "'.".$that->dumpParameter(strtolower($match[2])).".'";
};

$code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, var_export($value, true)));
$code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->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);
}
}

Expand Down Expand Up @@ -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);
}
}
Expand Up @@ -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
*/
Expand All @@ -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());
Expand Down
@@ -0,0 +1,110 @@
<?php

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;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;

/**
* ProjectServiceContainer
*
* This class has been auto-generated
* by the Symfony Dependency Injection Component.
*/
class ProjectServiceContainer extends Container
{
/**
* Constructor.
*/
public function __construct()
{
$this->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'),
);
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Component/HttpKernel/Kernel.php
Expand Up @@ -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);
}
Expand Down

0 comments on commit b604b0a

Please sign in to comment.