diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 67d49eb5ff..9518506d3e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -116,17 +116,12 @@ class FrameworkExtension extends Extension 'Symfony\\Component\\Config\\FileLocator', - 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface', - 'Symfony\\Component\\EventDispatcher\\EventDispatcher', 'Symfony\\Component\\EventDispatcher\\Event', - 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface', 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher', - 'Symfony\\Component\\HttpKernel\\HttpKernel', 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener', 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener', 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver', - 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface', 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent', 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent', 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent', @@ -140,7 +135,6 @@ class FrameworkExtension extends Extension 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver', // Cannot be included because annotations will parse the big compiled class file // 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', - 'Symfony\\Bundle\\FrameworkBundle\\HttpKernel', )); } @@ -263,13 +257,8 @@ class FrameworkExtension extends Extension $container->setParameter('request_listener.https_port', $config['https_port']); $this->addClassesToCompile(array( - 'Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface', - 'Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface', - 'Symfony\\Component\\Routing\\RouterInterface', 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', - 'Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface', - 'Symfony\\Component\\Routing\\RequestContextAwareInterface', 'Symfony\\Component\\Routing\\RequestContext', 'Symfony\\Component\\Routing\\Router', 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', @@ -313,7 +302,6 @@ class FrameworkExtension extends Extension $this->addClassesToCompile(array( 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\FileSessionHandler', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy', @@ -405,14 +393,6 @@ class FrameworkExtension extends Extension $this->addClassesToCompile(array( 'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\EngineInterface', - 'Symfony\\Component\\Templating\\StreamingEngineInterface', - 'Symfony\\Component\\Templating\\TemplateNameParserInterface', - 'Symfony\\Component\\Templating\\TemplateNameParser', - 'Symfony\\Component\\Templating\\EngineInterface', - 'Symfony\\Component\\Config\\FileLocatorInterface', - 'Symfony\\Component\\Templating\\TemplateReferenceInterface', - 'Symfony\\Component\\Templating\\TemplateReference', 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference', 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser', $container->findDefinition('templating.locator')->getClass(), @@ -420,9 +400,6 @@ class FrameworkExtension extends Extension if (in_array('php', $config['engines'], true)) { $this->addClassesToCompile(array( - 'Symfony\\Component\\Templating\\PhpEngine', - 'Symfony\\Component\\Templating\\Loader\\LoaderInterface', - 'Symfony\\Component\\Templating\\Storage\\Storage', 'Symfony\\Component\\Templating\\Storage\\FileStorage', 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader', diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index f2f47696f4..b856a9191f 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -91,21 +91,14 @@ class SecurityExtension extends Extension // add some required classes for compilation $this->addClassesToCompile(array( 'Symfony\\Component\\Security\\Http\\Firewall', - 'Symfony\\Component\\Security\\Http\\FirewallMapInterface', 'Symfony\\Component\\Security\\Core\\SecurityContext', - 'Symfony\\Component\\Security\\Core\\SecurityContextInterface', 'Symfony\\Component\\Security\\Core\\User\\UserProviderInterface', 'Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager', - 'Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationManagerInterface', 'Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager', - 'Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManagerInterface', 'Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface', - 'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallMap', 'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallContext', - 'Symfony\\Component\\HttpFoundation\\RequestMatcher', - 'Symfony\\Component\\HttpFoundation\\RequestMatcherInterface', )); } @@ -181,7 +174,6 @@ class SecurityExtension extends Extension } $this->addClassesToCompile(array( - 'Symfony\\Component\\Security\\Http\\AccessMapInterface', 'Symfony\\Component\\Security\\Http\\AccessMap', )); diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index f9a7617846..1de263fce8 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -92,14 +92,12 @@ class TwigExtension extends Extension $this->addClassesToCompile(array( 'Twig_Environment', - 'Twig_ExtensionInterface', 'Twig_Extension', 'Twig_Extension_Core', 'Twig_Extension_Escaper', 'Twig_Extension_Optimizer', 'Twig_LoaderInterface', 'Twig_Markup', - 'Twig_TemplateInterface', 'Twig_Template', )); } diff --git a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php index 7435e3fdf4..b01beb7707 100644 --- a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php +++ b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php @@ -19,7 +19,7 @@ namespace Symfony\Component\ClassLoader; class ClassCollectionLoader { static private $loaded; - static private $baseClassesCountMap; + static private $seen; /** * Loads a list of classes and caches them in one big file. @@ -42,14 +42,21 @@ class ClassCollectionLoader self::$loaded[$name] = true; + $declared = array_merge(get_declared_classes(), get_declared_interfaces()); + if (function_exists('get_declared_traits')) { + $declared = array_merge($declared, get_declared_traits()); + } + if ($adaptive) { // don't include already declared classes - $classes = array_diff($classes, get_declared_classes(), get_declared_interfaces()); + $classes = array_diff($classes, $declared); // the cache is different depending on which classes are already declared $name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5); } + $classes = array_unique($classes); + $cache = $cacheDir.'/'.$name.$extension; // auto-reload @@ -85,23 +92,19 @@ class ClassCollectionLoader return; } - // order classes to avoid redeclaration at runtime (class declared before its parent) - self::orderClasses($classes); - $files = array(); $content = ''; - foreach ($classes as $class) { - if (!class_exists($class) && !interface_exists($class) && (!function_exists('trait_exists') || !trait_exists($class))) { - throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class)); + foreach (self::getOrderedClasses($classes) as $class) { + if (in_array($class->getName(), $declared)) { + continue; } - $r = new \ReflectionClass($class); - $files[] = $r->getFileName(); + $files[] = $class->getFileName(); - $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($r->getFileName())); + $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($class->getFileName())); // add namespace declaration for global code - if (!$r->inNamespace()) { + if (!$class->inNamespace()) { $c = "\nnamespace\n{\n".self::stripComments($c)."\n}\n"; } else { $c = self::fixNamespaceDeclarations('getInterfaces()); + $map = array_merge($map, self::getClassHierarchy($reflectionClass)); } - asort(self::$baseClassesCountMap); - - $classes = array_intersect(array_keys(self::$baseClassesCountMap), $classes); + return $map; } - /** - * Counts the number of parent classes in userland. - * - * @param \ReflectionClass $class - * @param integer $count If exists, the current counter - * @return integer - */ - static private function countParentClasses(\ReflectionClass $class, $count = 0) + static private function getClassHierarchy(\ReflectionClass $class) { - if (($parent = $class->getParentClass()) && $parent->isUserDefined()) { - $count = self::countParentClasses($parent, ++$count); + if (isset(self::$seen[$class->getName()])) { + return array(); } - return $count; + self::$seen[$class->getName()] = true; + + $classes = array($class); + $parent = $class; + while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) { + self::$seen[$parent->getName()] = true; + + array_unshift($classes, $parent); + } + + if (function_exists('get_declared_traits')) { + foreach ($classes as $c) { + foreach (self::getTraits($c) as $trait) { + self::$seen[$trait->getName()] = true; + + array_unshift($classes, $trait); + } + } + } + + foreach ($class->getInterfaces() as $interface) { + if ($interface->isUserDefined() && !isset(self::$seen[$interface->getName()])) { + self::$seen[$interface->getName()] = true; + + array_unshift($classes, $interface); + } + } + + return $classes; + } + + static private function getTraits(\ReflectionClass $class) + { + $traits = $class->getTraits(); + $classes = array(); + while ($trait = array_pop($traits)) { + if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) { + $classes[] = $trait; + + $traits = array_merge($traits, $trait->getTraits()); + } + } + + return $classes; } } diff --git a/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php b/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php index 2e0753ee1f..58d96d8513 100644 --- a/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php +++ b/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\ClassLoader\Tests; use Symfony\Component\ClassLoader\ClassCollectionLoader; -use Symfony\Component\ClassLoader\UniversalClassLoader; require_once __DIR__.'/Fixtures/ClassesWithParents/CInterface.php'; require_once __DIR__.'/Fixtures/ClassesWithParents/B.php'; @@ -25,38 +24,19 @@ class ClassCollectionLoaderTest extends \PHPUnit_Framework_TestCase */ public function testClassReordering(array $classes) { - $expected = <<getMethod('getOrderedClasses'); + $m->setAccessible(true); -interface CInterface {} -} - + $ordered = $m->invoke('Symfony\Component\ClassLoader\ClassCollectionLoader', $classes); -namespace ClassesWithParents -{ - -class B implements CInterface {} -} - - -namespace ClassesWithParents -{ - -class A extends B {} -} - -EOF; - - $dir = sys_get_temp_dir(); - $fileName = uniqid('symfony_'); - - ClassCollectionLoader::load($classes, $dir, $fileName, true); - $cachedContent = @file_get_contents($dir.'/'.$fileName.'.php'); - - $this->assertEquals($expected, $cachedContent); + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); } public function getDifferentOrders() @@ -77,6 +57,59 @@ EOF; 'ClassesWithParents\\B', 'ClassesWithParents\\A', )), + array(array( + 'ClassesWithParents\\A', + )), + ); + } + + /** + * @dataProvider getDifferentOrdersForTraits + */ + public function testClassWithTraitsReordering(array $classes) + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP > 5.4.0.'); + + return; + } + + require_once __DIR__.'/Fixtures/ClassesWithParents/ATrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/BTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/D.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/E.php'; + + $expected = array( + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\CTrait', + 'ClassesWithParents\\ATrait', + 'ClassesWithParents\\BTrait', + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + 'ClassesWithParents\\D', + 'ClassesWithParents\\E', + ); + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke('Symfony\Component\ClassLoader\ClassCollectionLoader', $classes); + + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); + } + + public function getDifferentOrdersForTraits() + { + return array( + array(array( + 'ClassesWithParents\\E', + 'ClassesWithParents\\ATrait', + )), + array(array( + 'ClassesWithParents\\E', + )), ); } diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/ClassesWithParents/ATrait.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/ClassesWithParents/ATrait.php new file mode 100644 index 0000000000..b02d1859bc --- /dev/null +++ b/src/Symfony/Component/ClassLoader/Tests/Fixtures/ClassesWithParents/ATrait.php @@ -0,0 +1,7 @@ +