feature #22459 [HttpKernel] Fix deprecation of Extension::addClassesToCompile() / AddClassesToCachePass (nicolas-grekas)

This PR was merged into the 3.3-dev branch.

Discussion
----------

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

| Q             | A
| ------------- | ---
| Branch?       | 3.3
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

As done in https://github.com/symfony/symfony/pull/20735

Commits
-------

f4b5784dd9 [HttpKernel] Fix deprecation of Extension::addClassesToCompile() / AddClassesToCachePass
This commit is contained in:
Fabien Potencier 2017-04-19 10:18:46 -06:00
commit 610a2385f6
7 changed files with 170 additions and 147 deletions

View File

@ -255,6 +255,8 @@ HttpFoundation
HttpKernel 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 * The `Psr6CacheClearer::addPool()` method has been deprecated. Pass an array
of pools indexed by name to the constructor instead. of pools indexed by name to the constructor instead.

View File

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

View File

@ -4,12 +4,13 @@ CHANGELOG
3.3.0 3.3.0
----- -----
* Added `kernel.project_dir` and `Kernel::getProjectDir()` * added `kernel.project_dir` and `Kernel::getProjectDir()`
* Deprecated `kernel.root_dir` and `Kernel::getRootDir()` * deprecated `kernel.root_dir` and `Kernel::getRootDir()`
* Deprecated `Kernel::getEnvParameters()` * deprecated `Kernel::getEnvParameters()`
* Deprecated the special `SYMFONY__` environment variables * deprecated the special `SYMFONY__` environment variables
* added the possibility to change the query string parameter used by `UriSigner` * added the possibility to change the query string parameter used by `UriSigner`
* deprecated `LazyLoadingFragmentHandler::addRendererService()` * deprecated `LazyLoadingFragmentHandler::addRendererService()`
* deprecated `Extension::addClassesToCompile()`
3.2.0 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; 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);
@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;
/** /**
* Sets the classes to compile in the cache for the container. * 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. * @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\EnvParametersResource;
use Symfony\Component\HttpKernel\Config\FileLocator; use Symfony\Component\HttpKernel\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; 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\GlobFileLoader;
use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Config\Loader\DelegatingLoader; use Symfony\Component\Config\Loader\DelegatingLoader;
@ -652,9 +652,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$container->merge($cont); $container->merge($cont);
} }
if (PHP_VERSION_ID < 70000) { $container->addCompilerPass(new AddAnnotatedClassesToCachePass($this));
$container->addCompilerPass(new AddClassesToCachePass($this));
}
$container->addResource(new EnvParametersResource('SYMFONY__')); $container->addResource(new EnvParametersResource('SYMFONY__'));
return $container; return $container;

View File

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