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