Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/http-kernel/Kernel.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
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\HttpKernel; | |
13 | |
14 use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; | |
15 use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; | |
16 use Symfony\Component\DependencyInjection\ContainerInterface; | |
17 use Symfony\Component\DependencyInjection\ContainerBuilder; | |
18 use Symfony\Component\DependencyInjection\Dumper\PhpDumper; | |
19 use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; | |
20 use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; | |
21 use Symfony\Component\DependencyInjection\Loader\IniFileLoader; | |
22 use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; | |
23 use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; | |
24 use Symfony\Component\DependencyInjection\Loader\ClosureLoader; | |
25 use Symfony\Component\HttpFoundation\Request; | |
26 use Symfony\Component\HttpFoundation\Response; | |
27 use Symfony\Component\HttpKernel\Bundle\BundleInterface; | |
28 use Symfony\Component\HttpKernel\Config\EnvParametersResource; | |
29 use Symfony\Component\HttpKernel\Config\FileLocator; | |
30 use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; | |
31 use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass; | |
32 use Symfony\Component\Config\Loader\LoaderResolver; | |
33 use Symfony\Component\Config\Loader\DelegatingLoader; | |
34 use Symfony\Component\Config\ConfigCache; | |
35 use Symfony\Component\ClassLoader\ClassCollectionLoader; | |
36 | |
37 /** | |
38 * The Kernel is the heart of the Symfony system. | |
39 * | |
40 * It manages an environment made of bundles. | |
41 * | |
42 * @author Fabien Potencier <fabien@symfony.com> | |
43 */ | |
44 abstract class Kernel implements KernelInterface, TerminableInterface | |
45 { | |
46 /** | |
47 * @var BundleInterface[] | |
48 */ | |
49 protected $bundles = array(); | |
50 | |
51 protected $bundleMap; | |
52 protected $container; | |
53 protected $rootDir; | |
54 protected $environment; | |
55 protected $debug; | |
56 protected $booted = false; | |
57 protected $name; | |
58 protected $startTime; | |
59 protected $loadClassCache; | |
60 | |
61 const VERSION = '3.2.8'; | |
62 const VERSION_ID = 30208; | |
63 const MAJOR_VERSION = 3; | |
64 const MINOR_VERSION = 2; | |
65 const RELEASE_VERSION = 8; | |
66 const EXTRA_VERSION = ''; | |
67 | |
68 const END_OF_MAINTENANCE = '07/2017'; | |
69 const END_OF_LIFE = '01/2018'; | |
70 | |
71 /** | |
72 * Constructor. | |
73 * | |
74 * @param string $environment The environment | |
75 * @param bool $debug Whether to enable debugging or not | |
76 */ | |
77 public function __construct($environment, $debug) | |
78 { | |
79 $this->environment = $environment; | |
80 $this->debug = (bool) $debug; | |
81 $this->rootDir = $this->getRootDir(); | |
82 $this->name = $this->getName(); | |
83 | |
84 if ($this->debug) { | |
85 $this->startTime = microtime(true); | |
86 } | |
87 } | |
88 | |
89 public function __clone() | |
90 { | |
91 if ($this->debug) { | |
92 $this->startTime = microtime(true); | |
93 } | |
94 | |
95 $this->booted = false; | |
96 $this->container = null; | |
97 } | |
98 | |
99 /** | |
100 * Boots the current kernel. | |
101 */ | |
102 public function boot() | |
103 { | |
104 if (true === $this->booted) { | |
105 return; | |
106 } | |
107 | |
108 if ($this->loadClassCache) { | |
109 $this->doLoadClassCache($this->loadClassCache[0], $this->loadClassCache[1]); | |
110 } | |
111 | |
112 // init bundles | |
113 $this->initializeBundles(); | |
114 | |
115 // init container | |
116 $this->initializeContainer(); | |
117 | |
118 foreach ($this->getBundles() as $bundle) { | |
119 $bundle->setContainer($this->container); | |
120 $bundle->boot(); | |
121 } | |
122 | |
123 $this->booted = true; | |
124 } | |
125 | |
126 /** | |
127 * {@inheritdoc} | |
128 */ | |
129 public function terminate(Request $request, Response $response) | |
130 { | |
131 if (false === $this->booted) { | |
132 return; | |
133 } | |
134 | |
135 if ($this->getHttpKernel() instanceof TerminableInterface) { | |
136 $this->getHttpKernel()->terminate($request, $response); | |
137 } | |
138 } | |
139 | |
140 /** | |
141 * {@inheritdoc} | |
142 */ | |
143 public function shutdown() | |
144 { | |
145 if (false === $this->booted) { | |
146 return; | |
147 } | |
148 | |
149 $this->booted = false; | |
150 | |
151 foreach ($this->getBundles() as $bundle) { | |
152 $bundle->shutdown(); | |
153 $bundle->setContainer(null); | |
154 } | |
155 | |
156 $this->container = null; | |
157 } | |
158 | |
159 /** | |
160 * {@inheritdoc} | |
161 */ | |
162 public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) | |
163 { | |
164 if (false === $this->booted) { | |
165 $this->boot(); | |
166 } | |
167 | |
168 return $this->getHttpKernel()->handle($request, $type, $catch); | |
169 } | |
170 | |
171 /** | |
172 * Gets a HTTP kernel from the container. | |
173 * | |
174 * @return HttpKernel | |
175 */ | |
176 protected function getHttpKernel() | |
177 { | |
178 return $this->container->get('http_kernel'); | |
179 } | |
180 | |
181 /** | |
182 * {@inheritdoc} | |
183 */ | |
184 public function getBundles() | |
185 { | |
186 return $this->bundles; | |
187 } | |
188 | |
189 /** | |
190 * {@inheritdoc} | |
191 */ | |
192 public function getBundle($name, $first = true) | |
193 { | |
194 if (!isset($this->bundleMap[$name])) { | |
195 throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your %s.php file?', $name, get_class($this))); | |
196 } | |
197 | |
198 if (true === $first) { | |
199 return $this->bundleMap[$name][0]; | |
200 } | |
201 | |
202 return $this->bundleMap[$name]; | |
203 } | |
204 | |
205 /** | |
206 * {@inheritdoc} | |
207 * | |
208 * @throws \RuntimeException if a custom resource is hidden by a resource in a derived bundle | |
209 */ | |
210 public function locateResource($name, $dir = null, $first = true) | |
211 { | |
212 if ('@' !== $name[0]) { | |
213 throw new \InvalidArgumentException(sprintf('A resource name must start with @ ("%s" given).', $name)); | |
214 } | |
215 | |
216 if (false !== strpos($name, '..')) { | |
217 throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name)); | |
218 } | |
219 | |
220 $bundleName = substr($name, 1); | |
221 $path = ''; | |
222 if (false !== strpos($bundleName, '/')) { | |
223 list($bundleName, $path) = explode('/', $bundleName, 2); | |
224 } | |
225 | |
226 $isResource = 0 === strpos($path, 'Resources') && null !== $dir; | |
227 $overridePath = substr($path, 9); | |
228 $resourceBundle = null; | |
229 $bundles = $this->getBundle($bundleName, false); | |
230 $files = array(); | |
231 | |
232 foreach ($bundles as $bundle) { | |
233 if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) { | |
234 if (null !== $resourceBundle) { | |
235 throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.', | |
236 $file, | |
237 $resourceBundle, | |
238 $dir.'/'.$bundles[0]->getName().$overridePath | |
239 )); | |
240 } | |
241 | |
242 if ($first) { | |
243 return $file; | |
244 } | |
245 $files[] = $file; | |
246 } | |
247 | |
248 if (file_exists($file = $bundle->getPath().'/'.$path)) { | |
249 if ($first && !$isResource) { | |
250 return $file; | |
251 } | |
252 $files[] = $file; | |
253 $resourceBundle = $bundle->getName(); | |
254 } | |
255 } | |
256 | |
257 if (count($files) > 0) { | |
258 return $first && $isResource ? $files[0] : $files; | |
259 } | |
260 | |
261 throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $name)); | |
262 } | |
263 | |
264 /** | |
265 * {@inheritdoc} | |
266 */ | |
267 public function getName() | |
268 { | |
269 if (null === $this->name) { | |
270 $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir)); | |
271 if (ctype_digit($this->name[0])) { | |
272 $this->name = '_'.$this->name; | |
273 } | |
274 } | |
275 | |
276 return $this->name; | |
277 } | |
278 | |
279 /** | |
280 * {@inheritdoc} | |
281 */ | |
282 public function getEnvironment() | |
283 { | |
284 return $this->environment; | |
285 } | |
286 | |
287 /** | |
288 * {@inheritdoc} | |
289 */ | |
290 public function isDebug() | |
291 { | |
292 return $this->debug; | |
293 } | |
294 | |
295 /** | |
296 * {@inheritdoc} | |
297 */ | |
298 public function getRootDir() | |
299 { | |
300 if (null === $this->rootDir) { | |
301 $r = new \ReflectionObject($this); | |
302 $this->rootDir = dirname($r->getFileName()); | |
303 } | |
304 | |
305 return $this->rootDir; | |
306 } | |
307 | |
308 /** | |
309 * {@inheritdoc} | |
310 */ | |
311 public function getContainer() | |
312 { | |
313 return $this->container; | |
314 } | |
315 | |
316 /** | |
317 * Loads the PHP class cache. | |
318 * | |
319 * This methods only registers the fact that you want to load the cache classes. | |
320 * The cache will actually only be loaded when the Kernel is booted. | |
321 * | |
322 * That optimization is mainly useful when using the HttpCache class in which | |
323 * case the class cache is not loaded if the Response is in the cache. | |
324 * | |
325 * @param string $name The cache name prefix | |
326 * @param string $extension File extension of the resulting file | |
327 */ | |
328 public function loadClassCache($name = 'classes', $extension = '.php') | |
329 { | |
330 $this->loadClassCache = array($name, $extension); | |
331 } | |
332 | |
333 /** | |
334 * @internal | |
335 */ | |
336 public function setClassCache(array $classes) | |
337 { | |
338 file_put_contents($this->getCacheDir().'/classes.map', sprintf('<?php return %s;', var_export($classes, true))); | |
339 } | |
340 | |
341 /** | |
342 * @internal | |
343 */ | |
344 public function setAnnotatedClassCache(array $annotatedClasses) | |
345 { | |
346 file_put_contents($this->getCacheDir().'/annotations.map', sprintf('<?php return %s;', var_export($annotatedClasses, true))); | |
347 } | |
348 | |
349 /** | |
350 * {@inheritdoc} | |
351 */ | |
352 public function getStartTime() | |
353 { | |
354 return $this->debug ? $this->startTime : -INF; | |
355 } | |
356 | |
357 /** | |
358 * {@inheritdoc} | |
359 */ | |
360 public function getCacheDir() | |
361 { | |
362 return $this->rootDir.'/cache/'.$this->environment; | |
363 } | |
364 | |
365 /** | |
366 * {@inheritdoc} | |
367 */ | |
368 public function getLogDir() | |
369 { | |
370 return $this->rootDir.'/logs'; | |
371 } | |
372 | |
373 /** | |
374 * {@inheritdoc} | |
375 */ | |
376 public function getCharset() | |
377 { | |
378 return 'UTF-8'; | |
379 } | |
380 | |
381 protected function doLoadClassCache($name, $extension) | |
382 { | |
383 if (!$this->booted && is_file($this->getCacheDir().'/classes.map')) { | |
384 ClassCollectionLoader::load(include($this->getCacheDir().'/classes.map'), $this->getCacheDir(), $name, $this->debug, false, $extension); | |
385 } | |
386 } | |
387 | |
388 /** | |
389 * Initializes the data structures related to the bundle management. | |
390 * | |
391 * - the bundles property maps a bundle name to the bundle instance, | |
392 * - the bundleMap property maps a bundle name to the bundle inheritance hierarchy (most derived bundle first). | |
393 * | |
394 * @throws \LogicException if two bundles share a common name | |
395 * @throws \LogicException if a bundle tries to extend a non-registered bundle | |
396 * @throws \LogicException if a bundle tries to extend itself | |
397 * @throws \LogicException if two bundles extend the same ancestor | |
398 */ | |
399 protected function initializeBundles() | |
400 { | |
401 // init bundles | |
402 $this->bundles = array(); | |
403 $topMostBundles = array(); | |
404 $directChildren = array(); | |
405 | |
406 foreach ($this->registerBundles() as $bundle) { | |
407 $name = $bundle->getName(); | |
408 if (isset($this->bundles[$name])) { | |
409 throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name)); | |
410 } | |
411 $this->bundles[$name] = $bundle; | |
412 | |
413 if ($parentName = $bundle->getParent()) { | |
414 if (isset($directChildren[$parentName])) { | |
415 throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $directChildren[$parentName])); | |
416 } | |
417 if ($parentName == $name) { | |
418 throw new \LogicException(sprintf('Bundle "%s" can not extend itself.', $name)); | |
419 } | |
420 $directChildren[$parentName] = $name; | |
421 } else { | |
422 $topMostBundles[$name] = $bundle; | |
423 } | |
424 } | |
425 | |
426 // look for orphans | |
427 if (!empty($directChildren) && count($diff = array_diff_key($directChildren, $this->bundles))) { | |
428 $diff = array_keys($diff); | |
429 | |
430 throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $directChildren[$diff[0]], $diff[0])); | |
431 } | |
432 | |
433 // inheritance | |
434 $this->bundleMap = array(); | |
435 foreach ($topMostBundles as $name => $bundle) { | |
436 $bundleMap = array($bundle); | |
437 $hierarchy = array($name); | |
438 | |
439 while (isset($directChildren[$name])) { | |
440 $name = $directChildren[$name]; | |
441 array_unshift($bundleMap, $this->bundles[$name]); | |
442 $hierarchy[] = $name; | |
443 } | |
444 | |
445 foreach ($hierarchy as $hierarchyBundle) { | |
446 $this->bundleMap[$hierarchyBundle] = $bundleMap; | |
447 array_pop($bundleMap); | |
448 } | |
449 } | |
450 } | |
451 | |
452 /** | |
453 * Gets the container class. | |
454 * | |
455 * @return string The container class | |
456 */ | |
457 protected function getContainerClass() | |
458 { | |
459 return $this->name.ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer'; | |
460 } | |
461 | |
462 /** | |
463 * Gets the container's base class. | |
464 * | |
465 * All names except Container must be fully qualified. | |
466 * | |
467 * @return string | |
468 */ | |
469 protected function getContainerBaseClass() | |
470 { | |
471 return 'Container'; | |
472 } | |
473 | |
474 /** | |
475 * Initializes the service container. | |
476 * | |
477 * The cached version of the service container is used when fresh, otherwise the | |
478 * container is built. | |
479 */ | |
480 protected function initializeContainer() | |
481 { | |
482 $class = $this->getContainerClass(); | |
483 $cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug); | |
484 $fresh = true; | |
485 if (!$cache->isFresh()) { | |
486 $container = $this->buildContainer(); | |
487 $container->compile(); | |
488 $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); | |
489 | |
490 $fresh = false; | |
491 } | |
492 | |
493 require_once $cache->getPath(); | |
494 | |
495 $this->container = new $class(); | |
496 $this->container->set('kernel', $this); | |
497 | |
498 if (!$fresh && $this->container->has('cache_warmer')) { | |
499 $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir')); | |
500 } | |
501 } | |
502 | |
503 /** | |
504 * Returns the kernel parameters. | |
505 * | |
506 * @return array An array of kernel parameters | |
507 */ | |
508 protected function getKernelParameters() | |
509 { | |
510 $bundles = array(); | |
511 $bundlesMetadata = array(); | |
512 | |
513 foreach ($this->bundles as $name => $bundle) { | |
514 $bundles[$name] = get_class($bundle); | |
515 $bundlesMetadata[$name] = array( | |
516 'parent' => $bundle->getParent(), | |
517 'path' => $bundle->getPath(), | |
518 'namespace' => $bundle->getNamespace(), | |
519 ); | |
520 } | |
521 | |
522 return array_merge( | |
523 array( | |
524 'kernel.root_dir' => realpath($this->rootDir) ?: $this->rootDir, | |
525 'kernel.environment' => $this->environment, | |
526 'kernel.debug' => $this->debug, | |
527 'kernel.name' => $this->name, | |
528 'kernel.cache_dir' => realpath($this->getCacheDir()) ?: $this->getCacheDir(), | |
529 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(), | |
530 'kernel.bundles' => $bundles, | |
531 'kernel.bundles_metadata' => $bundlesMetadata, | |
532 'kernel.charset' => $this->getCharset(), | |
533 'kernel.container_class' => $this->getContainerClass(), | |
534 ), | |
535 $this->getEnvParameters() | |
536 ); | |
537 } | |
538 | |
539 /** | |
540 * Gets the environment parameters. | |
541 * | |
542 * Only the parameters starting with "SYMFONY__" are considered. | |
543 * | |
544 * @return array An array of parameters | |
545 */ | |
546 protected function getEnvParameters() | |
547 { | |
548 $parameters = array(); | |
549 foreach ($_SERVER as $key => $value) { | |
550 if (0 === strpos($key, 'SYMFONY__')) { | |
551 $parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value; | |
552 } | |
553 } | |
554 | |
555 return $parameters; | |
556 } | |
557 | |
558 /** | |
559 * Builds the service container. | |
560 * | |
561 * @return ContainerBuilder The compiled service container | |
562 * | |
563 * @throws \RuntimeException | |
564 */ | |
565 protected function buildContainer() | |
566 { | |
567 foreach (array('cache' => $this->getCacheDir(), 'logs' => $this->getLogDir()) as $name => $dir) { | |
568 if (!is_dir($dir)) { | |
569 if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) { | |
570 throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir)); | |
571 } | |
572 } elseif (!is_writable($dir)) { | |
573 throw new \RuntimeException(sprintf("Unable to write in the %s directory (%s)\n", $name, $dir)); | |
574 } | |
575 } | |
576 | |
577 $container = $this->getContainerBuilder(); | |
578 $container->addObjectResource($this); | |
579 $this->prepareContainer($container); | |
580 | |
581 if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) { | |
582 $container->merge($cont); | |
583 } | |
584 | |
585 $container->addCompilerPass(new AddClassesToCachePass($this)); | |
586 $container->addResource(new EnvParametersResource('SYMFONY__')); | |
587 | |
588 return $container; | |
589 } | |
590 | |
591 /** | |
592 * Prepares the ContainerBuilder before it is compiled. | |
593 * | |
594 * @param ContainerBuilder $container A ContainerBuilder instance | |
595 */ | |
596 protected function prepareContainer(ContainerBuilder $container) | |
597 { | |
598 $extensions = array(); | |
599 foreach ($this->bundles as $bundle) { | |
600 if ($extension = $bundle->getContainerExtension()) { | |
601 $container->registerExtension($extension); | |
602 $extensions[] = $extension->getAlias(); | |
603 } | |
604 | |
605 if ($this->debug) { | |
606 $container->addObjectResource($bundle); | |
607 } | |
608 } | |
609 foreach ($this->bundles as $bundle) { | |
610 $bundle->build($container); | |
611 } | |
612 | |
613 // ensure these extensions are implicitly loaded | |
614 $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions)); | |
615 } | |
616 | |
617 /** | |
618 * Gets a new ContainerBuilder instance used to build the service container. | |
619 * | |
620 * @return ContainerBuilder | |
621 */ | |
622 protected function getContainerBuilder() | |
623 { | |
624 $container = new ContainerBuilder(); | |
625 $container->getParameterBag()->add($this->getKernelParameters()); | |
626 | |
627 if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator')) { | |
628 $container->setProxyInstantiator(new RuntimeInstantiator()); | |
629 } | |
630 | |
631 return $container; | |
632 } | |
633 | |
634 /** | |
635 * Dumps the service container to PHP code in the cache. | |
636 * | |
637 * @param ConfigCache $cache The config cache | |
638 * @param ContainerBuilder $container The service container | |
639 * @param string $class The name of the class to generate | |
640 * @param string $baseClass The name of the container's base class | |
641 */ | |
642 protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass) | |
643 { | |
644 // cache the container | |
645 $dumper = new PhpDumper($container); | |
646 | |
647 if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper')) { | |
648 $dumper->setProxyDumper(new ProxyDumper(md5($cache->getPath()))); | |
649 } | |
650 | |
651 $content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass, 'file' => $cache->getPath(), 'debug' => $this->debug)); | |
652 | |
653 $cache->write($content, $container->getResources()); | |
654 } | |
655 | |
656 /** | |
657 * Returns a loader for the container. | |
658 * | |
659 * @param ContainerInterface $container The service container | |
660 * | |
661 * @return DelegatingLoader The loader | |
662 */ | |
663 protected function getContainerLoader(ContainerInterface $container) | |
664 { | |
665 $locator = new FileLocator($this); | |
666 $resolver = new LoaderResolver(array( | |
667 new XmlFileLoader($container, $locator), | |
668 new YamlFileLoader($container, $locator), | |
669 new IniFileLoader($container, $locator), | |
670 new PhpFileLoader($container, $locator), | |
671 new DirectoryLoader($container, $locator), | |
672 new ClosureLoader($container), | |
673 )); | |
674 | |
675 return new DelegatingLoader($resolver); | |
676 } | |
677 | |
678 /** | |
679 * Removes comments from a PHP source string. | |
680 * | |
681 * We don't use the PHP php_strip_whitespace() function | |
682 * as we want the content to be readable and well-formatted. | |
683 * | |
684 * @param string $source A PHP string | |
685 * | |
686 * @return string The PHP string with the comments removed | |
687 */ | |
688 public static function stripComments($source) | |
689 { | |
690 if (!function_exists('token_get_all')) { | |
691 return $source; | |
692 } | |
693 | |
694 $rawChunk = ''; | |
695 $output = ''; | |
696 $tokens = token_get_all($source); | |
697 $ignoreSpace = false; | |
698 for ($i = 0; isset($tokens[$i]); ++$i) { | |
699 $token = $tokens[$i]; | |
700 if (!isset($token[1]) || 'b"' === $token) { | |
701 $rawChunk .= $token; | |
702 } elseif (T_START_HEREDOC === $token[0]) { | |
703 $output .= $rawChunk.$token[1]; | |
704 do { | |
705 $token = $tokens[++$i]; | |
706 $output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token; | |
707 } while ($token[0] !== T_END_HEREDOC); | |
708 $rawChunk = ''; | |
709 } elseif (T_WHITESPACE === $token[0]) { | |
710 if ($ignoreSpace) { | |
711 $ignoreSpace = false; | |
712 | |
713 continue; | |
714 } | |
715 | |
716 // replace multiple new lines with a single newline | |
717 $rawChunk .= preg_replace(array('/\n{2,}/S'), "\n", $token[1]); | |
718 } elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) { | |
719 $ignoreSpace = true; | |
720 } else { | |
721 $rawChunk .= $token[1]; | |
722 | |
723 // The PHP-open tag already has a new-line | |
724 if (T_OPEN_TAG === $token[0]) { | |
725 $ignoreSpace = true; | |
726 } | |
727 } | |
728 } | |
729 | |
730 $output .= $rawChunk; | |
731 | |
732 if (PHP_VERSION_ID >= 70000) { | |
733 // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 | |
734 unset($tokens, $rawChunk); | |
735 gc_mem_caches(); | |
736 } | |
737 | |
738 return $output; | |
739 } | |
740 | |
741 public function serialize() | |
742 { | |
743 return serialize(array($this->environment, $this->debug)); | |
744 } | |
745 | |
746 public function unserialize($data) | |
747 { | |
748 list($environment, $debug) = unserialize($data); | |
749 | |
750 $this->__construct($environment, $debug); | |
751 } | |
752 } |