2010-02-17 13:54:36 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
2011-01-15 13:29:43 +00:00
|
|
|
* This file is part of the Symfony package.
|
2010-02-17 13:54:36 +00:00
|
|
|
*
|
2011-03-06 11:40:06 +00:00
|
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
2010-02-17 13:54:36 +00:00
|
|
|
*
|
2011-01-15 13:29:43 +00:00
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
2010-02-17 13:54:36 +00:00
|
|
|
*/
|
|
|
|
|
2011-01-27 13:11:54 +00:00
|
|
|
namespace Symfony\Component\ClassLoader;
|
2011-01-15 13:29:43 +00:00
|
|
|
|
2010-02-17 13:54:36 +00:00
|
|
|
/**
|
2010-04-07 01:51:29 +01:00
|
|
|
* ClassCollectionLoader.
|
2010-02-17 13:54:36 +00:00
|
|
|
*
|
2011-03-06 11:40:06 +00:00
|
|
|
* @author Fabien Potencier <fabien@symfony.com>
|
2010-02-17 13:54:36 +00:00
|
|
|
*/
|
|
|
|
class ClassCollectionLoader
|
|
|
|
{
|
2012-07-09 13:50:58 +01:00
|
|
|
private static $loaded;
|
|
|
|
private static $seen;
|
2012-10-27 16:55:46 +01:00
|
|
|
private static $useTokenizer = true;
|
2010-08-11 18:35:07 +01:00
|
|
|
|
2010-05-06 12:25:53 +01:00
|
|
|
/**
|
2010-08-18 12:43:32 +01:00
|
|
|
* Loads a list of classes and caches them in one big file.
|
|
|
|
*
|
2014-11-30 13:33:44 +00:00
|
|
|
* @param array $classes An array of classes to load
|
|
|
|
* @param string $cacheDir A cache directory
|
|
|
|
* @param string $name The cache name prefix
|
|
|
|
* @param bool $autoReload Whether to flush the cache when the cache is stale or not
|
|
|
|
* @param bool $adaptive Whether to remove already declared classes or not
|
|
|
|
* @param string $extension File extension of the resulting file
|
2010-08-18 12:43:32 +01:00
|
|
|
*
|
2010-05-06 12:25:53 +01:00
|
|
|
* @throws \InvalidArgumentException When class can't be loaded
|
|
|
|
*/
|
2012-07-09 13:50:58 +01:00
|
|
|
public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php')
|
2010-02-17 13:54:36 +00:00
|
|
|
{
|
2010-08-11 18:35:07 +01:00
|
|
|
// each $name can only be loaded once per PHP process
|
|
|
|
if (isset(self::$loaded[$name])) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-08-20 12:12:06 +01:00
|
|
|
self::$loaded[$name] = true;
|
|
|
|
|
2011-01-29 16:58:40 +00:00
|
|
|
if ($adaptive) {
|
2016-07-06 09:59:52 +01:00
|
|
|
$declared = array_merge(get_declared_classes(), get_declared_interfaces());
|
|
|
|
if (function_exists('get_declared_traits')) {
|
|
|
|
$declared = array_merge($declared, get_declared_traits());
|
|
|
|
}
|
|
|
|
|
2011-01-29 16:58:40 +00:00
|
|
|
// don't include already declared classes
|
2012-07-03 16:40:35 +01:00
|
|
|
$classes = array_diff($classes, $declared);
|
2011-01-29 16:58:40 +00:00
|
|
|
|
|
|
|
// the cache is different depending on which classes are already declared
|
2013-07-30 13:08:17 +01:00
|
|
|
$name = $name.'-'.substr(hash('sha256', implode('|', $classes)), 0, 5);
|
2011-01-29 16:58:40 +00:00
|
|
|
}
|
|
|
|
|
2012-07-03 16:41:16 +01:00
|
|
|
$classes = array_unique($classes);
|
|
|
|
|
2016-09-05 13:06:16 +01:00
|
|
|
// cache the core classes
|
|
|
|
if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) {
|
|
|
|
throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir));
|
|
|
|
}
|
2016-10-25 09:15:38 +01:00
|
|
|
$cacheDir = rtrim(realpath($cacheDir) ?: $cacheDir, '/'.DIRECTORY_SEPARATOR);
|
2016-09-05 13:06:16 +01:00
|
|
|
$cache = $cacheDir.DIRECTORY_SEPARATOR.$name.$extension;
|
2010-05-06 12:25:53 +01:00
|
|
|
|
|
|
|
// auto-reload
|
|
|
|
$reload = false;
|
2010-05-07 15:09:11 +01:00
|
|
|
if ($autoReload) {
|
2013-05-24 18:54:44 +01:00
|
|
|
$metadata = $cache.'.meta';
|
2011-08-29 14:28:03 +01:00
|
|
|
if (!is_file($metadata) || !is_file($cache)) {
|
2010-05-06 12:25:53 +01:00
|
|
|
$reload = true;
|
2010-05-07 15:09:11 +01:00
|
|
|
} else {
|
2010-05-06 12:25:53 +01:00
|
|
|
$time = filemtime($cache);
|
|
|
|
$meta = unserialize(file_get_contents($metadata));
|
|
|
|
|
2012-07-02 18:17:58 +01:00
|
|
|
sort($meta[1]);
|
|
|
|
sort($classes);
|
|
|
|
|
2010-05-07 15:09:11 +01:00
|
|
|
if ($meta[1] != $classes) {
|
2010-05-06 12:25:53 +01:00
|
|
|
$reload = true;
|
2010-05-07 15:09:11 +01:00
|
|
|
} else {
|
2010-05-08 14:32:30 +01:00
|
|
|
foreach ($meta[0] as $resource) {
|
2011-08-29 14:28:03 +01:00
|
|
|
if (!is_file($resource) || filemtime($resource) > $time) {
|
2010-05-06 12:25:53 +01:00
|
|
|
$reload = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-02-17 13:54:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-06 09:59:52 +01:00
|
|
|
if (!$reload && file_exists($cache)) {
|
2010-05-06 12:25:53 +01:00
|
|
|
require_once $cache;
|
2010-02-17 13:54:36 +00:00
|
|
|
|
2010-05-06 12:25:53 +01:00
|
|
|
return;
|
|
|
|
}
|
2016-07-06 09:59:52 +01:00
|
|
|
if (!$adaptive) {
|
|
|
|
$declared = array_merge(get_declared_classes(), get_declared_interfaces());
|
|
|
|
if (function_exists('get_declared_traits')) {
|
|
|
|
$declared = array_merge($declared, get_declared_traits());
|
|
|
|
}
|
|
|
|
}
|
2010-02-17 13:54:36 +00:00
|
|
|
|
2016-09-05 13:06:16 +01:00
|
|
|
$c = '(?:\s*+(?:(?:#|//)[^\n]*+\n|/\*(?:(?<!\*/).)++)?+)*+';
|
|
|
|
$strictTypesRegex = str_replace('.', $c, "'^<\?php\s.declare.\(.strict_types.=.1.\).;'is");
|
|
|
|
|
|
|
|
$cacheDir = explode(DIRECTORY_SEPARATOR, $cacheDir);
|
2010-05-06 12:25:53 +01:00
|
|
|
$files = array();
|
|
|
|
$content = '';
|
2012-07-03 16:41:16 +01:00
|
|
|
foreach (self::getOrderedClasses($classes) as $class) {
|
2013-04-26 09:12:31 +01:00
|
|
|
if (in_array($class->getName(), $declared)) {
|
2012-07-03 16:41:16 +01:00
|
|
|
continue;
|
2010-05-06 12:25:53 +01:00
|
|
|
}
|
2010-02-17 13:54:36 +00:00
|
|
|
|
2016-09-05 13:06:16 +01:00
|
|
|
$files[] = $file = $class->getFileName();
|
|
|
|
$c = file_get_contents($file);
|
2010-02-17 13:54:36 +00:00
|
|
|
|
2016-09-05 13:06:16 +01:00
|
|
|
if (preg_match($strictTypesRegex, $c)) {
|
|
|
|
$file = explode(DIRECTORY_SEPARATOR, $file);
|
2011-01-16 09:16:20 +00:00
|
|
|
|
2016-09-05 13:06:16 +01:00
|
|
|
for ($i = 0; isset($file[$i], $cacheDir[$i]); ++$i) {
|
|
|
|
if ($file[$i] !== $cacheDir[$i]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (1 >= $i) {
|
|
|
|
$file = var_export(implode(DIRECTORY_SEPARATOR, $file), true);
|
|
|
|
} else {
|
|
|
|
$file = array_slice($file, $i);
|
|
|
|
$file = str_repeat('..'.DIRECTORY_SEPARATOR, count($cacheDir) - $i).implode(DIRECTORY_SEPARATOR, $file);
|
|
|
|
$file = '__DIR__.'.var_export(DIRECTORY_SEPARATOR.$file, true);
|
|
|
|
}
|
2011-01-16 09:16:20 +00:00
|
|
|
|
2016-09-05 13:06:16 +01:00
|
|
|
$c = "\nnamespace {require $file;}";
|
|
|
|
} else {
|
|
|
|
$c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', $c);
|
2012-10-14 12:45:05 +01:00
|
|
|
|
2016-09-05 13:06:16 +01:00
|
|
|
// fakes namespace declaration for global code
|
|
|
|
if (!$class->inNamespace()) {
|
|
|
|
$c = "\nnamespace\n{\n".$c."\n}\n";
|
|
|
|
}
|
2010-02-17 13:54:36 +00:00
|
|
|
|
2016-09-05 13:06:16 +01:00
|
|
|
$c = self::fixNamespaceDeclarations('<?php '.$c);
|
|
|
|
$c = preg_replace('/^\s*<\?php/', '', $c);
|
|
|
|
}
|
|
|
|
|
|
|
|
$content .= $c;
|
2010-05-06 12:25:53 +01:00
|
|
|
}
|
2011-06-17 13:14:54 +01:00
|
|
|
self::writeCacheFile($cache, '<?php '.$content);
|
2010-02-17 13:54:36 +00:00
|
|
|
|
2010-05-07 15:09:11 +01:00
|
|
|
if ($autoReload) {
|
2010-05-06 12:25:53 +01:00
|
|
|
// save the resources
|
|
|
|
self::writeCacheFile($metadata, serialize(array($files, $classes)));
|
|
|
|
}
|
2010-02-17 13:54:36 +00:00
|
|
|
}
|
|
|
|
|
2011-01-16 09:16:20 +00:00
|
|
|
/**
|
|
|
|
* Adds brackets around each namespace if it's not already the case.
|
2011-02-06 21:31:19 +00:00
|
|
|
*
|
2011-02-06 22:27:05 +00:00
|
|
|
* @param string $source Namespace string
|
2011-02-06 21:31:19 +00:00
|
|
|
*
|
|
|
|
* @return string Namespaces with brackets
|
2011-01-16 09:16:20 +00:00
|
|
|
*/
|
2012-07-09 13:50:58 +01:00
|
|
|
public static function fixNamespaceDeclarations($source)
|
2011-01-16 09:16:20 +00:00
|
|
|
{
|
2012-10-27 16:55:46 +01:00
|
|
|
if (!function_exists('token_get_all') || !self::$useTokenizer) {
|
2015-11-25 17:16:22 +00:00
|
|
|
if (preg_match('/(^|\s)namespace(.*?)\s*;/', $source)) {
|
|
|
|
$source = preg_replace('/(^|\s)namespace(.*?)\s*;/', "$1namespace$2\n{", $source)."}\n";
|
2012-10-27 16:55:46 +01:00
|
|
|
}
|
|
|
|
|
2011-01-16 09:16:20 +00:00
|
|
|
return $source;
|
|
|
|
}
|
|
|
|
|
2012-12-10 12:27:35 +00:00
|
|
|
$rawChunk = '';
|
2011-01-16 09:16:20 +00:00
|
|
|
$output = '';
|
|
|
|
$inNamespace = false;
|
|
|
|
$tokens = token_get_all($source);
|
|
|
|
|
2016-01-14 13:08:57 +00:00
|
|
|
for ($i = 0; isset($tokens[$i]); ++$i) {
|
|
|
|
$token = $tokens[$i];
|
|
|
|
if (!isset($token[1]) || 'b"' === $token) {
|
2012-12-10 12:27:35 +00:00
|
|
|
$rawChunk .= $token;
|
2011-06-17 13:14:54 +01:00
|
|
|
} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
|
|
|
|
// strip comments
|
|
|
|
continue;
|
2011-01-16 09:16:20 +00:00
|
|
|
} elseif (T_NAMESPACE === $token[0]) {
|
|
|
|
if ($inNamespace) {
|
2012-12-10 12:27:35 +00:00
|
|
|
$rawChunk .= "}\n";
|
2011-01-16 09:16:20 +00:00
|
|
|
}
|
2012-12-10 12:27:35 +00:00
|
|
|
$rawChunk .= $token[1];
|
2011-01-16 09:16:20 +00:00
|
|
|
|
|
|
|
// namespace name and whitespaces
|
2016-01-14 13:08:57 +00:00
|
|
|
while (isset($tokens[++$i][1]) && in_array($tokens[$i][0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
|
|
|
|
$rawChunk .= $tokens[$i][1];
|
2011-01-16 09:16:20 +00:00
|
|
|
}
|
2016-01-14 13:08:57 +00:00
|
|
|
if ('{' === $tokens[$i]) {
|
2011-01-16 09:16:20 +00:00
|
|
|
$inNamespace = false;
|
2016-01-14 13:08:57 +00:00
|
|
|
--$i;
|
2011-01-16 09:16:20 +00:00
|
|
|
} else {
|
2013-03-19 08:18:44 +00:00
|
|
|
$rawChunk = rtrim($rawChunk)."\n{";
|
2011-01-16 09:16:20 +00:00
|
|
|
$inNamespace = true;
|
|
|
|
}
|
2012-12-10 12:27:35 +00:00
|
|
|
} elseif (T_START_HEREDOC === $token[0]) {
|
2013-03-19 08:18:44 +00:00
|
|
|
$output .= self::compressCode($rawChunk).$token[1];
|
2012-12-10 12:27:35 +00:00
|
|
|
do {
|
2016-01-14 13:08:57 +00:00
|
|
|
$token = $tokens[++$i];
|
|
|
|
$output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token;
|
2012-12-10 12:27:35 +00:00
|
|
|
} while ($token[0] !== T_END_HEREDOC);
|
2013-03-19 08:32:24 +00:00
|
|
|
$output .= "\n";
|
2012-12-10 12:27:35 +00:00
|
|
|
$rawChunk = '';
|
|
|
|
} elseif (T_CONSTANT_ENCAPSED_STRING === $token[0]) {
|
2013-03-19 08:18:44 +00:00
|
|
|
$output .= self::compressCode($rawChunk).$token[1];
|
2012-12-10 12:27:35 +00:00
|
|
|
$rawChunk = '';
|
2011-01-16 09:16:20 +00:00
|
|
|
} else {
|
2012-12-10 12:27:35 +00:00
|
|
|
$rawChunk .= $token[1];
|
2011-01-16 09:16:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($inNamespace) {
|
2012-12-10 12:27:35 +00:00
|
|
|
$rawChunk .= "}\n";
|
2011-01-16 09:16:20 +00:00
|
|
|
}
|
|
|
|
|
2016-01-15 08:12:11 +00:00
|
|
|
$output .= self::compressCode($rawChunk);
|
|
|
|
|
|
|
|
if (PHP_VERSION_ID >= 70000) {
|
|
|
|
// PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
|
|
|
|
unset($tokens, $rawChunk);
|
|
|
|
gc_mem_caches();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $output;
|
2012-12-10 12:27:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method is only useful for testing.
|
|
|
|
*/
|
|
|
|
public static function enableTokenizer($bool)
|
|
|
|
{
|
2014-04-12 18:44:00 +01:00
|
|
|
self::$useTokenizer = (bool) $bool;
|
2012-12-10 12:27:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Strips leading & trailing ws, multiple EOL, multiple ws.
|
|
|
|
*
|
|
|
|
* @param string $code Original PHP code
|
|
|
|
*
|
|
|
|
* @return string compressed code
|
|
|
|
*/
|
|
|
|
private static function compressCode($code)
|
|
|
|
{
|
|
|
|
return preg_replace(
|
|
|
|
array('/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'),
|
|
|
|
array('', '', "\n", ' '),
|
|
|
|
$code
|
|
|
|
);
|
2011-01-16 09:16:20 +00:00
|
|
|
}
|
|
|
|
|
2011-02-06 21:31:19 +00:00
|
|
|
/**
|
2011-02-06 23:58:54 +00:00
|
|
|
* Writes a cache file.
|
2011-02-06 21:31:19 +00:00
|
|
|
*
|
2012-04-23 06:37:21 +01:00
|
|
|
* @param string $file Filename
|
2011-02-06 22:27:05 +00:00
|
|
|
* @param string $content Temporary file content
|
2011-02-06 21:31:19 +00:00
|
|
|
*
|
|
|
|
* @throws \RuntimeException when a cache file cannot be written
|
|
|
|
*/
|
2012-07-09 13:50:58 +01:00
|
|
|
private static function writeCacheFile($file, $content)
|
2010-02-17 13:54:36 +00:00
|
|
|
{
|
2010-05-06 12:25:53 +01:00
|
|
|
$tmpFile = tempnam(dirname($file), basename($file));
|
2010-08-12 20:58:19 +01:00
|
|
|
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
|
2012-05-15 07:43:47 +01:00
|
|
|
@chmod($file, 0666 & ~umask());
|
2010-02-17 13:54:36 +00:00
|
|
|
|
2010-08-12 20:58:19 +01:00
|
|
|
return;
|
2010-06-23 06:35:46 +01:00
|
|
|
}
|
|
|
|
|
2010-08-12 20:58:19 +01:00
|
|
|
throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
|
2010-05-06 12:25:53 +01:00
|
|
|
}
|
2011-01-27 13:11:54 +00:00
|
|
|
|
2012-07-02 18:17:58 +01:00
|
|
|
/**
|
2012-07-03 16:41:16 +01:00
|
|
|
* Gets an ordered array of passed classes including all their dependencies.
|
2012-07-02 18:17:58 +01:00
|
|
|
*
|
|
|
|
* @param array $classes
|
|
|
|
*
|
2012-11-01 15:08:59 +00:00
|
|
|
* @return \ReflectionClass[] An array of sorted \ReflectionClass instances (dependencies added if needed)
|
2012-07-03 16:41:16 +01:00
|
|
|
*
|
2012-07-02 18:17:58 +01:00
|
|
|
* @throws \InvalidArgumentException When a class can't be loaded
|
|
|
|
*/
|
2012-07-09 13:50:58 +01:00
|
|
|
private static function getOrderedClasses(array $classes)
|
2012-07-02 18:17:58 +01:00
|
|
|
{
|
2012-07-03 16:41:16 +01:00
|
|
|
$map = array();
|
|
|
|
self::$seen = array();
|
2012-07-02 18:17:58 +01:00
|
|
|
foreach ($classes as $class) {
|
|
|
|
try {
|
|
|
|
$reflectionClass = new \ReflectionClass($class);
|
|
|
|
} catch (\ReflectionException $e) {
|
|
|
|
throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
|
|
|
|
}
|
|
|
|
|
2012-07-03 16:41:16 +01:00
|
|
|
$map = array_merge($map, self::getClassHierarchy($reflectionClass));
|
|
|
|
}
|
|
|
|
|
|
|
|
return $map;
|
|
|
|
}
|
|
|
|
|
2012-07-09 13:50:58 +01:00
|
|
|
private static function getClassHierarchy(\ReflectionClass $class)
|
2012-07-03 16:41:16 +01:00
|
|
|
{
|
|
|
|
if (isset(self::$seen[$class->getName()])) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-12-10 12:27:35 +00:00
|
|
|
$traits = array();
|
|
|
|
|
2015-10-19 15:17:09 +01:00
|
|
|
if (method_exists('ReflectionClass', 'getTraits')) {
|
2012-07-03 16:41:16 +01:00
|
|
|
foreach ($classes as $c) {
|
2012-12-10 12:27:35 +00:00
|
|
|
foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) {
|
|
|
|
if ($trait !== $c) {
|
|
|
|
$traits[] = $trait;
|
|
|
|
}
|
2012-07-03 16:41:16 +01:00
|
|
|
}
|
|
|
|
}
|
2012-07-02 18:17:58 +01:00
|
|
|
}
|
|
|
|
|
2012-12-10 12:27:35 +00:00
|
|
|
return array_merge(self::getInterfaces($class), $traits, $classes);
|
2012-07-10 19:27:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private static function getInterfaces(\ReflectionClass $class)
|
|
|
|
{
|
|
|
|
$classes = array();
|
|
|
|
|
2012-07-03 16:41:16 +01:00
|
|
|
foreach ($class->getInterfaces() as $interface) {
|
2012-07-10 19:27:54 +01:00
|
|
|
$classes = array_merge($classes, self::getInterfaces($interface));
|
|
|
|
}
|
2012-07-02 18:17:58 +01:00
|
|
|
|
2012-07-10 19:27:54 +01:00
|
|
|
if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) {
|
|
|
|
self::$seen[$class->getName()] = true;
|
|
|
|
|
|
|
|
$classes[] = $class;
|
2012-07-03 16:41:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $classes;
|
2012-07-02 18:17:58 +01:00
|
|
|
}
|
|
|
|
|
2012-12-10 12:27:35 +00:00
|
|
|
private static function computeTraitDeps(\ReflectionClass $class)
|
2012-07-02 18:17:58 +01:00
|
|
|
{
|
2012-07-03 16:41:16 +01:00
|
|
|
$traits = $class->getTraits();
|
2012-12-10 12:27:35 +00:00
|
|
|
$deps = array($class->getName() => $traits);
|
2012-07-03 16:41:16 +01:00
|
|
|
while ($trait = array_pop($traits)) {
|
|
|
|
if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) {
|
2012-12-10 12:27:35 +00:00
|
|
|
self::$seen[$trait->getName()] = true;
|
|
|
|
$traitDeps = $trait->getTraits();
|
|
|
|
$deps[$trait->getName()] = $traitDeps;
|
|
|
|
$traits = array_merge($traits, $traitDeps);
|
2012-07-03 16:41:16 +01:00
|
|
|
}
|
2012-07-02 18:17:58 +01:00
|
|
|
}
|
|
|
|
|
2012-12-10 12:27:35 +00:00
|
|
|
return $deps;
|
2012-07-02 18:17:58 +01:00
|
|
|
}
|
2012-10-27 16:58:32 +01:00
|
|
|
|
2012-10-27 16:55:46 +01:00
|
|
|
/**
|
2012-12-10 12:27:35 +00:00
|
|
|
* Dependencies resolution.
|
|
|
|
*
|
|
|
|
* This function does not check for circular dependencies as it should never
|
|
|
|
* occur with PHP traits.
|
|
|
|
*
|
2014-11-30 13:33:44 +00:00
|
|
|
* @param array $tree The dependency tree
|
|
|
|
* @param \ReflectionClass $node The node
|
|
|
|
* @param \ArrayObject $resolved An array of already resolved dependencies
|
|
|
|
* @param \ArrayObject $unresolved An array of dependencies to be resolved
|
2012-12-10 12:27:35 +00:00
|
|
|
*
|
|
|
|
* @return \ArrayObject The dependencies for the given node
|
|
|
|
*
|
|
|
|
* @throws \RuntimeException if a circular dependency is detected
|
2012-10-27 16:55:46 +01:00
|
|
|
*/
|
2012-12-10 12:27:35 +00:00
|
|
|
private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null)
|
2012-10-27 16:55:46 +01:00
|
|
|
{
|
2012-12-10 12:27:35 +00:00
|
|
|
if (null === $resolved) {
|
|
|
|
$resolved = new \ArrayObject();
|
|
|
|
}
|
|
|
|
if (null === $unresolved) {
|
|
|
|
$unresolved = new \ArrayObject();
|
|
|
|
}
|
|
|
|
$nodeName = $node->getName();
|
2014-12-04 21:06:34 +00:00
|
|
|
|
|
|
|
if (isset($tree[$nodeName])) {
|
|
|
|
$unresolved[$nodeName] = $node;
|
|
|
|
foreach ($tree[$nodeName] as $dependency) {
|
|
|
|
if (!$resolved->offsetExists($dependency->getName())) {
|
|
|
|
self::resolveDependencies($tree, $dependency, $resolved, $unresolved);
|
|
|
|
}
|
2012-12-10 12:27:35 +00:00
|
|
|
}
|
2014-12-04 21:06:34 +00:00
|
|
|
$resolved[$nodeName] = $node;
|
|
|
|
unset($unresolved[$nodeName]);
|
2012-12-10 12:27:35 +00:00
|
|
|
}
|
2013-03-01 10:42:10 +00:00
|
|
|
|
2012-12-10 12:27:35 +00:00
|
|
|
return $resolved;
|
2012-10-27 16:55:46 +01:00
|
|
|
}
|
2010-02-17 13:54:36 +00:00
|
|
|
}
|