vendor/composer/ClassLoader.php line 444

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Composer.
  4.  *
  5.  * (c) Nils Adermann <naderman@naderman.de>
  6.  *     Jordi Boggiano <j.boggiano@seld.be>
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Composer\Autoload;
  12. /**
  13.  * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
  14.  *
  15.  *     $loader = new \Composer\Autoload\ClassLoader();
  16.  *
  17.  *     // register classes with namespaces
  18.  *     $loader->add('Symfony\Component', __DIR__.'/component');
  19.  *     $loader->add('Symfony',           __DIR__.'/framework');
  20.  *
  21.  *     // activate the autoloader
  22.  *     $loader->register();
  23.  *
  24.  *     // to enable searching the include path (eg. for PEAR packages)
  25.  *     $loader->setUseIncludePath(true);
  26.  *
  27.  * In this example, if you try to use a class in the Symfony\Component
  28.  * namespace or one of its children (Symfony\Component\Console for instance),
  29.  * the autoloader will first look for the class under the component/
  30.  * directory, and it will then fallback to the framework/ directory if not
  31.  * found before giving up.
  32.  *
  33.  * This class is loosely based on the Symfony UniversalClassLoader.
  34.  *
  35.  * @author Fabien Potencier <fabien@symfony.com>
  36.  * @author Jordi Boggiano <j.boggiano@seld.be>
  37.  * @see    http://www.php-fig.org/psr/psr-0/
  38.  * @see    http://www.php-fig.org/psr/psr-4/
  39.  */
  40. class ClassLoader
  41. {
  42.     // PSR-4
  43.     private $prefixLengthsPsr4 = array();
  44.     private $prefixDirsPsr4 = array();
  45.     private $fallbackDirsPsr4 = array();
  46.     // PSR-0
  47.     private $prefixesPsr0 = array();
  48.     private $fallbackDirsPsr0 = array();
  49.     private $useIncludePath false;
  50.     private $classMap = array();
  51.     private $classMapAuthoritative false;
  52.     private $missingClasses = array();
  53.     private $apcuPrefix;
  54.     public function getPrefixes()
  55.     {
  56.         if (!empty($this->prefixesPsr0)) {
  57.             return call_user_func_array('array_merge'$this->prefixesPsr0);
  58.         }
  59.         return array();
  60.     }
  61.     public function getPrefixesPsr4()
  62.     {
  63.         return $this->prefixDirsPsr4;
  64.     }
  65.     public function getFallbackDirs()
  66.     {
  67.         return $this->fallbackDirsPsr0;
  68.     }
  69.     public function getFallbackDirsPsr4()
  70.     {
  71.         return $this->fallbackDirsPsr4;
  72.     }
  73.     public function getClassMap()
  74.     {
  75.         return $this->classMap;
  76.     }
  77.     /**
  78.      * @param array $classMap Class to filename map
  79.      */
  80.     public function addClassMap(array $classMap)
  81.     {
  82.         if ($this->classMap) {
  83.             $this->classMap array_merge($this->classMap$classMap);
  84.         } else {
  85.             $this->classMap $classMap;
  86.         }
  87.     }
  88.     /**
  89.      * Registers a set of PSR-0 directories for a given prefix, either
  90.      * appending or prepending to the ones previously set for this prefix.
  91.      *
  92.      * @param string       $prefix  The prefix
  93.      * @param array|string $paths   The PSR-0 root directories
  94.      * @param bool         $prepend Whether to prepend the directories
  95.      */
  96.     public function add($prefix$paths$prepend false)
  97.     {
  98.         if (!$prefix) {
  99.             if ($prepend) {
  100.                 $this->fallbackDirsPsr0 array_merge(
  101.                     (array) $paths,
  102.                     $this->fallbackDirsPsr0
  103.                 );
  104.             } else {
  105.                 $this->fallbackDirsPsr0 array_merge(
  106.                     $this->fallbackDirsPsr0,
  107.                     (array) $paths
  108.                 );
  109.             }
  110.             return;
  111.         }
  112.         $first $prefix[0];
  113.         if (!isset($this->prefixesPsr0[$first][$prefix])) {
  114.             $this->prefixesPsr0[$first][$prefix] = (array) $paths;
  115.             return;
  116.         }
  117.         if ($prepend) {
  118.             $this->prefixesPsr0[$first][$prefix] = array_merge(
  119.                 (array) $paths,
  120.                 $this->prefixesPsr0[$first][$prefix]
  121.             );
  122.         } else {
  123.             $this->prefixesPsr0[$first][$prefix] = array_merge(
  124.                 $this->prefixesPsr0[$first][$prefix],
  125.                 (array) $paths
  126.             );
  127.         }
  128.     }
  129.     /**
  130.      * Registers a set of PSR-4 directories for a given namespace, either
  131.      * appending or prepending to the ones previously set for this namespace.
  132.      *
  133.      * @param string       $prefix  The prefix/namespace, with trailing '\\'
  134.      * @param array|string $paths   The PSR-4 base directories
  135.      * @param bool         $prepend Whether to prepend the directories
  136.      *
  137.      * @throws \InvalidArgumentException
  138.      */
  139.     public function addPsr4($prefix$paths$prepend false)
  140.     {
  141.         if (!$prefix) {
  142.             // Register directories for the root namespace.
  143.             if ($prepend) {
  144.                 $this->fallbackDirsPsr4 array_merge(
  145.                     (array) $paths,
  146.                     $this->fallbackDirsPsr4
  147.                 );
  148.             } else {
  149.                 $this->fallbackDirsPsr4 array_merge(
  150.                     $this->fallbackDirsPsr4,
  151.                     (array) $paths
  152.                 );
  153.             }
  154.         } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
  155.             // Register directories for a new namespace.
  156.             $length strlen($prefix);
  157.             if ('\\' !== $prefix[$length 1]) {
  158.                 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
  159.             }
  160.             $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
  161.             $this->prefixDirsPsr4[$prefix] = (array) $paths;
  162.         } elseif ($prepend) {
  163.             // Prepend directories for an already registered namespace.
  164.             $this->prefixDirsPsr4[$prefix] = array_merge(
  165.                 (array) $paths,
  166.                 $this->prefixDirsPsr4[$prefix]
  167.             );
  168.         } else {
  169.             // Append directories for an already registered namespace.
  170.             $this->prefixDirsPsr4[$prefix] = array_merge(
  171.                 $this->prefixDirsPsr4[$prefix],
  172.                 (array) $paths
  173.             );
  174.         }
  175.     }
  176.     /**
  177.      * Registers a set of PSR-0 directories for a given prefix,
  178.      * replacing any others previously set for this prefix.
  179.      *
  180.      * @param string       $prefix The prefix
  181.      * @param array|string $paths  The PSR-0 base directories
  182.      */
  183.     public function set($prefix$paths)
  184.     {
  185.         if (!$prefix) {
  186.             $this->fallbackDirsPsr0 = (array) $paths;
  187.         } else {
  188.             $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
  189.         }
  190.     }
  191.     /**
  192.      * Registers a set of PSR-4 directories for a given namespace,
  193.      * replacing any others previously set for this namespace.
  194.      *
  195.      * @param string       $prefix The prefix/namespace, with trailing '\\'
  196.      * @param array|string $paths  The PSR-4 base directories
  197.      *
  198.      * @throws \InvalidArgumentException
  199.      */
  200.     public function setPsr4($prefix$paths)
  201.     {
  202.         if (!$prefix) {
  203.             $this->fallbackDirsPsr4 = (array) $paths;
  204.         } else {
  205.             $length strlen($prefix);
  206.             if ('\\' !== $prefix[$length 1]) {
  207.                 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
  208.             }
  209.             $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
  210.             $this->prefixDirsPsr4[$prefix] = (array) $paths;
  211.         }
  212.     }
  213.     /**
  214.      * Turns on searching the include path for class files.
  215.      *
  216.      * @param bool $useIncludePath
  217.      */
  218.     public function setUseIncludePath($useIncludePath)
  219.     {
  220.         $this->useIncludePath $useIncludePath;
  221.     }
  222.     /**
  223.      * Can be used to check if the autoloader uses the include path to check
  224.      * for classes.
  225.      *
  226.      * @return bool
  227.      */
  228.     public function getUseIncludePath()
  229.     {
  230.         return $this->useIncludePath;
  231.     }
  232.     /**
  233.      * Turns off searching the prefix and fallback directories for classes
  234.      * that have not been registered with the class map.
  235.      *
  236.      * @param bool $classMapAuthoritative
  237.      */
  238.     public function setClassMapAuthoritative($classMapAuthoritative)
  239.     {
  240.         $this->classMapAuthoritative $classMapAuthoritative;
  241.     }
  242.     /**
  243.      * Should class lookup fail if not found in the current class map?
  244.      *
  245.      * @return bool
  246.      */
  247.     public function isClassMapAuthoritative()
  248.     {
  249.         return $this->classMapAuthoritative;
  250.     }
  251.     /**
  252.      * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
  253.      *
  254.      * @param string|null $apcuPrefix
  255.      */
  256.     public function setApcuPrefix($apcuPrefix)
  257.     {
  258.         $this->apcuPrefix function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix null;
  259.     }
  260.     /**
  261.      * The APCu prefix in use, or null if APCu caching is not enabled.
  262.      *
  263.      * @return string|null
  264.      */
  265.     public function getApcuPrefix()
  266.     {
  267.         return $this->apcuPrefix;
  268.     }
  269.     /**
  270.      * Registers this instance as an autoloader.
  271.      *
  272.      * @param bool $prepend Whether to prepend the autoloader or not
  273.      */
  274.     public function register($prepend false)
  275.     {
  276.         spl_autoload_register(array($this'loadClass'), true$prepend);
  277.     }
  278.     /**
  279.      * Unregisters this instance as an autoloader.
  280.      */
  281.     public function unregister()
  282.     {
  283.         spl_autoload_unregister(array($this'loadClass'));
  284.     }
  285.     /**
  286.      * Loads the given class or interface.
  287.      *
  288.      * @param  string    $class The name of the class
  289.      * @return bool|null True if loaded, null otherwise
  290.      */
  291.     public function loadClass($class)
  292.     {
  293.         if ($file $this->findFile($class)) {
  294.             includeFile($file);
  295.             return true;
  296.         }
  297.     }
  298.     /**
  299.      * Finds the path to the file where the class is defined.
  300.      *
  301.      * @param string $class The name of the class
  302.      *
  303.      * @return string|false The path if found, false otherwise
  304.      */
  305.     public function findFile($class)
  306.     {
  307.         // class map lookup
  308.         if (isset($this->classMap[$class])) {
  309.             return $this->classMap[$class];
  310.         }
  311.         if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
  312.             return false;
  313.         }
  314.         if (null !== $this->apcuPrefix) {
  315.             $file apcu_fetch($this->apcuPrefix.$class$hit);
  316.             if ($hit) {
  317.                 return $file;
  318.             }
  319.         }
  320.         $file $this->findFileWithExtension($class'.php');
  321.         // Search for Hack files if we are running on HHVM
  322.         if (false === $file && defined('HHVM_VERSION')) {
  323.             $file $this->findFileWithExtension($class'.hh');
  324.         }
  325.         if (null !== $this->apcuPrefix) {
  326.             apcu_add($this->apcuPrefix.$class$file);
  327.         }
  328.         if (false === $file) {
  329.             // Remember that this class does not exist.
  330.             $this->missingClasses[$class] = true;
  331.         }
  332.         return $file;
  333.     }
  334.     private function findFileWithExtension($class$ext)
  335.     {
  336.         // PSR-4 lookup
  337.         $logicalPathPsr4 strtr($class'\\'DIRECTORY_SEPARATOR) . $ext;
  338.         $first $class[0];
  339.         if (isset($this->prefixLengthsPsr4[$first])) {
  340.             $subPath $class;
  341.             while (false !== $lastPos strrpos($subPath'\\')) {
  342.                 $subPath substr($subPath0$lastPos);
  343.                 $search $subPath.'\\';
  344.                 if (isset($this->prefixDirsPsr4[$search])) {
  345.                     $pathEnd DIRECTORY_SEPARATOR substr($logicalPathPsr4$lastPos 1);
  346.                     foreach ($this->prefixDirsPsr4[$search] as $dir) {
  347.                         if (file_exists($file $dir $pathEnd)) {
  348.                             return $file;
  349.                         }
  350.                     }
  351.                 }
  352.             }
  353.         }
  354.         // PSR-4 fallback dirs
  355.         foreach ($this->fallbackDirsPsr4 as $dir) {
  356.             if (file_exists($file $dir DIRECTORY_SEPARATOR $logicalPathPsr4)) {
  357.                 return $file;
  358.             }
  359.         }
  360.         // PSR-0 lookup
  361.         if (false !== $pos strrpos($class'\\')) {
  362.             // namespaced class name
  363.             $logicalPathPsr0 substr($logicalPathPsr40$pos 1)
  364.                 . strtr(substr($logicalPathPsr4$pos 1), '_'DIRECTORY_SEPARATOR);
  365.         } else {
  366.             // PEAR-like class name
  367.             $logicalPathPsr0 strtr($class'_'DIRECTORY_SEPARATOR) . $ext;
  368.         }
  369.         if (isset($this->prefixesPsr0[$first])) {
  370.             foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
  371.                 if (=== strpos($class$prefix)) {
  372.                     foreach ($dirs as $dir) {
  373.                         if (file_exists($file $dir DIRECTORY_SEPARATOR $logicalPathPsr0)) {
  374.                             return $file;
  375.                         }
  376.                     }
  377.                 }
  378.             }
  379.         }
  380.         // PSR-0 fallback dirs
  381.         foreach ($this->fallbackDirsPsr0 as $dir) {
  382.             if (file_exists($file $dir DIRECTORY_SEPARATOR $logicalPathPsr0)) {
  383.                 return $file;
  384.             }
  385.         }
  386.         // PSR-0 include paths.
  387.         if ($this->useIncludePath && $file stream_resolve_include_path($logicalPathPsr0)) {
  388.             return $file;
  389.         }
  390.         return false;
  391.     }
  392. }
  393. /**
  394.  * Scope isolated include.
  395.  *
  396.  * Prevents access to $this/self from included files.
  397.  */
  398. function includeFile($file)
  399. {
  400.     include $file;
  401. }