Chris@4: Chris@4: * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) Chris@4: * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence Chris@4: */ Chris@4: Chris@4: namespace PHP_CodeSniffer; Chris@4: Chris@4: if (class_exists('PHP_CodeSniffer\Autoload', false) === false) { Chris@4: class Autoload Chris@4: { Chris@4: Chris@4: /** Chris@4: * The composer autoloader. Chris@4: * Chris@5: * @var \Composer\Autoload\ClassLoader Chris@4: */ Chris@4: private static $composerAutoloader = null; Chris@4: Chris@4: /** Chris@4: * A mapping of file names to class names. Chris@4: * Chris@4: * @var array Chris@4: */ Chris@4: private static $loadedClasses = []; Chris@4: Chris@4: /** Chris@4: * A mapping of class names to file names. Chris@4: * Chris@4: * @var array Chris@4: */ Chris@4: private static $loadedFiles = []; Chris@4: Chris@4: /** Chris@4: * A list of additional directories to search during autoloading. Chris@4: * Chris@4: * This is typically a list of coding standard directories. Chris@4: * Chris@4: * @var string[] Chris@4: */ Chris@4: private static $searchPaths = []; Chris@4: Chris@4: Chris@4: /** Chris@4: * Loads a class. Chris@4: * Chris@4: * This method only loads classes that exist in the PHP_CodeSniffer namespace. Chris@4: * All other classes are ignored and loaded by subsequent autoloaders. Chris@4: * Chris@4: * @param string $class The name of the class to load. Chris@4: * Chris@4: * @return bool Chris@4: */ Chris@4: public static function load($class) Chris@4: { Chris@4: // Include the composer autoloader if there is one, but re-register it Chris@4: // so this autoloader runs before the composer one as we need to include Chris@4: // all files so we can figure out what the class/interface/trait name is. Chris@4: if (self::$composerAutoloader === null) { Chris@4: // Make sure we don't try to load any of Composer's classes Chris@4: // while the autoloader is being setup. Chris@4: if (strpos($class, 'Composer\\') === 0) { Chris@4: return; Chris@4: } Chris@4: Chris@4: if (strpos(__DIR__, 'phar://') !== 0 Chris@4: && file_exists(__DIR__.'/../../autoload.php') === true Chris@4: ) { Chris@4: self::$composerAutoloader = include __DIR__.'/../../autoload.php'; Chris@4: if (self::$composerAutoloader instanceof \Composer\Autoload\ClassLoader) { Chris@4: self::$composerAutoloader->unregister(); Chris@4: self::$composerAutoloader->register(); Chris@4: } else { Chris@4: // Something went wrong, so keep going without the autoloader Chris@4: // although namespaced sniffs might error. Chris@4: self::$composerAutoloader = false; Chris@4: } Chris@4: } else { Chris@4: self::$composerAutoloader = false; Chris@4: } Chris@4: }//end if Chris@4: Chris@4: $ds = DIRECTORY_SEPARATOR; Chris@4: $path = false; Chris@4: Chris@4: if (substr($class, 0, 16) === 'PHP_CodeSniffer\\') { Chris@4: if (substr($class, 0, 22) === 'PHP_CodeSniffer\Tests\\') { Chris@4: $isInstalled = !is_dir(__DIR__.$ds.'tests'); Chris@4: if ($isInstalled === false) { Chris@4: $path = __DIR__.$ds.'tests'; Chris@4: } else { Chris@4: $path = '@test_dir@'.$ds.'PHP_CodeSniffer'.$ds.'CodeSniffer'; Chris@4: } Chris@4: Chris@4: $path .= $ds.substr(str_replace('\\', $ds, $class), 22).'.php'; Chris@4: } else { Chris@4: $path = __DIR__.$ds.'src'.$ds.substr(str_replace('\\', $ds, $class), 16).'.php'; Chris@4: } Chris@4: } Chris@4: Chris@4: // See if the composer autoloader knows where the class is. Chris@4: if ($path === false && self::$composerAutoloader !== false) { Chris@4: $path = self::$composerAutoloader->findFile($class); Chris@4: } Chris@4: Chris@4: // See if the class is inside one of our alternate search paths. Chris@4: if ($path === false) { Chris@4: foreach (self::$searchPaths as $searchPath => $nsPrefix) { Chris@4: $className = $class; Chris@4: if ($nsPrefix !== '' && substr($class, 0, strlen($nsPrefix)) === $nsPrefix) { Chris@4: $className = substr($class, (strlen($nsPrefix) + 1)); Chris@4: } Chris@4: Chris@4: $path = $searchPath.$ds.str_replace('\\', $ds, $className).'.php'; Chris@4: if (is_file($path) === true) { Chris@4: break; Chris@4: } Chris@4: Chris@4: $path = false; Chris@4: } Chris@4: } Chris@4: Chris@4: if ($path !== false && is_file($path) === true) { Chris@4: self::loadFile($path); Chris@4: return true; Chris@4: } Chris@4: Chris@4: return false; Chris@4: Chris@4: }//end load() Chris@4: Chris@4: Chris@4: /** Chris@4: * Includes a file and tracks what class or interface was loaded as a result. Chris@4: * Chris@4: * @param string $path The path of the file to load. Chris@4: * Chris@4: * @return string The fully qualified name of the class in the loaded file. Chris@4: */ Chris@4: public static function loadFile($path) Chris@4: { Chris@4: if (strpos(__DIR__, 'phar://') !== 0) { Chris@4: $path = realpath($path); Chris@4: if ($path === false) { Chris@4: return false; Chris@4: } Chris@4: } Chris@4: Chris@4: if (isset(self::$loadedClasses[$path]) === true) { Chris@4: return self::$loadedClasses[$path]; Chris@4: } Chris@4: Chris@4: $classes = get_declared_classes(); Chris@4: $interfaces = get_declared_interfaces(); Chris@4: $traits = get_declared_traits(); Chris@4: Chris@4: include $path; Chris@4: Chris@4: $className = null; Chris@4: $newClasses = array_reverse(array_diff(get_declared_classes(), $classes)); Chris@4: foreach ($newClasses as $name) { Chris@4: if (isset(self::$loadedFiles[$name]) === false) { Chris@4: $className = $name; Chris@4: break; Chris@4: } Chris@4: } Chris@4: Chris@4: if ($className === null) { Chris@4: $newClasses = array_reverse(array_diff(get_declared_interfaces(), $interfaces)); Chris@4: foreach ($newClasses as $name) { Chris@4: if (isset(self::$loadedFiles[$name]) === false) { Chris@4: $className = $name; Chris@4: break; Chris@4: } Chris@4: } Chris@4: } Chris@4: Chris@4: if ($className === null) { Chris@4: $newClasses = array_reverse(array_diff(get_declared_traits(), $traits)); Chris@4: foreach ($newClasses as $name) { Chris@4: if (isset(self::$loadedFiles[$name]) === false) { Chris@4: $className = $name; Chris@4: break; Chris@4: } Chris@4: } Chris@4: } Chris@4: Chris@4: self::$loadedClasses[$path] = $className; Chris@4: self::$loadedFiles[$className] = $path; Chris@4: return self::$loadedClasses[$path]; Chris@4: Chris@4: }//end loadFile() Chris@4: Chris@4: Chris@4: /** Chris@4: * Adds a directory to search during autoloading. Chris@4: * Chris@4: * @param string $path The path to the directory to search. Chris@4: * @param string $nsPrefix The namespace prefix used by files under this path. Chris@4: * Chris@4: * @return void Chris@4: */ Chris@4: public static function addSearchPath($path, $nsPrefix='') Chris@4: { Chris@4: self::$searchPaths[$path] = rtrim(trim((string) $nsPrefix), '\\'); Chris@4: Chris@4: }//end addSearchPath() Chris@4: Chris@4: Chris@4: /** Chris@4: * Retrieve the namespaces and paths registered by external standards. Chris@4: * Chris@4: * @return array Chris@4: */ Chris@4: public static function getSearchPaths() Chris@4: { Chris@4: return self::$searchPaths; Chris@4: Chris@4: }//end getSearchPaths() Chris@4: Chris@4: Chris@4: /** Chris@4: * Gets the class name for the given file path. Chris@4: * Chris@4: * @param string $path The name of the file. Chris@4: * Chris@4: * @throws \Exception If the file path has not been loaded. Chris@4: * @return string Chris@4: */ Chris@4: public static function getLoadedClassName($path) Chris@4: { Chris@4: if (isset(self::$loadedClasses[$path]) === false) { Chris@4: throw new \Exception("Cannot get class name for $path; file has not been included"); Chris@4: } Chris@4: Chris@4: return self::$loadedClasses[$path]; Chris@4: Chris@4: }//end getLoadedClassName() Chris@4: Chris@4: Chris@4: /** Chris@4: * Gets the file path for the given class name. Chris@4: * Chris@4: * @param string $class The name of the class. Chris@4: * Chris@4: * @throws \Exception If the class name has not been loaded Chris@4: * @return string Chris@4: */ Chris@4: public static function getLoadedFileName($class) Chris@4: { Chris@4: if (isset(self::$loadedFiles[$class]) === false) { Chris@4: throw new \Exception("Cannot get file name for $class; class has not been included"); Chris@4: } Chris@4: Chris@4: return self::$loadedFiles[$class]; Chris@4: Chris@4: }//end getLoadedFileName() Chris@4: Chris@4: Chris@4: /** Chris@4: * Gets the mapping of file names to class names. Chris@4: * Chris@4: * @return array Chris@4: */ Chris@4: public static function getLoadedClasses() Chris@4: { Chris@4: return self::$loadedClasses; Chris@4: Chris@4: }//end getLoadedClasses() Chris@4: Chris@4: Chris@4: /** Chris@4: * Gets the mapping of class names to file names. Chris@4: * Chris@4: * @return array Chris@4: */ Chris@4: public static function getLoadedFiles() Chris@4: { Chris@4: return self::$loadedFiles; Chris@4: Chris@4: }//end getLoadedFiles() Chris@4: Chris@4: Chris@4: }//end class Chris@4: Chris@4: // Register the autoloader before any existing autoloaders to ensure Chris@4: // it gets a chance to hear about every autoload request, and record Chris@4: // the file and class name for it. Chris@4: spl_autoload_register(__NAMESPACE__.'\Autoload::load', true, true); Chris@4: }//end if