PageRenderTime 44ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Components/Autoloader/UniversalClassLoader.php

http://github.com/amal/AzaThread
PHP | 340 lines | 169 code | 32 blank | 139 comment | 1 complexity | c44b2ed53e1440515d5c9ac0903a6ac5 MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. namespace Aza\Components\Autoloader;
  3. /**
  4. * Implements a "universal" autoloader for PHP
  5. *
  6. * It is able to load classes that use either:
  7. *
  8. * * PSR-0 Standart. The technical interoperability standards for PHP 5.3 namespaces and
  9. * class names (https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md);
  10. *
  11. * * The PEAR naming convention for classes (http://pear.php.net/).
  12. *
  13. * * Pre-registered classes map
  14. *
  15. * @project Anizoptera CMF
  16. * @package system.AzaAutoloader
  17. * @version $Id: UniversalClassLoader.php 3252 2012-04-09 21:53:00Z samally $
  18. * @author Amal Samally <amal.samally at gmail.com>
  19. * @license MIT
  20. */
  21. class UniversalClassLoader
  22. {
  23. /**
  24. * Class map.
  25. * class => file path
  26. */
  27. private $map = array();
  28. /**
  29. * Registered namespaces.
  30. * namespace => array(directories)
  31. */
  32. private $namespaces = array();
  33. /**
  34. * Registered fallback directories for namespaces classes.
  35. */
  36. private $namespaceFallbacks = array();
  37. /**
  38. * Registered prefixes for the PEAR naming convention.
  39. * prefix => array(directories)
  40. */
  41. private $prefixes = array();
  42. /**
  43. * Registered fallback directories the PEAR naming convention.
  44. */
  45. private $prefixFallbacks = array();
  46. /**
  47. * Whether to use include path.
  48. */
  49. private $useIncludePath = true;
  50. /**
  51. * Turns on searching the include for class files. Allows easy loading
  52. * of installed PEAR packages
  53. *
  54. * @param bool $useIncludePath
  55. */
  56. public function setUseIncludePath($useIncludePath)
  57. {
  58. $this->useIncludePath = $useIncludePath;
  59. }
  60. /**
  61. * Can be used to check if the autoloader uses the include path to check
  62. * for classes.
  63. *
  64. * @return bool
  65. */
  66. public function getUseIncludePath()
  67. {
  68. return $this->useIncludePath;
  69. }
  70. /**
  71. * Sets the class map
  72. *
  73. * @param array $classes
  74. */
  75. public function setMap($classes)
  76. {
  77. $this->map = $classes;
  78. }
  79. /**
  80. * Adds classes to the the map
  81. *
  82. * @param array $classes
  83. */
  84. public function addToMap($classes)
  85. {
  86. $this->map = $classes + $this->map;
  87. }
  88. /**
  89. * Returns the class map
  90. *
  91. * @return array
  92. */
  93. public function getMap()
  94. {
  95. return $this->map;
  96. }
  97. /**
  98. * Gets the configured namespaces.
  99. *
  100. * @return array A hash with namespaces as keys and directories as values
  101. */
  102. public function getNamespaces()
  103. {
  104. return $this->namespaces;
  105. }
  106. /**
  107. * Gets the configured class prefixes.
  108. *
  109. * @return array A hash with class prefixes as keys and directories as values
  110. */
  111. public function getPrefixes()
  112. {
  113. return $this->prefixes;
  114. }
  115. /**
  116. * Gets the directory(ies) to use as a fallback for namespaces.
  117. *
  118. * @return array An array of directories
  119. */
  120. public function getNamespaceFallbacks()
  121. {
  122. return $this->namespaceFallbacks;
  123. }
  124. /**
  125. * Gets the directory(ies) to use as a fallback for class prefixes.
  126. *
  127. * @return array An array of directories
  128. */
  129. public function getPrefixFallbacks()
  130. {
  131. return $this->prefixFallbacks;
  132. }
  133. /**
  134. * Registers an array of namespaces
  135. *
  136. * @param array $namespaces An array of namespaces (namespaces as keys and locations as values)
  137. */
  138. public function registerNamespaces($namespaces)
  139. {
  140. foreach ($namespaces as $namespace => $locations) {
  141. $this->namespaces[$namespace] = (array)$locations;
  142. }
  143. }
  144. /**
  145. * Registers a namespace.
  146. *
  147. * @param string $namespace The namespace
  148. * @param array|string $paths The location(s) of the namespace
  149. */
  150. public function registerNamespace($namespace, $paths)
  151. {
  152. $this->namespaces[$namespace] = (array)$paths;
  153. }
  154. /**
  155. * Registers the directory to use as a fallback for namespaces.
  156. *
  157. * @param array $dirs An array of directories
  158. */
  159. public function registerNamespaceFallbacks($dirs)
  160. {
  161. $this->namespaceFallbacks = $dirs;
  162. }
  163. /**
  164. * Registers an array of classes using the PEAR naming convention.
  165. *
  166. * @param array $classes An array of classes (prefixes as keys and locations as values)
  167. */
  168. public function registerPrefixes($classes)
  169. {
  170. foreach ($classes as $prefix => $locations) {
  171. $this->prefixes[$prefix] = (array)$locations;
  172. }
  173. }
  174. /**
  175. * Registers a set of classes using the PEAR naming convention.
  176. *
  177. * @param string $prefix The classes prefix
  178. * @param array|string $paths The location(s) of the classes
  179. */
  180. public function registerPrefix($prefix, $paths)
  181. {
  182. $this->prefixes[$prefix] = (array)$paths;
  183. }
  184. /**
  185. * Registers the directory to use as a fallback for class prefixes.
  186. *
  187. * @param array $dirs An array of directories
  188. */
  189. public function registerPrefixFallbacks($dirs)
  190. {
  191. $this->prefixFallbacks = $dirs;
  192. }
  193. /**
  194. * Registers this instance as an autoloader.
  195. *
  196. * @param bool $prepend Whether to prepend the autoloader or not
  197. */
  198. public function register($prepend = false)
  199. {
  200. spl_autoload_register(array($this, 'loadClass'), true, $prepend);
  201. }
  202. /**
  203. * Loads the given class or interface.
  204. *
  205. * @param string $class The name of the class
  206. *
  207. * @return bool
  208. */
  209. public function loadClass($class)
  210. {
  211. /** @noinspection PhpIncludeInspection */
  212. return ($file = $this->findFile($class)) && require_once $file;
  213. }
  214. /**
  215. * Finds the path to the file where the class is defined.
  216. *
  217. * @param string $class The name of the class
  218. *
  219. * @return string|bool The path, if found, FALSE otherwise.
  220. */
  221. public function findFile($class)
  222. {
  223. // Trim first slash
  224. $class[0] === ($ns = '\\') && $class = substr($class, 1);
  225. // Class is registered in the map
  226. if (isset($this->map[$class])) {
  227. return $this->map[$class];
  228. }
  229. // Namespaced class name
  230. $ext = '.php';
  231. if ($pos = strrpos($class, $ns)) {
  232. $namespace = substr($class, 0, $pos);
  233. $class = substr($class, $pos + 1);
  234. $normalizedClass = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
  235. // Iterate over NS parts to find registered paths
  236. $last = false;
  237. $path = '';
  238. $pos = 0;
  239. do {
  240. if (false === $pos = strpos($namespace, $ns, $pos)) {
  241. $rootNS = $namespace;
  242. $last = true;
  243. } else {
  244. $rootNS = substr($namespace, 0, $pos);
  245. }
  246. // Check in the registered namespaces
  247. if (isset($this->namespaces[$rootNS])) {
  248. $_path = $last
  249. ? $normalizedClass
  250. : strtr(substr($namespace, $pos + 1), $ns, DIRECTORY_SEPARATOR)
  251. . DIRECTORY_SEPARATOR . $normalizedClass;
  252. foreach ($this->namespaces[$rootNS] as $filePath) {
  253. if (file_exists($filePath .= $_path)) {
  254. return $filePath;
  255. }
  256. }
  257. if ($last) {
  258. break;
  259. }
  260. } elseif ($last) {
  261. $path = strtr($namespace, $ns, DIRECTORY_SEPARATOR)
  262. . DIRECTORY_SEPARATOR . $normalizedClass;
  263. break;
  264. }
  265. $pos++;
  266. } while (true);
  267. // Check fallbacks
  268. foreach ($this->namespaceFallbacks as $filePath) {
  269. if (file_exists($filePath .= DIRECTORY_SEPARATOR . $path)) {
  270. return $filePath;
  271. }
  272. }
  273. }
  274. // PEAR naming convention
  275. else {
  276. // Check in the registered prefixes
  277. $normalizedClass = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
  278. foreach ($this->prefixes as $prefix => $dirs) {
  279. if (!strncmp($class, $prefix, strlen($prefix))) {
  280. continue;
  281. }
  282. foreach ($dirs as $filePath) {
  283. if (file_exists($filePath .= DIRECTORY_SEPARATOR . $normalizedClass)) {
  284. return $filePath;
  285. }
  286. }
  287. }
  288. // Check fallbacks
  289. $path = $normalizedClass;
  290. foreach ($this->prefixFallbacks as $filePath) {
  291. if (file_exists($filePath .= DIRECTORY_SEPARATOR . $normalizedClass)) {
  292. return $filePath;
  293. }
  294. }
  295. }
  296. // Last chance - include path
  297. return ($path && $this->useIncludePath && $path = stream_resolve_include_path($path))
  298. ? $path
  299. : false;
  300. }
  301. }