Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit cacd27a

Browse filesBrowse files
committed
[HttpKernel] Create ClassMatcher to use patterns in classes and annotations to cache
1 parent f67912f commit cacd27a
Copy full SHA for cacd27a

File tree

11 files changed

+353
-6
lines changed
Filter options

11 files changed

+353
-6
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php
+16-1Lines changed: 16 additions & 1 deletion
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,6 +22,13 @@
2122
*/
2223
class ClassCacheCacheWarmer implements CacheWarmerInterface
2324
{
25+
private $classMatcher;
26+
27+
public function __construct(ClassMatcherInterface $classMatcher = null)
28+
{
29+
$this->classMatcher = $classMatcher;
30+
}
31+
2432
/**
2533
* Warms up the cache.
2634
*
@@ -38,7 +46,14 @@ public function warmUp($cacheDir)
3846
return;
3947
}
4048

41-
ClassCollectionLoader::load(include($classmap), $cacheDir, 'classes', false);
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);
4257
}
4358

4459
/**

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+9-2Lines changed: 9 additions & 2 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\\FileLocator',
174182

@@ -194,8 +202,7 @@ public function load(array $configs, ContainerBuilder $container)
194202

195203
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser',
196204
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver',
197-
// Cannot be included because annotations will parse the big compiled class file
198-
// 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller',
205+
'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller',
199206
));
200207
}
201208

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml
+3Lines 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
</service>
2831

‎src/Symfony/Bundle/FrameworkBundle/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/composer.json
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
"php": ">=5.5.9",
2020
"symfony/asset": "~2.8|~3.0",
2121
"symfony/cache": "~3.1",
22-
"symfony/class-loader": "~2.8|~3.0",
22+
"symfony/class-loader": "~3.2",
2323
"symfony/dependency-injection": "~3.2",
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",
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

Copy file name to clipboardExpand all lines: src/Symfony/Component/ClassLoader/ClassCollectionLoader.php
+29Lines 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
*
+91Lines 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+
}
+30Lines 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+
}
+46Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php
+21Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Kernel.php
+11-1Lines 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;
@@ -337,7 +338,15 @@ public function loadClassCache($name = 'classes', $extension = '.php')
337338
}
338339

339340
/**
340-
* Used internally.
341+
* @internal
342+
*/
343+
public function setAnnotatedClassCache(array $annotatedClasses)
344+
{
345+
file_put_contents($this->getCacheDir().'/annotations.map', sprintf('<?php return %s;', var_export($annotatedClasses, true)));
346+
}
347+
348+
/**
349+
* @internal
341350
*/
342351
public function setClassCache(array $classes)
343352
{
@@ -573,6 +582,7 @@ protected function buildContainer()
573582
}
574583

575584
$container->addCompilerPass(new AddClassesToCachePass($this));
585+
$container->addCompilerPass(new AddAnnotatedClassesToCachePass($this));
576586
$container->addResource(new EnvParametersResource('SYMFONY__'));
577587

578588
return $container;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.