annotate vendor/symfony/debug/DebugClassLoader.php @ 2:92f882872392

Trusted hosts, + remove migration modules
author Chris Cannam
date Tue, 05 Dec 2017 09:26:43 +0000
parents 4c8ae668cc8c
children 7a779792577d
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /*
Chris@0 4 * This file is part of the Symfony package.
Chris@0 5 *
Chris@0 6 * (c) Fabien Potencier <fabien@symfony.com>
Chris@0 7 *
Chris@0 8 * For the full copyright and license information, please view the LICENSE
Chris@0 9 * file that was distributed with this source code.
Chris@0 10 */
Chris@0 11
Chris@0 12 namespace Symfony\Component\Debug;
Chris@0 13
Chris@0 14 /**
Chris@0 15 * Autoloader checking if the class is really defined in the file found.
Chris@0 16 *
Chris@0 17 * The ClassLoader will wrap all registered autoloaders
Chris@0 18 * and will throw an exception if a file is found but does
Chris@0 19 * not declare the class.
Chris@0 20 *
Chris@0 21 * @author Fabien Potencier <fabien@symfony.com>
Chris@0 22 * @author Christophe Coevoet <stof@notk.org>
Chris@0 23 * @author Nicolas Grekas <p@tchwork.com>
Chris@0 24 */
Chris@0 25 class DebugClassLoader
Chris@0 26 {
Chris@0 27 private $classLoader;
Chris@0 28 private $isFinder;
Chris@0 29 private static $caseCheck;
Chris@0 30 private static $deprecated = array();
Chris@0 31 private static $php7Reserved = array('int', 'float', 'bool', 'string', 'true', 'false', 'null');
Chris@0 32 private static $darwinCache = array('/' => array('/', array()));
Chris@0 33
Chris@0 34 /**
Chris@0 35 * Constructor.
Chris@0 36 *
Chris@0 37 * @param callable $classLoader A class loader
Chris@0 38 */
Chris@0 39 public function __construct(callable $classLoader)
Chris@0 40 {
Chris@0 41 $this->classLoader = $classLoader;
Chris@0 42 $this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile');
Chris@0 43
Chris@0 44 if (!isset(self::$caseCheck)) {
Chris@0 45 $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), DIRECTORY_SEPARATOR);
Chris@0 46 $i = strrpos($file, DIRECTORY_SEPARATOR);
Chris@0 47 $dir = substr($file, 0, 1 + $i);
Chris@0 48 $file = substr($file, 1 + $i);
Chris@0 49 $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file);
Chris@0 50 $test = realpath($dir.$test);
Chris@0 51
Chris@0 52 if (false === $test || false === $i) {
Chris@0 53 // filesystem is case sensitive
Chris@0 54 self::$caseCheck = 0;
Chris@0 55 } elseif (substr($test, -strlen($file)) === $file) {
Chris@0 56 // filesystem is case insensitive and realpath() normalizes the case of characters
Chris@0 57 self::$caseCheck = 1;
Chris@0 58 } elseif (false !== stripos(PHP_OS, 'darwin')) {
Chris@0 59 // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters
Chris@0 60 self::$caseCheck = 2;
Chris@0 61 } else {
Chris@0 62 // filesystem case checks failed, fallback to disabling them
Chris@0 63 self::$caseCheck = 0;
Chris@0 64 }
Chris@0 65 }
Chris@0 66 }
Chris@0 67
Chris@0 68 /**
Chris@0 69 * Gets the wrapped class loader.
Chris@0 70 *
Chris@0 71 * @return callable The wrapped class loader
Chris@0 72 */
Chris@0 73 public function getClassLoader()
Chris@0 74 {
Chris@0 75 return $this->classLoader;
Chris@0 76 }
Chris@0 77
Chris@0 78 /**
Chris@0 79 * Wraps all autoloaders.
Chris@0 80 */
Chris@0 81 public static function enable()
Chris@0 82 {
Chris@0 83 // Ensures we don't hit https://bugs.php.net/42098
Chris@0 84 class_exists('Symfony\Component\Debug\ErrorHandler');
Chris@0 85 class_exists('Psr\Log\LogLevel');
Chris@0 86
Chris@0 87 if (!is_array($functions = spl_autoload_functions())) {
Chris@0 88 return;
Chris@0 89 }
Chris@0 90
Chris@0 91 foreach ($functions as $function) {
Chris@0 92 spl_autoload_unregister($function);
Chris@0 93 }
Chris@0 94
Chris@0 95 foreach ($functions as $function) {
Chris@0 96 if (!is_array($function) || !$function[0] instanceof self) {
Chris@0 97 $function = array(new static($function), 'loadClass');
Chris@0 98 }
Chris@0 99
Chris@0 100 spl_autoload_register($function);
Chris@0 101 }
Chris@0 102 }
Chris@0 103
Chris@0 104 /**
Chris@0 105 * Disables the wrapping.
Chris@0 106 */
Chris@0 107 public static function disable()
Chris@0 108 {
Chris@0 109 if (!is_array($functions = spl_autoload_functions())) {
Chris@0 110 return;
Chris@0 111 }
Chris@0 112
Chris@0 113 foreach ($functions as $function) {
Chris@0 114 spl_autoload_unregister($function);
Chris@0 115 }
Chris@0 116
Chris@0 117 foreach ($functions as $function) {
Chris@0 118 if (is_array($function) && $function[0] instanceof self) {
Chris@0 119 $function = $function[0]->getClassLoader();
Chris@0 120 }
Chris@0 121
Chris@0 122 spl_autoload_register($function);
Chris@0 123 }
Chris@0 124 }
Chris@0 125
Chris@0 126 /**
Chris@0 127 * Loads the given class or interface.
Chris@0 128 *
Chris@0 129 * @param string $class The name of the class
Chris@0 130 *
Chris@0 131 * @return bool|null True, if loaded
Chris@0 132 *
Chris@0 133 * @throws \RuntimeException
Chris@0 134 */
Chris@0 135 public function loadClass($class)
Chris@0 136 {
Chris@0 137 ErrorHandler::stackErrors();
Chris@0 138
Chris@0 139 try {
Chris@0 140 if ($this->isFinder) {
Chris@0 141 if ($file = $this->classLoader[0]->findFile($class)) {
Chris@0 142 require_once $file;
Chris@0 143 }
Chris@0 144 } else {
Chris@0 145 call_user_func($this->classLoader, $class);
Chris@0 146 $file = false;
Chris@0 147 }
Chris@0 148 } finally {
Chris@0 149 ErrorHandler::unstackErrors();
Chris@0 150 }
Chris@0 151
Chris@0 152 $exists = class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
Chris@0 153
Chris@0 154 if ($class && '\\' === $class[0]) {
Chris@0 155 $class = substr($class, 1);
Chris@0 156 }
Chris@0 157
Chris@0 158 if ($exists) {
Chris@0 159 $refl = new \ReflectionClass($class);
Chris@0 160 $name = $refl->getName();
Chris@0 161
Chris@0 162 if ($name !== $class && 0 === strcasecmp($name, $class)) {
Chris@0 163 throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: %s vs %s', $class, $name));
Chris@0 164 }
Chris@0 165
Chris@0 166 if (in_array(strtolower($refl->getShortName()), self::$php7Reserved)) {
Chris@0 167 @trigger_error(sprintf('%s uses a reserved class name (%s) that will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED);
Chris@0 168 } elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) {
Chris@0 169 self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]);
Chris@0 170 } else {
Chris@0 171 if (2 > $len = 1 + (strpos($name, '\\', 1 + strpos($name, '\\')) ?: strpos($name, '_'))) {
Chris@0 172 $len = 0;
Chris@0 173 $ns = '';
Chris@0 174 } else {
Chris@0 175 switch ($ns = substr($name, 0, $len)) {
Chris@0 176 case 'Symfony\Bridge\\':
Chris@0 177 case 'Symfony\Bundle\\':
Chris@0 178 case 'Symfony\Component\\':
Chris@0 179 $ns = 'Symfony\\';
Chris@0 180 $len = strlen($ns);
Chris@0 181 break;
Chris@0 182 }
Chris@0 183 }
Chris@0 184 $parent = get_parent_class($class);
Chris@0 185
Chris@0 186 if (!$parent || strncmp($ns, $parent, $len)) {
Chris@0 187 if ($parent && isset(self::$deprecated[$parent]) && strncmp($ns, $parent, $len)) {
Chris@0 188 @trigger_error(sprintf('The %s class extends %s that is deprecated %s', $name, $parent, self::$deprecated[$parent]), E_USER_DEPRECATED);
Chris@0 189 }
Chris@0 190
Chris@0 191 $parentInterfaces = array();
Chris@0 192 $deprecatedInterfaces = array();
Chris@0 193 if ($parent) {
Chris@0 194 foreach (class_implements($parent) as $interface) {
Chris@0 195 $parentInterfaces[$interface] = 1;
Chris@0 196 }
Chris@0 197 }
Chris@0 198
Chris@0 199 foreach ($refl->getInterfaceNames() as $interface) {
Chris@0 200 if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len)) {
Chris@0 201 $deprecatedInterfaces[] = $interface;
Chris@0 202 }
Chris@0 203 foreach (class_implements($interface) as $interface) {
Chris@0 204 $parentInterfaces[$interface] = 1;
Chris@0 205 }
Chris@0 206 }
Chris@0 207
Chris@0 208 foreach ($deprecatedInterfaces as $interface) {
Chris@0 209 if (!isset($parentInterfaces[$interface])) {
Chris@0 210 @trigger_error(sprintf('The %s %s %s that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED);
Chris@0 211 }
Chris@0 212 }
Chris@0 213 }
Chris@0 214 }
Chris@0 215 }
Chris@0 216
Chris@0 217 if ($file) {
Chris@0 218 if (!$exists) {
Chris@0 219 if (false !== strpos($class, '/')) {
Chris@0 220 throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class));
Chris@0 221 }
Chris@0 222
Chris@0 223 throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file));
Chris@0 224 }
Chris@0 225 if (self::$caseCheck) {
Chris@0 226 $real = explode('\\', $class.strrchr($file, '.'));
Chris@0 227 $tail = explode(DIRECTORY_SEPARATOR, str_replace('/', DIRECTORY_SEPARATOR, $file));
Chris@0 228
Chris@0 229 $i = count($tail) - 1;
Chris@0 230 $j = count($real) - 1;
Chris@0 231
Chris@0 232 while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) {
Chris@0 233 --$i;
Chris@0 234 --$j;
Chris@0 235 }
Chris@0 236
Chris@0 237 array_splice($tail, 0, $i + 1);
Chris@0 238 }
Chris@0 239 if (self::$caseCheck && $tail) {
Chris@0 240 $tail = DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $tail);
Chris@0 241 $tailLen = strlen($tail);
Chris@0 242 $real = $refl->getFileName();
Chris@0 243
Chris@0 244 if (2 === self::$caseCheck) {
Chris@0 245 // realpath() on MacOSX doesn't normalize the case of characters
Chris@0 246
Chris@0 247 $i = 1 + strrpos($real, '/');
Chris@0 248 $file = substr($real, $i);
Chris@0 249 $real = substr($real, 0, $i);
Chris@0 250
Chris@0 251 if (isset(self::$darwinCache[$real])) {
Chris@0 252 $kDir = $real;
Chris@0 253 } else {
Chris@0 254 $kDir = strtolower($real);
Chris@0 255
Chris@0 256 if (isset(self::$darwinCache[$kDir])) {
Chris@0 257 $real = self::$darwinCache[$kDir][0];
Chris@0 258 } else {
Chris@0 259 $dir = getcwd();
Chris@0 260 chdir($real);
Chris@0 261 $real = getcwd().'/';
Chris@0 262 chdir($dir);
Chris@0 263
Chris@0 264 $dir = $real;
Chris@0 265 $k = $kDir;
Chris@0 266 $i = strlen($dir) - 1;
Chris@0 267 while (!isset(self::$darwinCache[$k])) {
Chris@0 268 self::$darwinCache[$k] = array($dir, array());
Chris@0 269 self::$darwinCache[$dir] = &self::$darwinCache[$k];
Chris@0 270
Chris@0 271 while ('/' !== $dir[--$i]) {
Chris@0 272 }
Chris@0 273 $k = substr($k, 0, ++$i);
Chris@0 274 $dir = substr($dir, 0, $i--);
Chris@0 275 }
Chris@0 276 }
Chris@0 277 }
Chris@0 278
Chris@0 279 $dirFiles = self::$darwinCache[$kDir][1];
Chris@0 280
Chris@0 281 if (isset($dirFiles[$file])) {
Chris@0 282 $kFile = $file;
Chris@0 283 } else {
Chris@0 284 $kFile = strtolower($file);
Chris@0 285
Chris@0 286 if (!isset($dirFiles[$kFile])) {
Chris@0 287 foreach (scandir($real, 2) as $f) {
Chris@0 288 if ('.' !== $f[0]) {
Chris@0 289 $dirFiles[$f] = $f;
Chris@0 290 if ($f === $file) {
Chris@0 291 $kFile = $k = $file;
Chris@0 292 } elseif ($f !== $k = strtolower($f)) {
Chris@0 293 $dirFiles[$k] = $f;
Chris@0 294 }
Chris@0 295 }
Chris@0 296 }
Chris@0 297 self::$darwinCache[$kDir][1] = $dirFiles;
Chris@0 298 }
Chris@0 299 }
Chris@0 300
Chris@0 301 $real .= $dirFiles[$kFile];
Chris@0 302 }
Chris@0 303
Chris@0 304 if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
Chris@0 305 && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
Chris@0 306 ) {
Chris@0 307 throw new \RuntimeException(sprintf('Case mismatch between class and real file names: %s vs %s in %s', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)));
Chris@0 308 }
Chris@0 309 }
Chris@0 310
Chris@0 311 return true;
Chris@0 312 }
Chris@0 313 }
Chris@0 314 }