8000 [HttpKernel] Create ClassMatcher to use patterns in classes and annot… · symfony/symfony@8ea4ed6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8ea4ed6

Browse files
committed
[HttpKernel] Create ClassMatcher to use patterns in classes and annotations to cache
1 parent 194dcf3 commit 8ea4ed6

File tree

11 files changed

+347
-7
lines changed

11 files changed

+347
-7
lines changed

src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\ClassLoader\ClassCollectionLoader;
1515
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
16+
use Symfony\Component\HttpKernel\CacheWarmer\ClassMatcherInterface;
1617

1718
/**
1819
* Generates the Class Cache (classes.php) file.
@@ -21,11 +22,11 @@
2122
*/
2223
class ClassCacheCacheWarmer implements CacheWarmerInterface
2324
{
24-
private $declaredClasses;
25+
private $classMatcher;
2526

26-
public function __construct(array $declaredClasses = null)
27+
public function __construct(ClassMatcherInterface $classMatcher = null)
2728
{
28-
$this->declaredClasses = $declaredClasses;
29+
$this->classMatcher = $classMatcher;
2930
}
3031

3132
/**
@@ -44,9 +45,15 @@ public function warmUp($cacheDir)
4445
if (file_exists($cacheDir.'/classes.php')) {
4546
return;
4647
}
47-
$declared = null !== $this->declaredClasses ? $this->declaredClasses : array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
4848

49-
ClassCollectionLoader::inline(include($classmap), $cacheDir.'/classes.php', $declared);
49+
$classesToCompile = include $classmap;
50+
51+
if ($this->classMatcher) {
52+
$declaredClasses = array_keys(ClassCollectionLoader::getComposerClassMap());
53+
$classesToCompile = $this->classMatcher->match($declaredClasses, $classesToCompile);
54+
}
55+
56+
ClassCollectionLoader::load($classesToCompile, $cacheDir, 'classes', false);
5057
}
5158

5259
/**

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ public function load(array $configs, ContainerBuilder $container)
169169
$definition->replaceArgument(1, null);
170170
}
171171

172+
$this->addAnnotatedClassesToCompile(array(
173+
'**Bundle\\Controller\\',
174+
'**Bundle\\Entity\\',
175+
176+
// Added explicitly so that we dont rely on the class map being dumped to make it work
177+
'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller',
178+
));
179+
172180
$this->addClassesToCompile(array(
173181
'Symfony\\Component\\Config\\ConfigCache',
174182
'Symfony\\Component\\Config\\FileLocator',

src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818

1919
<service id="request_stack" class="Symfony\Component\HttpFoundation\RequestStack" />
2020

21+
<service id="kernel.class_cache.matcher" class="Symfony\Component\HttpKernel\CacheWarmer\ClassMatcher" public="false" />
22+
2123
<service id="cache_warmer" class="Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate">
2224
<argument type="collection" />
2325
</service>
2426

2527
<service id="kernel.class_cache.cache_warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\ClassCacheCacheWarmer">
28+
<argument type="service" id="kernel.class_cache.matcher" />
2629
<tag name="kernel.cache_warmer" />
2730
<argument type="collection">
2831
<argument>Doctrine\Common\Annotations\AnnotationRegistry</argument>

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"symfony/config": "~2.8|~3.0",
2525
"symfony/event-dispatcher": "~2.8|~3.0",
2626
"symfony/http-foundation": "~3.1",
27-
"symfony/http-kernel": "~3.1.2|~3.2",
27+
"symfony/http-kernel": "~3.2",
2828
"symfony/polyfill-mbstring": "~1.0",
2929
"symfony/filesystem": "~2.8|~3.0",
3030
"symfony/finder": "~2.8|~3.0",

src/Symfony/Component/ClassLoader/ClassCollectionLoader.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
namespace Symfony\Component\ClassLoader;
1313

14+
use Composer\Autoload\ClassLoader;
15+
use Symfony\Component\Debug\DebugClassLoader;
16+
1417
/**
1518
* ClassCollectionLoader.
1619
*
@@ -22,6 +25,32 @@ class ClassCollectionLoader
2225
private static $seen;
2326
private static $useTokenizer = true;
2427

28+
/**
29+
* Return the Composer class map.
30+
*
31+
* @return array
32+
*/
33+
public static function getComposerClassMap()
34+
{
35+
$classes = array();
36+
37+
foreach (spl_autoload_functions() as $function) {
38+
if (!is_array($function)) {
39+
continue;
40+
}
41+
42+
if ($function[0] instanceof DebugClassLoader) {
43+
$function = $function[0]->getClassLoader();
44+
}
45+
46+
if (is_array($function) && $function[0] instanceof ClassLoader) {
47+
$classes += $function[0]->getClassMap();
48+
}
49+
}
50+
51+
return $classes;
52+
}
53+
2554
/**
2655
* Loads a list of classes and caches them in one big file.
2756
*
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\CacheWarmer;
13+
14+
/**
15+
* Default implementation of the ClassMatcherInterface.
16+
*
17+
* This implementation uses single wildcards for any character other than backslashes
18+
* and double wildcards for any character.
19+
*
20+
* @author Titouan Galopin <galopintitouan@gmail.com>
21+
*/
22+
class ClassMatcher implements ClassMatcherInterface
23+
{
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
public function match(array $classes, array $patterns)
28+
{
29+
$matched = array();
30+
31+
// Explicit classes declared in the patterns are returned directly
32+
foreach ($patterns as $key => $pattern) {
33+
if (substr($pattern, -1) !== '\\' && false === strpos($pattern, '*')) {
34+
unset($patterns[$key]);
35+
$matched[] = ltrim($pattern, '\\');
36+
}
37+
}
38+
39+
// Match patterns with the classes list
40+
$regexps = $this->patternsToRegexps($patterns);
41+
42+
foreach ($classes as $class) {
43+
$class = ltrim($class, '\\');
44+
45+
if ($this->matchAnyRegexp($class, $regexps)) {
46+
$matched[] = $class;
47+
}
48+
}
49+
50+
return $matched;
51+
}
52+
53+
private function patternsToRegexps($patterns)
54+
{
55+
$regexps = array();
56+
57+
foreach ($patterns as $pattern) {
58+
// Escape user input
59+
$regex = preg_quote(ltrim($pattern, '\\'));
60+
61+
// Wildcards * and **
62+
$regex = strtr($regex, array('\\*\\*' => '.*?', '\\*' => '[^\\\\]*?'));
63+
64+
// If this class does not end by a slash, anchor the end
65+
if (substr($regex, -1) !== '\\') {
66+
$regex .= '$';
67+
}
68+
69+
$regexps[] = '{^\\\\'.$regex.'}';
70+
}
71+
72+
return $regexps;
73+
}
74+
75+
private function matchAnyRegexp($class, $regexps)
76+
{
77+
$blacklisted = false !== strpos($class, 'Test');
78+
79+
foreach ($regexps as $regex) {
80+
if ($blacklisted && false === strpos($regex, 'Test')) {
81+
continue;
82+
}
83+
84+
if (preg_match($regex, '\\'.$class)) {
85+
return true;
86+
}
87+
}
88+
89+
return false;
90+
}
91+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\CacheWarmer;
13+
14+
/**
15+
* A class matcher find classes matching given patterns.
16+
*
17+
* @author Titouan Galopin <galopintitouan@gmail.com>
18+
*/
19+
interface ClassMatcherInterface
20+
{
21+
/**
22+
* Return classes matching at least one of the given patterns.
23+
*
24+
* @param array $classes All the possibles classes
25+
* @param array $patterns The patterns to filter these classes
26+
*
27+
* @return array The classes matching the patterns
28+
*/
29+
public function match(array $classes, array $patterns);
30+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\DependencyInjection;
13+
14+
use Symfony\Component\DependencyInjection\ContainerBuilder;
15+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16+
use Symfony\Component\HttpKernel\Kernel;
17+
18+
/**
19+
* Sets the annotated classes to compile in the cache for the container.
20+
*
21+
* @author Titouan Galopin <galopintitouan@gmail.com>
22+
*/
23+
class AddAnnotatedClassesToCachePass implements CompilerPassInterface
24+
{
25+
private $kernel;
26+
27+
public function __construct(Kernel $kernel)
28+
{
29+
$this->kernel = $kernel;
30+
}
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public function process(ContainerBuilder $container)
36+
{
37+
$classes = array();
38+
foreach ($container->getExtensions() as $extension) {
39+
if ($extension instanceof Extension) {
40+
$classes = array_merge($classes, $extension->getAnnotatedClassesToCompile());
41+
}
42+
}
43+
44+
$this->kernel->setAnnotatedClassCache(array_unique($container->getParameterBag()->resolveValue($classes)));
45+
}
46+
}

src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
abstract class Extension extends BaseExtension
2222
{
2323
private $classes = array();
24+
private $annotatedClasses = array();
2425

2526
/**
2627
* Gets the classes to cache.
@@ -41,4 +42,24 @@ public function addClassesToCompile(array $classes)
4142
{
4243
$this->classes = array_merge($this->classes, $classes);
4344
}
45+
46+
/**
47+
* Gets the annotated classes to cache.
48+
*
49+
* @return array An array of classes
50+
*/
51+
public function getAnnotatedClassesToCompile()
52+
{
53+
return $this->annotatedClasses;
54+
}
55+
56+
/**
57+
* Adds annotated classes to the annotation cache.
58+
*
59+
* @param array $annotatedClasses An array of classes
60+
*/
61+
public function addAnnotatedClassesToCompile(array $annotatedClasses)
62+
{
63+
$this->annotatedClasses = array_merge($this->annotatedClasses, $annotatedClasses);
64+
}
4465
}

src/Symfony/Component/HttpKernel/Kernel.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
2929
use Symfony\Component\HttpKernel\Config\EnvParametersResource;
3030
use Symfony\Component\HttpKernel\Config\FileLocator;
31+
use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass;
3132
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
3233
use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass;
3334
use Symfony\Component\Config\Loader\LoaderResolver;
@@ -329,7 +330,15 @@ public function loadClassCache($name = 'classes', $extension = '.php')
329330
}
330331

331332
/**
332-
* Used internally.
333+
* @internal
334+
*/
335+
public function setAnnotatedClassCache(array $annotatedClasses)
336+
{
337+
file_put_contents($this->getCacheDir().'/annotations.map', sprintf('<?php return %s;', var_export($annotatedClasses, true)));
338+
}
339+
340+
/**
341+
* @internal
333342
*/
334343
public function setClassCache(array $classes)
335344
{
@@ -565,6 +574,7 @@ protected function buildContainer()
565574
}
566575

567576
$container->addCompilerPass(new AddClassesToCachePass($this));
577+
$container->addCompilerPass(new AddAnnotatedClassesToCachePass($this));
568578
$container->addResource(new EnvParametersResource('SYMFONY__'));
569579

570580
return $container;

0 commit comments

Comments
 (0)
0