vendor/symfony/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     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\ClassLoader;
       
    13 
       
    14 /**
       
    15  * ClassCollectionLoader.
       
    16  *
       
    17  * @author Fabien Potencier <fabien@symfony.com>
       
    18  */
       
    19 class ClassCollectionLoader
       
    20 {
       
    21     static private $loaded;
       
    22 
       
    23     /**
       
    24      * Loads a list of classes and caches them in one big file.
       
    25      *
       
    26      * @param array   $classes    An array of classes to load
       
    27      * @param string  $cacheDir   A cache directory
       
    28      * @param string  $name       The cache name prefix
       
    29      * @param Boolean $autoReload Whether to flush the cache when the cache is stale or not
       
    30      * @param Boolean $adaptive   Whether to remove already declared classes or not
       
    31      * @param string  $extension  File extension of the resulting file
       
    32      *
       
    33      * @throws \InvalidArgumentException When class can't be loaded
       
    34      */
       
    35     static public function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php')
       
    36     {
       
    37         // each $name can only be loaded once per PHP process
       
    38         if (isset(self::$loaded[$name])) {
       
    39             return;
       
    40         }
       
    41 
       
    42         self::$loaded[$name] = true;
       
    43 
       
    44         if ($adaptive) {
       
    45             // don't include already declared classes
       
    46             $classes = array_diff($classes, get_declared_classes(), get_declared_interfaces());
       
    47 
       
    48             // the cache is different depending on which classes are already declared
       
    49             $name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5);
       
    50         }
       
    51 
       
    52         $cache = $cacheDir.'/'.$name.$extension;
       
    53 
       
    54         // auto-reload
       
    55         $reload = false;
       
    56         if ($autoReload) {
       
    57             $metadata = $cacheDir.'/'.$name.$extension.'.meta';
       
    58             if (!file_exists($metadata) || !file_exists($cache)) {
       
    59                 $reload = true;
       
    60             } else {
       
    61                 $time = filemtime($cache);
       
    62                 $meta = unserialize(file_get_contents($metadata));
       
    63 
       
    64                 if ($meta[1] != $classes) {
       
    65                     $reload = true;
       
    66                 } else {
       
    67                     foreach ($meta[0] as $resource) {
       
    68                         if (!file_exists($resource) || filemtime($resource) > $time) {
       
    69                             $reload = true;
       
    70 
       
    71                             break;
       
    72                         }
       
    73                     }
       
    74                 }
       
    75             }
       
    76         }
       
    77 
       
    78         if (!$reload && file_exists($cache)) {
       
    79             require_once $cache;
       
    80 
       
    81             return;
       
    82         }
       
    83 
       
    84         $files = array();
       
    85         $content = '';
       
    86         foreach ($classes as $class) {
       
    87             if (!class_exists($class) && !interface_exists($class)) {
       
    88                 throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
       
    89             }
       
    90 
       
    91             $r = new \ReflectionClass($class);
       
    92             $files[] = $r->getFileName();
       
    93 
       
    94             $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($r->getFileName()));
       
    95 
       
    96             // add namespace declaration for global code
       
    97             if (!$r->inNamespace()) {
       
    98                 $c = "\nnamespace\n{\n".self::stripComments($c)."\n}\n";
       
    99             } else {
       
   100                 $c = self::fixNamespaceDeclarations('<?php '.$c);
       
   101                 $c = preg_replace('/^\s*<\?php/', '', $c);
       
   102             }
       
   103 
       
   104             $content .= $c;
       
   105         }
       
   106 
       
   107         // cache the core classes
       
   108         if (!is_dir(dirname($cache))) {
       
   109             mkdir(dirname($cache), 0777, true);
       
   110         }
       
   111         self::writeCacheFile($cache, '<?php '.$content);
       
   112 
       
   113         if ($autoReload) {
       
   114             // save the resources
       
   115             self::writeCacheFile($metadata, serialize(array($files, $classes)));
       
   116         }
       
   117     }
       
   118 
       
   119     /**
       
   120      * Adds brackets around each namespace if it's not already the case.
       
   121      *
       
   122      * @param string $source Namespace string
       
   123      *
       
   124      * @return string Namespaces with brackets
       
   125      */
       
   126     static public function fixNamespaceDeclarations($source)
       
   127     {
       
   128         if (!function_exists('token_get_all')) {
       
   129             return $source;
       
   130         }
       
   131 
       
   132         $output = '';
       
   133         $inNamespace = false;
       
   134         $tokens = token_get_all($source);
       
   135 
       
   136         for ($i = 0, $max = count($tokens); $i < $max; $i++) {
       
   137             $token = $tokens[$i];
       
   138             if (is_string($token)) {
       
   139                 $output .= $token;
       
   140             } elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
       
   141                 // strip comments
       
   142                 continue;
       
   143             } elseif (T_NAMESPACE === $token[0]) {
       
   144                 if ($inNamespace) {
       
   145                     $output .= "}\n";
       
   146                 }
       
   147                 $output .= $token[1];
       
   148 
       
   149                 // namespace name and whitespaces
       
   150                 while (($t = $tokens[++$i]) && is_array($t) && in_array($t[0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
       
   151                     $output .= $t[1];
       
   152                 }
       
   153                 if (is_string($t) && '{' === $t) {
       
   154                     $inNamespace = false;
       
   155                     --$i;
       
   156                 } else {
       
   157                     $output .= "\n{";
       
   158                     $inNamespace = true;
       
   159                 }
       
   160             } else {
       
   161                 $output .= $token[1];
       
   162             }
       
   163         }
       
   164 
       
   165         if ($inNamespace) {
       
   166             $output .= "}\n";
       
   167         }
       
   168 
       
   169         return $output;
       
   170     }
       
   171 
       
   172     /**
       
   173      * Writes a cache file.
       
   174      *
       
   175      * @param string $file Filename
       
   176      * @param string $content Temporary file content
       
   177      *
       
   178      * @throws \RuntimeException when a cache file cannot be written
       
   179      */
       
   180     static private function writeCacheFile($file, $content)
       
   181     {
       
   182         $tmpFile = tempnam(dirname($file), basename($file));
       
   183         if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
       
   184             chmod($file, 0644);
       
   185 
       
   186             return;
       
   187         }
       
   188 
       
   189         throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
       
   190     }
       
   191 
       
   192     /**
       
   193      * Removes comments from a PHP source string.
       
   194      *
       
   195      * We don't use the PHP php_strip_whitespace() function
       
   196      * as we want the content to be readable and well-formatted.
       
   197      *
       
   198      * @param string $source A PHP string
       
   199      *
       
   200      * @return string The PHP string with the comments removed
       
   201      */
       
   202     static private function stripComments($source)
       
   203     {
       
   204         if (!function_exists('token_get_all')) {
       
   205             return $source;
       
   206         }
       
   207 
       
   208         $output = '';
       
   209         foreach (token_get_all($source) as $token) {
       
   210             if (is_string($token)) {
       
   211                 $output .= $token;
       
   212             } elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
       
   213                 $output .= $token[1];
       
   214             }
       
   215         }
       
   216 
       
   217         // replace multiple new lines with a single newline
       
   218         $output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output);
       
   219 
       
   220         return $output;
       
   221     }
       
   222 }