[HttpKernel] Fix deprecation of Extension::addClassesToCompile() / AddClassesToCachePass

This commit is contained in:
Nicolas Grekas 2017-04-17 21:24:29 +02:00
parent ac5cfee6f1
commit f4b5784dd9
7 changed files with 170 additions and 147 deletions

View File

@ -252,6 +252,8 @@ HttpFoundation
HttpKernel
-----------
* The `Extension::addClassesToCompile()` method has been deprecated and will be removed in 4.0.
* The `Psr6CacheClearer::addPool()` method has been deprecated. Pass an array
of pools indexed by name to the constructor instead.

View File

@ -364,6 +364,8 @@ HttpFoundation
HttpKernel
----------
* The `Extension::addClassesToCompile()` method has been removed.
* Possibility to pass non-scalar values as URI attributes to the ESI and SSI
renderers has been removed. The inline fragment renderer should be used with
non-scalar attributes.

View File

@ -4,12 +4,13 @@ CHANGELOG
3.3.0
-----
* Added `kernel.project_dir` and `Kernel::getProjectDir()`
* Deprecated `kernel.root_dir` and `Kernel::getRootDir()`
* Deprecated `Kernel::getEnvParameters()`
* Deprecated the special `SYMFONY__` environment variables
* added `kernel.project_dir` and `Kernel::getProjectDir()`
* deprecated `kernel.root_dir` and `Kernel::getRootDir()`
* deprecated `Kernel::getEnvParameters()`
* deprecated the special `SYMFONY__` environment variables
* added the possibility to change the query string parameter used by `UriSigner`
* deprecated `LazyLoadingFragmentHandler::addRendererService()`
* deprecated `Extension::addClassesToCompile()`
3.2.0
-----

View File

@ -0,0 +1,153 @@
<?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\HttpKernel\DependencyInjection;
use Composer\Autoload\ClassLoader;
use Symfony\Component\Debug\DebugClassLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\HttpKernel\Kernel;
/**
* Sets the classes to compile in the cache for the container.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class AddAnnotatedClassesToCachePass implements CompilerPassInterface
{
private $kernel;
public function __construct(Kernel $kernel)
{
$this->kernel = $kernel;
}
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
$classes = array();
$annotatedClasses = array();
foreach ($container->getExtensions() as $extension) {
if ($extension instanceof Extension) {
if (PHP_VERSION_ID < 70000) {
$classes = array_merge($classes, $extension->getClassesToCompile());
}
$annotatedClasses = array_merge($annotatedClasses, $extension->getAnnotatedClassesToCompile());
}
}
$existingClasses = $this->getClassesInComposerClassMaps();
if (PHP_VERSION_ID < 70000) {
$classes = $container->getParameterBag()->resolveValue($classes);
$this->kernel->setClassCache($this->expandClasses($classes, $existingClasses));
}
$annotatedClasses = $container->getParameterBag()->resolveValue($annotatedClasses);
$this->kernel->setAnnotatedClassCache($this->expandClasses($annotatedClasses, $existingClasses));
}
/**
* Expands the given class patterns using a list of existing classes.
*
* @param array $patterns The class patterns to expand
* @param array $classes The existing classes to match against the patterns
*
* @return array A list of classes derivated from the patterns
*/
private function expandClasses(array $patterns, array $classes)
{
$expanded = array();
// Explicit classes declared in the patterns are returned directly
foreach ($patterns as $key => $pattern) {
if (substr($pattern, -1) !== '\\' && false === strpos($pattern, '*')) {
unset($patterns[$key]);
$expanded[] = ltrim($pattern, '\\');
}
}
// Match patterns with the classes list
$regexps = $this->patternsToRegexps($patterns);
foreach ($classes as $class) {
$class = ltrim($class, '\\');
if ($this->matchAnyRegexps($class, $regexps)) {
$expanded[] = $class;
}
}
return array_unique($expanded);
}
private function getClassesInComposerClassMaps()
{
$classes = array();
foreach (spl_autoload_functions() as $function) {
if (!is_array($function)) {
continue;
}
if ($function[0] instanceof DebugClassLoader) {
$function = $function[0]->getClassLoader();
}
if (is_array($function) && $function[0] instanceof ClassLoader) {
$classes += array_filter($function[0]->getClassMap());
}
}
return array_keys($classes);
}
private function patternsToRegexps($patterns)
{
$regexps = array();
foreach ($patterns as $pattern) {
// Escape user input
$regex = preg_quote(ltrim($pattern, '\\'));
// Wildcards * and **
$regex = strtr($regex, array('\\*\\*' => '.*?', '\\*' => '[^\\\\]*?'));
// If this class does not end by a slash, anchor the end
if (substr($regex, -1) !== '\\') {
$regex .= '$';
}
$regexps[] = '{^\\\\'.$regex.'}';
}
return $regexps;
}
private function matchAnyRegexps($class, $regexps)
{
$blacklisted = false !== strpos($class, 'Test');
foreach ($regexps as $regex) {
if ($blacklisted && false === strpos($regex, 'Test')) {
continue;
}
if (preg_match($regex, '\\'.$class)) {
return true;
}
}
return false;
}
}

View File

@ -11,15 +11,7 @@
namespace Symfony\Component\HttpKernel\DependencyInjection;
if (PHP_VERSION_ID >= 70000) {
@trigger_error('The '.__NAMESPACE__.'\AddClassesToCachePass class is deprecated since version 3.3 and will be removed in 4.0.', E_USER_DEPRECATED);
}
use Composer\Autoload\ClassLoader;
use Symfony\Component\Debug\DebugClassLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\HttpKernel\Kernel;
@trigger_error('The '.__NAMESPACE__.'\AddClassesToCachePass class is deprecated since version 3.3 and will be removed in 4.0.', E_USER_DEPRECATED);
/**
* Sets the classes to compile in the cache for the container.
@ -28,128 +20,6 @@ use Symfony\Component\HttpKernel\Kernel;
*
* @deprecated since version 3.3, to be removed in 4.0.
*/
class AddClassesToCachePass implements CompilerPassInterface
class AddClassesToCachePass extends AddAnnotatedClassesToCachePass
{
private $kernel;
public function __construct(Kernel $kernel)
{
$this->kernel = $kernel;
}
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
$classes = array();
$annotatedClasses = array();
foreach ($container->getExtensions() as $extension) {
if ($extension instanceof Extension) {
$classes = array_merge($classes, $extension->getClassesToCompile());
$annotatedClasses = array_merge($annotatedClasses, $extension->getAnnotatedClassesToCompile());
}
}
$classes = $container->getParameterBag()->resolveValue($classes);
$annotatedClasses = $container->getParameterBag()->resolveValue($annotatedClasses);
$existingClasses = $this->getClassesInComposerClassMaps();
$this->kernel->setClassCache($this->expandClasses($classes, $existingClasses));
$this->kernel->setAnnotatedClassCache($this->expandClasses($annotatedClasses, $existingClasses));
}
/**
* Expands the given class patterns using a list of existing classes.
*
* @param array $patterns The class patterns to expand
* @param array $classes The existing classes to match against the patterns
*
* @return array A list of classes derivated from the patterns
*/
private function expandClasses(array $patterns, array $classes)
{
$expanded = array();
// Explicit classes declared in the patterns are returned directly
foreach ($patterns as $key => $pattern) {
if (substr($pattern, -1) !== '\\' && false === strpos($pattern, '*')) {
unset($patterns[$key]);
$expanded[] = ltrim($pattern, '\\');
}
}
// Match patterns with the classes list
$regexps = $this->patternsToRegexps($patterns);
foreach ($classes as $class) {
$class = ltrim($class, '\\');
if ($this->matchAnyRegexps($class, $regexps)) {
$expanded[] = $class;
}
}
return array_unique($expanded);
}
private function getClassesInComposerClassMaps()
{
$classes = array();
foreach (spl_autoload_functions() as $function) {
if (!is_array($function)) {
continue;
}
if ($function[0] instanceof DebugClassLoader) {
$function = $function[0]->getClassLoader();
}
if (is_array($function) && $function[0] instanceof ClassLoader) {
$classes += array_filter($function[0]->getClassMap());
}
}
return array_keys($classes);
}
private function patternsToRegexps($patterns)
{
$regexps = array();
foreach ($patterns as $pattern) {
// Escape user input
$regex = preg_quote(ltrim($pattern, '\\'));
// Wildcards * and **
$regex = strtr($regex, array('\\*\\*' => '.*?', '\\*' => '[^\\\\]*?'));
// If this class does not end by a slash, anchor the end
if (substr($regex, -1) !== '\\') {
$regex .= '$';
}
$regexps[] = '{^\\\\'.$regex.'}';
}
return $regexps;
}
private function matchAnyRegexps($class, $regexps)
{
$blacklisted = false !== strpos($class, 'Test');
foreach ($regexps as $regex) {
if ($blacklisted && false === strpos($regex, 'Test')) {
continue;
}
if (preg_match($regex, '\\'.$class)) {
return true;
}
}
return false;
}
}

View File

@ -28,7 +28,7 @@ use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\Config\EnvParametersResource;
use Symfony\Component\HttpKernel\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass;
use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass;
use Symfony\Component\Config\Loader\GlobFileLoader;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Config\Loader\DelegatingLoader;
@ -652,9 +652,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$container->merge($cont);
}
if (PHP_VERSION_ID < 70000) {
$container->addCompilerPass(new AddClassesToCachePass($this));
}
$container->addCompilerPass(new AddAnnotatedClassesToCachePass($this));
$container->addResource(new EnvParametersResource('SYMFONY__'));
return $container;

View File

@ -12,18 +12,15 @@
namespace Symfony\Component\HttpKernel\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass;
use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass;
/**
* @group legacy
*/
class AddClassesToCachePassTest extends TestCase
class AddAnnotatedClassesToCachePassTest extends TestCase
{
public function testExpandClasses()
{
$r = new \ReflectionClass(AddClassesToCachePass::class);
$r = new \ReflectionClass(AddAnnotatedClassesToCachePass::class);
$pass = $r->newInstanceWithoutConstructor();
$r = new \ReflectionMethod(AddClassesToCachePass::class, 'expandClasses');
$r = new \ReflectionMethod(AddAnnotatedClassesToCachePass::class, 'expandClasses');
$r->setAccessible(true);
$expand = $r->getClosure($pass);