Security update for Core, with self-updated composer
[yaffs-website] / vendor / symfony / http-kernel / DependencyInjection / AddClassesToCachePass.php
index 09af6bd25d93810147b2fda8dc93b2cd741fd660..e9d012033d6d4f7d5730a6286e458fe372b7b077 100644 (file)
@@ -11,6 +11,8 @@
 
 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;
@@ -35,12 +37,113 @@ class AddClassesToCachePass implements CompilerPassInterface
     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());
             }
         }
 
-        $this->kernel->setClassCache(array_unique($container->getParameterBag()->resolveValue($classes)));
+        $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;
     }
 }