Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 321 lines (266 sloc) 9.808 kB
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
0364560 @fabpot replaced symfony-project.org by symfony.com
fabpot authored
6 * (c) Fabien Potencier <fabien@symfony.com>
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
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 *
0364560 @fabpot replaced symfony-project.org by symfony.com
fabpot authored
17 * @author Fabien Potencier <fabien@symfony.com>
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
18 */
19 class ClassCollectionLoader
20 {
5256043 @fabpot fixed CS
fabpot authored
21 private static $loaded;
22 private static $seen;
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
23
24 /**
25 * Loads a list of classes and caches them in one big file.
26 *
27 * @param array $classes An array of classes to load
28 * @param string $cacheDir A cache directory
29 * @param string $name The cache name prefix
30 * @param Boolean $autoReload Whether to flush the cache when the cache is stale or not
31 * @param Boolean $adaptive Whether to remove already declared classes or not
5822ece @lsmith77 make it possible to define the file extension
lsmith77 authored
32 * @param string $extension File extension of the resulting file
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
33 *
34 * @throws \InvalidArgumentException When class can't be loaded
35 */
5256043 @fabpot fixed CS
fabpot authored
36 public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php')
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
37 {
38 // each $name can only be loaded once per PHP process
39 if (isset(self::$loaded[$name])) {
40 return;
41 }
42
43 self::$loaded[$name] = true;
44
ddc8887 @fabpot [ClassLoader] added missing support for PHP 5.4 traits
fabpot authored
45 $declared = array_merge(get_declared_classes(), get_declared_interfaces());
46 if (function_exists('get_declared_traits')) {
47 $declared = array_merge($declared, get_declared_traits());
48 }
49
72153e3 @fabpot reverted changes to the adaptive cache loader cache
fabpot authored
50 if ($adaptive) {
51 // don't include already declared classes
ddc8887 @fabpot [ClassLoader] added missing support for PHP 5.4 traits
fabpot authored
52 $classes = array_diff($classes, $declared);
72153e3 @fabpot reverted changes to the adaptive cache loader cache
fabpot authored
53
54 // the cache is different depending on which classes are already declared
55 $name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5);
56 }
57
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
58 $classes = array_unique($classes);
59
5822ece @lsmith77 make it possible to define the file extension
lsmith77 authored
60 $cache = $cacheDir.'/'.$name.$extension;
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
61
62 // auto-reload
63 $reload = false;
64 if ($autoReload) {
2a0e3f6 @fabpot [ClassLoader] made a small change to be consistent with the previous …
fabpot authored
65 $metadata = $cacheDir.'/'.$name.$extension.'.meta';
c6336b8 @fabpot converted file_exists calls to either is_file or is_dir where it make…
fabpot authored
66 if (!is_file($metadata) || !is_file($cache)) {
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
67 $reload = true;
68 } else {
69 $time = filemtime($cache);
70 $meta = unserialize(file_get_contents($metadata));
71
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
72 sort($meta[1]);
73 sort($classes);
74
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
75 if ($meta[1] != $classes) {
76 $reload = true;
77 } else {
78 foreach ($meta[0] as $resource) {
c6336b8 @fabpot converted file_exists calls to either is_file or is_dir where it make…
fabpot authored
79 if (!is_file($resource) || filemtime($resource) > $time) {
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
80 $reload = true;
81
82 break;
83 }
84 }
85 }
86 }
87 }
88
c6336b8 @fabpot converted file_exists calls to either is_file or is_dir where it make…
fabpot authored
89 if (!$reload && is_file($cache)) {
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
90 require_once $cache;
91
92 return;
93 }
94
95 $files = array();
96 $content = '';
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
97 foreach (self::getOrderedClasses($classes) as $class) {
98 if (in_array($class->getName(), $declared)) {
99 continue;
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
100 }
101
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
102 $files[] = $class->getFileName();
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
103
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
104 $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($class->getFileName()));
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
105
106 // add namespace declaration for global code
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
107 if (!$class->inNamespace()) {
6894a17 @fabpot [ClassLoader] made another performance improvement
fabpot authored
108 $c = "\nnamespace\n{\n".self::stripComments($c)."\n}\n";
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
109 } else {
110 $c = self::fixNamespaceDeclarations('<?php '.$c);
111 $c = preg_replace('/^\s*<\?php/', '', $c);
112 }
113
114 $content .= $c;
115 }
116
117 // cache the core classes
118 if (!is_dir(dirname($cache))) {
119 mkdir(dirname($cache), 0777, true);
120 }
6894a17 @fabpot [ClassLoader] made another performance improvement
fabpot authored
121 self::writeCacheFile($cache, '<?php '.$content);
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
122
123 if ($autoReload) {
124 // save the resources
125 self::writeCacheFile($metadata, serialize(array($files, $classes)));
126 }
127 }
128
129 /**
130 * Adds brackets around each namespace if it's not already the case.
7af6c90 @merk Classloader PHPDoc
merk authored
131 *
e1c74d7 @merk PHPDoc fixes
merk authored
132 * @param string $source Namespace string
7af6c90 @merk Classloader PHPDoc
merk authored
133 *
134 * @return string Namespaces with brackets
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
135 */
5256043 @fabpot fixed CS
fabpot authored
136 public static function fixNamespaceDeclarations($source)
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
137 {
138 if (!function_exists('token_get_all')) {
139 return $source;
140 }
141
142 $output = '';
143 $inNamespace = false;
144 $tokens = token_get_all($source);
145
96b7979 @fabpot [ClassLoader] made a big performance improvement
fabpot authored
146 for ($i = 0, $max = count($tokens); $i < $max; $i++) {
147 $token = $tokens[$i];
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
148 if (is_string($token)) {
149 $output .= $token;
6894a17 @fabpot [ClassLoader] made another performance improvement
fabpot authored
150 } elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
151 // strip comments
152 continue;
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
153 } elseif (T_NAMESPACE === $token[0]) {
154 if ($inNamespace) {
155 $output .= "}\n";
156 }
157 $output .= $token[1];
158
159 // namespace name and whitespaces
96b7979 @fabpot [ClassLoader] made a big performance improvement
fabpot authored
160 while (($t = $tokens[++$i]) && is_array($t) && in_array($t[0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
161 $output .= $t[1];
162 }
163 if (is_string($t) && '{' === $t) {
164 $inNamespace = false;
96b7979 @fabpot [ClassLoader] made a big performance improvement
fabpot authored
165 --$i;
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
166 } else {
0b8b41c @fabpot fixed CS
fabpot authored
167 $output = rtrim($output);
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
168 $output .= "\n{";
169 $inNamespace = true;
170 }
171 } else {
172 $output .= $token[1];
173 }
174 }
175
176 if ($inNamespace) {
177 $output .= "}\n";
178 }
179
180 return $output;
181 }
182
7af6c90 @merk Classloader PHPDoc
merk authored
183 /**
ad17481 @merk PHPDoc style fix
merk authored
184 * Writes a cache file.
7af6c90 @merk Classloader PHPDoc
merk authored
185 *
6329e84 @gajdaw [2.0][Component][ClassLoader] cs
gajdaw authored
186 * @param string $file Filename
e1c74d7 @merk PHPDoc fixes
merk authored
187 * @param string $content Temporary file content
7af6c90 @merk Classloader PHPDoc
merk authored
188 *
189 * @throws \RuntimeException when a cache file cannot be written
190 */
5256043 @fabpot fixed CS
fabpot authored
191 private static function writeCacheFile($file, $content)
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
192 {
193 $tmpFile = tempnam(dirname($file), basename($file));
194 if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
c57e62c @fabpot added @ to all chmod() calls to avoid PHP warnings (operation not per…
fabpot authored
195 @chmod($file, 0666 & ~umask());
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
196
197 return;
198 }
199
200 throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
201 }
202
203 /**
204 * Removes comments from a PHP source string.
205 *
206 * We don't use the PHP php_strip_whitespace() function
207 * as we want the content to be readable and well-formatted.
208 *
209 * @param string $source A PHP string
210 *
211 * @return string The PHP string with the comments removed
212 */
5256043 @fabpot fixed CS
fabpot authored
213 private static function stripComments($source)
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
214 {
215 if (!function_exists('token_get_all')) {
216 return $source;
217 }
218
219 $output = '';
220 foreach (token_get_all($source) as $token) {
221 if (is_string($token)) {
222 $output .= $token;
223 } elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
224 $output .= $token[1];
225 }
226 }
227
228 // replace multiple new lines with a single newline
229 $output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output);
230
231 return $output;
232 }
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
233
234 /**
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
235 * Gets an ordered array of passed classes including all their dependencies.
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
236 *
237 * @param array $classes
238 *
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
239 * @return array An array of sorted \ReflectionClass instances (dependencies added if needed)
240 *
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
241 * @throws \InvalidArgumentException When a class can't be loaded
242 */
5256043 @fabpot fixed CS
fabpot authored
243 private static function getOrderedClasses(array $classes)
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
244 {
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
245 $map = array();
246 self::$seen = array();
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
247 foreach ($classes as $class) {
248 try {
249 $reflectionClass = new \ReflectionClass($class);
250 } catch (\ReflectionException $e) {
251 throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
252 }
253
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
254 $map = array_merge($map, self::getClassHierarchy($reflectionClass));
255 }
256
257 return $map;
258 }
259
5256043 @fabpot fixed CS
fabpot authored
260 private static function getClassHierarchy(\ReflectionClass $class)
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
261 {
262 if (isset(self::$seen[$class->getName()])) {
263 return array();
264 }
265
266 self::$seen[$class->getName()] = true;
267
268 $classes = array($class);
269 $parent = $class;
270 while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) {
271 self::$seen[$parent->getName()] = true;
272
273 array_unshift($classes, $parent);
274 }
275
276 if (function_exists('get_declared_traits')) {
277 foreach ($classes as $c) {
278 foreach (self::getTraits($c) as $trait) {
279 self::$seen[$trait->getName()] = true;
280
281 array_unshift($classes, $trait);
282 }
283 }
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
284 }
285
9c92900 @fabpot [ClassLoader] fixed order of interfaces in generated class collection…
fabpot authored
286 return array_merge(self::getInterfaces($class), $classes);
287 }
288
289 private static function getInterfaces(\ReflectionClass $class)
290 {
291 $classes = array();
292
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
293 foreach ($class->getInterfaces() as $interface) {
9c92900 @fabpot [ClassLoader] fixed order of interfaces in generated class collection…
fabpot authored
294 $classes = array_merge($classes, self::getInterfaces($interface));
295 }
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
296
9c92900 @fabpot [ClassLoader] fixed order of interfaces in generated class collection…
fabpot authored
297 if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) {
298 self::$seen[$class->getName()] = true;
299
300 $classes[] = $class;
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
301 }
302
303 return $classes;
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
304 }
305
5256043 @fabpot fixed CS
fabpot authored
306 private static function getTraits(\ReflectionClass $class)
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
307 {
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
308 $traits = $class->getTraits();
309 $classes = array();
310 while ($trait = array_pop($traits)) {
311 if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) {
312 $classes[] = $trait;
313
314 $traits = array_merge($traits, $trait->getTraits());
315 }
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
316 }
317
685dd12 @fabpot [ClassLoader] made ClassCollectionLoader::load() automatically includ…
fabpot authored
318 return $classes;
a04dde5 @bamarni [ClassLoader] ordered ClassCollectionLoader writing to avoid redeclar…
bamarni authored
319 }
bfaf58e @fabpot moved ClassLoaderCollection class to the ClassLoader component
fabpot authored
320 }
Something went wrong with that request. Please try again.