Skip to content

[Config][DI] Add ComposerResource to track the runtime engine + deps #21505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\Resource\FileResource;

/**
* This abstract classes groups common code that Doctrine Object Manager extensions (ORM, MongoDB, CouchDB) need.
Expand Down Expand Up @@ -268,30 +267,28 @@ protected function assertValidMappingConfiguration(array $mappingConfig, $object
*/
protected function detectMetadataDriver($dir, ContainerBuilder $container)
{
// add the closest existing directory as a resource
$configPath = $this->getMappingResourceConfigDirectory();
$resource = $dir.'/'.$configPath;
while (!is_dir($resource)) {
$resource = dirname($resource);
}

$container->addResource(new FileResource($resource));

$extension = $this->getMappingResourceExtension();
if (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.xml')) && count($files)) {
return 'xml';
} elseif (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.yml')) && count($files)) {
return 'yml';
} elseif (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.php')) && count($files)) {
return 'php';
}

// add the directory itself as a resource
$container->addResource(new FileResource($dir));
if (glob($dir.'/'.$configPath.'/*.'.$extension.'.xml')) {
$driver = 'xml';
} elseif (glob($dir.'/'.$configPath.'/*.'.$extension.'.yml')) {
$driver = 'yml';
} elseif (glob($dir.'/'.$configPath.'/*.'.$extension.'.php')) {
$driver = 'php';
} else {
// add the closest existing directory as a resource
$resource = $dir.'/'.$configPath;
while (!is_dir($resource)) {
$resource = dirname($resource);
}
$container->fileExists($resource, false);

if (is_dir($dir.'/'.$this->getMappingObjectDefaultName())) {
return 'annotation';
return $container->fileExists($dir.'/'.$this->getMappingObjectDefaultName(), false) ? 'annotation' : null;
}
$container->fileExists($dir.'/'.$configPath, false);

return $driver;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Config\FileLocator;
Expand Down Expand Up @@ -994,8 +993,7 @@ private function getValidatorMappingFiles(ContainerBuilder $container, array &$f
{
if (interface_exists('Symfony\Component\Form\FormInterface')) {
$reflClass = new \ReflectionClass('Symfony\Component\Form\FormInterface');
$files['xml'][] = $file = dirname($reflClass->getFileName()).'/Resources/config/validation.xml';
$container->addResource(new FileResource($file));
$files['xml'][] = dirname($reflClass->getFileName()).'/Resources/config/validation.xml';
}

foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
Expand Down
94 changes: 94 additions & 0 deletions src/Symfony/Component/Config/Resource/ComposerResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?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\Config\Resource;

/**
* ComposerResource tracks the PHP version and Composer dependencies.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class ComposerResource implements SelfCheckingResourceInterface, \Serializable
{
private $versions;
private $vendors;

private static $runtimeVersion;
private static $runtimeVendors;

public function __construct()
{
self::refresh();
$this->versions = self::$runtimeVersion;
$this->vendors = self::$runtimeVendors;
}

public function getVendors()
{
return array_keys($this->vendors);
}

/**
* {@inheritdoc}
*/
public function __toString()
{
return __CLASS__;
}

/**
* {@inheritdoc}
*/
public function isFresh($timestamp)
{
self::refresh();

if (self::$runtimeVersion !== $this->versions) {
return false;
}

return self::$runtimeVendors === $this->vendors;
}

public function serialize()
{
return serialize(array($this->versions, $this->vendors));
}

public function unserialize($serialized)
{
list($this->versions, $this->vendors) = unserialize($serialized);
}

private static function refresh()
{
if (null !== self::$runtimeVersion) {
return;
}

self::$runtimeVersion = array();
self::$runtimeVendors = array();

foreach (get_loaded_extensions() as $ext) {
self::$runtimeVersion[$ext] = phpversion($ext);
}

foreach (get_declared_classes() as $class) {
if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
$r = new \ReflectionClass($class);
$v = dirname(dirname($r->getFileName()));
if (file_exists($v.'/composer/installed.json')) {
self::$runtimeVendors[$v] = @filemtime($v.'/composer/installed.json');
}
}
}
}
}
14 changes: 12 additions & 2 deletions src/Symfony/Component/Config/Resource/ReflectionClassResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ class ReflectionClassResource implements SelfCheckingResourceInterface, \Seriali
private $files = array();
private $className;
private $classReflector;
private $excludedVendors = array();
private $hash;

public function __construct(\ReflectionClass $classReflector)
public function __construct(\ReflectionClass $classReflector, $excludedVendors = array())
{
$this->className = $classReflector->name;
$this->classReflector = $classReflector;
$this->excludedVendors = $excludedVendors;
}

public function isFresh($timestamp)
Expand Down Expand Up @@ -75,7 +77,15 @@ private function loadFiles(\ReflectionClass $class)
do {
$file = $class->getFileName();
if (false !== $file && file_exists($file)) {
$this->files[$file] = null;
foreach ($this->excludedVendors as $vendor) {
if (0 === strpos($file, $vendor) && false !== strpbrk(substr($file, strlen($vendor), 1), '/'.DIRECTORY_SEPARATOR)) {
$file = false;
break;
}
}
if ($file) {
$this->files[$file] = null;
}
}
foreach ($class->getTraits() as $v) {
$this->loadFiles($v);
Expand Down
Original file line number Diff line number Diff line change
@@ -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\Config\Tests\Resource;

use Composer\Autoload\ClassLoader;
use Symfony\Component\Config\Resource\ComposerResource;

class ComposerResourceTest extends \PHPUnit_Framework_TestCase
{
public function testGetVendor()
{
$res = new ComposerResource();

$r = new \ReflectionClass(ClassLoader::class);
$found = false;

foreach ($res->getVendors() as $vendor) {
if ($vendor && 0 === strpos($r->getFileName(), $vendor)) {
$found = true;
break;
}
}

$this->assertTrue($found);
}

public function testSerializeUnserialize()
{
$res = new ComposerResource();
$ser = unserialize(serialize($res));

$this->assertTrue($res->isFresh(0));
$this->assertTrue($ser->isFresh(0));

$this->assertEquals($res, $ser);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

namespace Symfony\Component\DependencyInjection\Compiler;

use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
Expand Down Expand Up @@ -67,7 +66,7 @@ private function updateDefinition(ContainerBuilder $container, $id, Definition $
try {
$m = new \ReflectionFunction($factory);
if (false !== $m->getFileName() && file_exists($m->getFileName())) {
$container->addResource(new FileResource($m->getFileName()));
$container->fileExists($m->getFileName());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't you replace the file_exists check above instead ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need: if the file does not exist, we should not track its non-existence

}
} catch (\ReflectionException $e) {
return;
Expand Down
46 changes: 36 additions & 10 deletions src/Symfony/Component/DependencyInjection/ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
use Symfony\Component\Config\Resource\ClassExistenceResource;
use Symfony\Component\Config\Resource\ComposerResource;
use Symfony\Component\Config\Resource\DirectoryResource;
use Symfony\Component\Config\Resource\FileExistenceResource;
use Symfony\Component\Config\Resource\FileResource;
Expand Down Expand Up @@ -108,6 +109,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
private $envCounters = array();

/**
* @var string[] the list of vendor directories
*/
private $vendors;

/**
* Sets the track resources flag.
*
Expand Down Expand Up @@ -269,13 +275,13 @@ public function addObjectResource($object)
}
$file = $interface->getFileName();
if (false !== $file && file_exists($file)) {
$this->addResource(new FileResource($file));
$this->fileExists($file);
}
}
do {
$file = $class->getFileName();
if (false !== $file && file_exists($file)) {
$this->addResource(new FileResource($file));
$this->fileExists($file);
}
foreach ($class->getTraitNames() as $name) {
$this->addObjectResource($name);
Expand Down Expand Up @@ -337,7 +343,11 @@ public function getReflectionClass($class, $koWithThrowingAutoloader = false)
if (!$classReflector) {
$this->addResource($resource ?: new ClassExistenceResource($class, ClassExistenceResource::EXISTS_KO));
} elseif (!$classReflector->isInternal()) {
$this->addResource(new ReflectionClassResource($classReflector));
$path = $classReflector->getFileName();

if (!$this->inVendors($path)) {
$this->addResource(new ReflectionClassResource($classReflector, $this->vendors));
}
}
$this->classReflectors[$class] = $classReflector;
}
Expand All @@ -360,7 +370,7 @@ public function fileExists($path, $trackContents = true)
{
$exists = file_exists($path);

if (!$this->trackResources) {
if (!$this->trackResources || $this->inVendors($path)) {
return $exists;
}

Expand All @@ -370,12 +380,10 @@ public function fileExists($path, $trackContents = true)
return $exists;
}

if ($trackContents) {
if (is_file($path)) {
$this->addResource(new FileResource($path));
} else {
$this->addResource(new DirectoryResource($path, is_string($trackContents) ? $trackContents : null));
}
if ($trackContents && is_dir($path)) {
$this->addResource(new DirectoryResource($path, is_string($trackContents) ? $trackContents : null));
} elseif ($trackContents || is_dir($path)) {
$this->addResource(new FileResource($path));
}

return $exists;
Expand Down Expand Up @@ -1488,4 +1496,22 @@ private function getExpressionLanguage()

return $this->expressionLanguage;
}

private function inVendors($path)
{
if (null === $this->vendors) {
$resource = new ComposerResource();
$this->vendors = $resource->getVendors();
$this->addResource($resource);
}
$path = realpath($path) ?: $path;

foreach ($this->vendors as $vendor) {
if (0 === strpos($path, $vendor) && false !== strpbrk(substr($path, strlen($vendor), 1), '/'.DIRECTORY_SEPARATOR)) {
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function load($file, $type = null)
{
$file = rtrim($file, '/');
$path = $this->locator->locate($file);
$this->container->addResource(new DirectoryResource($path));
$this->container->fileExists($path, false);

foreach (scandir($path) as $dir) {
if ('.' !== $dir[0]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function load($resource, $type = null)
{
$path = $this->locator->locate($resource);

$this->container->addResource(new FileResource($path));
$this->container->fileExists($path);

// first pass to catch parsing errors
$result = parse_ini_file($path, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function load($resource, $type = null)

$path = $this->locator->locate($resource);
$this->setCurrentDir(dirname($path));
$this->container->addResource(new FileResource($path));
$this->container->fileExists($path);

include $path;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function load($resource, $type = null)

$xml = $this->parseFileToDOM($path);

$this->container->addResource(new FileResource($path));
$this->container->fileExists($path);

// anonymous services
$this->processAnonymousServices($xml, $path);
Expand Down
Loading