[ClassLoader] made ClassCollectionLoader::load() automatically include class dependencies
This commit is contained in:
parent
6f4d28181a
commit
3f9e8ffe5c
@ -116,17 +116,12 @@ class FrameworkExtension extends Extension
|
|||||||
|
|
||||||
'Symfony\\Component\\Config\\FileLocator',
|
'Symfony\\Component\\Config\\FileLocator',
|
||||||
|
|
||||||
'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface',
|
|
||||||
'Symfony\\Component\\EventDispatcher\\EventDispatcher',
|
|
||||||
'Symfony\\Component\\EventDispatcher\\Event',
|
'Symfony\\Component\\EventDispatcher\\Event',
|
||||||
'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface',
|
|
||||||
'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher',
|
'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher',
|
||||||
|
|
||||||
'Symfony\\Component\\HttpKernel\\HttpKernel',
|
|
||||||
'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener',
|
'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener',
|
||||||
'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener',
|
'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener',
|
||||||
'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver',
|
'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver',
|
||||||
'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface',
|
|
||||||
'Symfony\\Component\\HttpKernel\\Event\\KernelEvent',
|
'Symfony\\Component\\HttpKernel\\Event\\KernelEvent',
|
||||||
'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent',
|
'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent',
|
||||||
'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent',
|
'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent',
|
||||||
@ -140,7 +135,6 @@ class FrameworkExtension extends Extension
|
|||||||
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver',
|
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver',
|
||||||
// Cannot be included because annotations will parse the big compiled class file
|
// Cannot be included because annotations will parse the big compiled class file
|
||||||
// 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller',
|
// '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']);
|
$container->setParameter('request_listener.https_port', $config['https_port']);
|
||||||
|
|
||||||
$this->addClassesToCompile(array(
|
$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\\Matcher\\UrlMatcher',
|
||||||
'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
|
'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
|
||||||
'Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface',
|
|
||||||
'Symfony\\Component\\Routing\\RequestContextAwareInterface',
|
|
||||||
'Symfony\\Component\\Routing\\RequestContext',
|
'Symfony\\Component\\Routing\\RequestContext',
|
||||||
'Symfony\\Component\\Routing\\Router',
|
'Symfony\\Component\\Routing\\Router',
|
||||||
'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher',
|
'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher',
|
||||||
@ -313,7 +302,6 @@ class FrameworkExtension extends Extension
|
|||||||
|
|
||||||
$this->addClassesToCompile(array(
|
$this->addClassesToCompile(array(
|
||||||
'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener',
|
'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener',
|
||||||
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface',
|
|
||||||
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage',
|
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage',
|
||||||
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\FileSessionHandler',
|
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\FileSessionHandler',
|
||||||
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy',
|
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy',
|
||||||
@ -405,14 +393,6 @@ class FrameworkExtension extends Extension
|
|||||||
|
|
||||||
$this->addClassesToCompile(array(
|
$this->addClassesToCompile(array(
|
||||||
'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables',
|
'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\\TemplateReference',
|
||||||
'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser',
|
'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser',
|
||||||
$container->findDefinition('templating.locator')->getClass(),
|
$container->findDefinition('templating.locator')->getClass(),
|
||||||
@ -420,9 +400,6 @@ class FrameworkExtension extends Extension
|
|||||||
|
|
||||||
if (in_array('php', $config['engines'], true)) {
|
if (in_array('php', $config['engines'], true)) {
|
||||||
$this->addClassesToCompile(array(
|
$this->addClassesToCompile(array(
|
||||||
'Symfony\\Component\\Templating\\PhpEngine',
|
|
||||||
'Symfony\\Component\\Templating\\Loader\\LoaderInterface',
|
|
||||||
'Symfony\\Component\\Templating\\Storage\\Storage',
|
|
||||||
'Symfony\\Component\\Templating\\Storage\\FileStorage',
|
'Symfony\\Component\\Templating\\Storage\\FileStorage',
|
||||||
'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine',
|
'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine',
|
||||||
'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader',
|
'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader',
|
||||||
|
@ -91,21 +91,14 @@ class SecurityExtension extends Extension
|
|||||||
// add some required classes for compilation
|
// add some required classes for compilation
|
||||||
$this->addClassesToCompile(array(
|
$this->addClassesToCompile(array(
|
||||||
'Symfony\\Component\\Security\\Http\\Firewall',
|
'Symfony\\Component\\Security\\Http\\Firewall',
|
||||||
'Symfony\\Component\\Security\\Http\\FirewallMapInterface',
|
|
||||||
'Symfony\\Component\\Security\\Core\\SecurityContext',
|
'Symfony\\Component\\Security\\Core\\SecurityContext',
|
||||||
'Symfony\\Component\\Security\\Core\\SecurityContextInterface',
|
|
||||||
'Symfony\\Component\\Security\\Core\\User\\UserProviderInterface',
|
'Symfony\\Component\\Security\\Core\\User\\UserProviderInterface',
|
||||||
'Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager',
|
'Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager',
|
||||||
'Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationManagerInterface',
|
|
||||||
'Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager',
|
'Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager',
|
||||||
'Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManagerInterface',
|
|
||||||
'Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface',
|
'Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface',
|
||||||
|
|
||||||
'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallMap',
|
'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallMap',
|
||||||
'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallContext',
|
'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallContext',
|
||||||
|
|
||||||
'Symfony\\Component\\HttpFoundation\\RequestMatcher',
|
'Symfony\\Component\\HttpFoundation\\RequestMatcher',
|
||||||
'Symfony\\Component\\HttpFoundation\\RequestMatcherInterface',
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +174,6 @@ class SecurityExtension extends Extension
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->addClassesToCompile(array(
|
$this->addClassesToCompile(array(
|
||||||
'Symfony\\Component\\Security\\Http\\AccessMapInterface',
|
|
||||||
'Symfony\\Component\\Security\\Http\\AccessMap',
|
'Symfony\\Component\\Security\\Http\\AccessMap',
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -92,14 +92,12 @@ class TwigExtension extends Extension
|
|||||||
|
|
||||||
$this->addClassesToCompile(array(
|
$this->addClassesToCompile(array(
|
||||||
'Twig_Environment',
|
'Twig_Environment',
|
||||||
'Twig_ExtensionInterface',
|
|
||||||
'Twig_Extension',
|
'Twig_Extension',
|
||||||
'Twig_Extension_Core',
|
'Twig_Extension_Core',
|
||||||
'Twig_Extension_Escaper',
|
'Twig_Extension_Escaper',
|
||||||
'Twig_Extension_Optimizer',
|
'Twig_Extension_Optimizer',
|
||||||
'Twig_LoaderInterface',
|
'Twig_LoaderInterface',
|
||||||
'Twig_Markup',
|
'Twig_Markup',
|
||||||
'Twig_TemplateInterface',
|
|
||||||
'Twig_Template',
|
'Twig_Template',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace Symfony\Component\ClassLoader;
|
|||||||
class ClassCollectionLoader
|
class ClassCollectionLoader
|
||||||
{
|
{
|
||||||
static private $loaded;
|
static private $loaded;
|
||||||
static private $baseClassesCountMap;
|
static private $seen;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a list of classes and caches them in one big file.
|
* Loads a list of classes and caches them in one big file.
|
||||||
@ -55,6 +55,8 @@ class ClassCollectionLoader
|
|||||||
$name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5);
|
$name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$classes = array_unique($classes);
|
||||||
|
|
||||||
$cache = $cacheDir.'/'.$name.$extension;
|
$cache = $cacheDir.'/'.$name.$extension;
|
||||||
|
|
||||||
// auto-reload
|
// auto-reload
|
||||||
@ -90,23 +92,19 @@ class ClassCollectionLoader
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// order classes to avoid redeclaration at runtime (class declared before its parent)
|
|
||||||
self::orderClasses($classes);
|
|
||||||
|
|
||||||
$files = array();
|
$files = array();
|
||||||
$content = '';
|
$content = '';
|
||||||
foreach ($classes as $class) {
|
foreach (self::getOrderedClasses($classes) as $class) {
|
||||||
if (!class_exists($class) && !interface_exists($class) && (!function_exists('trait_exists') || !trait_exists($class))) {
|
if (in_array($class->getName(), $declared)) {
|
||||||
throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$r = new \ReflectionClass($class);
|
$files[] = $class->getFileName();
|
||||||
$files[] = $r->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
|
// add namespace declaration for global code
|
||||||
if (!$r->inNamespace()) {
|
if (!$class->inNamespace()) {
|
||||||
$c = "\nnamespace\n{\n".self::stripComments($c)."\n}\n";
|
$c = "\nnamespace\n{\n".self::stripComments($c)."\n}\n";
|
||||||
} else {
|
} else {
|
||||||
$c = self::fixNamespaceDeclarations('<?php '.$c);
|
$c = self::fixNamespaceDeclarations('<?php '.$c);
|
||||||
@ -234,47 +232,80 @@ class ClassCollectionLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Orders a set of classes according to their number of parents.
|
* Gets an ordered array of passed classes including all their dependencies.
|
||||||
*
|
*
|
||||||
* @param array $classes
|
* @param array $classes
|
||||||
*
|
*
|
||||||
|
* @return array An array of sorted \ReflectionClass instances (dependencies added if needed)
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException When a class can't be loaded
|
* @throws \InvalidArgumentException When a class can't be loaded
|
||||||
*/
|
*/
|
||||||
static private function orderClasses(array &$classes)
|
static private function getOrderedClasses(array $classes)
|
||||||
{
|
{
|
||||||
|
$map = array();
|
||||||
|
self::$seen = array();
|
||||||
foreach ($classes as $class) {
|
foreach ($classes as $class) {
|
||||||
if (isset(self::$baseClassesCountMap[$class])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$reflectionClass = new \ReflectionClass($class);
|
$reflectionClass = new \ReflectionClass($class);
|
||||||
} catch (\ReflectionException $e) {
|
} catch (\ReflectionException $e) {
|
||||||
throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
|
throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The counter is cached to avoid reflection if the same class is asked again later
|
$map = array_merge($map, self::getClassHierarchy($reflectionClass));
|
||||||
self::$baseClassesCountMap[$class] = self::countParentClasses($reflectionClass) + count($reflectionClass->getInterfaces());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asort(self::$baseClassesCountMap);
|
return $map;
|
||||||
|
|
||||||
$classes = array_intersect(array_keys(self::$baseClassesCountMap), $classes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static private function getClassHierarchy(\ReflectionClass $class)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
if (($parent = $class->getParentClass()) && $parent->isUserDefined()) {
|
if (isset(self::$seen[$class->getName()])) {
|
||||||
$count = self::countParentClasses($parent, ++$count);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
namespace Symfony\Component\ClassLoader\Tests;
|
namespace Symfony\Component\ClassLoader\Tests;
|
||||||
|
|
||||||
use Symfony\Component\ClassLoader\ClassCollectionLoader;
|
use Symfony\Component\ClassLoader\ClassCollectionLoader;
|
||||||
use Symfony\Component\ClassLoader\UniversalClassLoader;
|
|
||||||
|
|
||||||
require_once __DIR__.'/Fixtures/ClassesWithParents/CInterface.php';
|
require_once __DIR__.'/Fixtures/ClassesWithParents/CInterface.php';
|
||||||
require_once __DIR__.'/Fixtures/ClassesWithParents/B.php';
|
require_once __DIR__.'/Fixtures/ClassesWithParents/B.php';
|
||||||
@ -25,38 +24,19 @@ class ClassCollectionLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
*/
|
*/
|
||||||
public function testClassReordering(array $classes)
|
public function testClassReordering(array $classes)
|
||||||
{
|
{
|
||||||
$expected = <<<EOF
|
$expected = array(
|
||||||
<?php
|
'ClassesWithParents\\CInterface',
|
||||||
|
'ClassesWithParents\\B',
|
||||||
|
'ClassesWithParents\\A',
|
||||||
|
);
|
||||||
|
|
||||||
namespace ClassesWithParents
|
$r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader');
|
||||||
{
|
$m = $r->getMethod('getOrderedClasses');
|
||||||
|
$m->setAccessible(true);
|
||||||
|
|
||||||
interface CInterface {}
|
$ordered = $m->invoke('Symfony\Component\ClassLoader\ClassCollectionLoader', $classes);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace ClassesWithParents
|
$this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered));
|
||||||
{
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDifferentOrders()
|
public function getDifferentOrders()
|
||||||
@ -77,6 +57,59 @@ EOF;
|
|||||||
'ClassesWithParents\\B',
|
'ClassesWithParents\\B',
|
||||||
'ClassesWithParents\\A',
|
'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',
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ClassesWithParents;
|
||||||
|
|
||||||
|
trait ATrait
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ClassesWithParents;
|
||||||
|
|
||||||
|
trait BTrait
|
||||||
|
{
|
||||||
|
use ATrait;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ClassesWithParents;
|
||||||
|
|
||||||
|
trait CTrait
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ClassesWithParents;
|
||||||
|
|
||||||
|
class D extends A
|
||||||
|
{
|
||||||
|
use BTrait;
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ClassesWithParents;
|
||||||
|
|
||||||
|
class E extends D
|
||||||
|
{
|
||||||
|
use CTrait;
|
||||||
|
}
|
Reference in New Issue
Block a user