autoloader.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.5
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2013 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. /**
  14. * The Autloader is responsible for all class loading. It allows you to define
  15. * different load paths based on namespaces. It also lets you set explicit paths
  16. * for classes to be loaded from.
  17. *
  18. * @package Fuel
  19. * @subpackage Core
  20. */
  21. class Autoloader
  22. {
  23. /**
  24. * @var array $classes holds all the classes and paths
  25. */
  26. protected static $classes = array();
  27. /**
  28. * @var array holds all the namespace paths
  29. */
  30. protected static $namespaces = array();
  31. /**
  32. * Holds all the PSR-0 compliant namespaces. These namespaces should
  33. * be loaded according to the PSR-0 standard.
  34. *
  35. * @var array
  36. */
  37. protected static $psr_namespaces = array();
  38. /**
  39. * @var array list off namespaces of which classes will be aliased to global namespace
  40. */
  41. protected static $core_namespaces = array(
  42. 'Fuel\\Core'
  43. );
  44. /**
  45. * @var array the default path to look in if the class is not in a package
  46. */
  47. protected static $default_path = null;
  48. /**
  49. * @var bool whether to initialize a loaded class
  50. */
  51. protected static $auto_initialize = null;
  52. /**
  53. * Adds a namespace search path. Any class in the given namespace will be
  54. * looked for in the given path.
  55. *
  56. * @param string the namespace
  57. * @param string the path
  58. * @return void
  59. */
  60. public static function add_namespace($namespace, $path, $psr = false)
  61. {
  62. static::$namespaces[$namespace] = $path;
  63. if ($psr)
  64. {
  65. static::$psr_namespaces[$namespace] = $path;
  66. }
  67. }
  68. /**
  69. * Adds an array of namespace paths. See {add_namespace}.
  70. *
  71. * @param array the namespaces
  72. * @param bool whether to prepend the namespace to the search path
  73. * @return void
  74. */
  75. public static function add_namespaces(array $namespaces, $prepend = false)
  76. {
  77. if ( ! $prepend)
  78. {
  79. static::$namespaces = array_merge(static::$namespaces, $namespaces);
  80. }
  81. else
  82. {
  83. static::$namespaces = $namespaces + static::$namespaces;
  84. }
  85. }
  86. /**
  87. * Returns the namespace's path or false when it doesn't exist.
  88. *
  89. * @param string the namespace to get the path for
  90. * @return array|bool the namespace path or false
  91. */
  92. public static function namespace_path($namespace)
  93. {
  94. if ( ! array_key_exists($namespace, static::$namespaces))
  95. {
  96. return false;
  97. }
  98. return static::$namespaces[$namespace];
  99. }
  100. /**
  101. * Adds a classes load path. Any class added here will not be searched for
  102. * but explicitly loaded from the path.
  103. *
  104. * @param string the class name
  105. * @param string the path to the class file
  106. * @return void
  107. */
  108. public static function add_class($class, $path)
  109. {
  110. static::$classes[$class] = $path;
  111. }
  112. /**
  113. * Adds multiple class paths to the load path. See {@see Autoloader::add_class}.
  114. *
  115. * @param array the class names and paths
  116. * @return void
  117. */
  118. public static function add_classes($classes)
  119. {
  120. foreach ($classes as $class => $path)
  121. {
  122. static::$classes[$class] = $path;
  123. }
  124. }
  125. /**
  126. * Aliases the given class into the given Namespace. By default it will
  127. * add it to the global namespace.
  128. *
  129. * <code>
  130. * Autoloader::alias_to_namespace('Foo\\Bar');
  131. * Autoloader::alias_to_namespace('Foo\\Bar', '\\Baz');
  132. * </code>
  133. *
  134. * @param string $class the class name
  135. * @param string $namespace the namespace to alias to
  136. */
  137. public static function alias_to_namespace($class, $namespace = '')
  138. {
  139. empty($namespace) or $namespace = rtrim($namespace, '\\').'\\';
  140. $parts = explode('\\', $class);
  141. $root_class = $namespace.array_pop($parts);
  142. class_alias($class, $root_class);
  143. }
  144. /**
  145. * Register's the autoloader to the SPL autoload stack.
  146. *
  147. * @return void
  148. */
  149. public static function register()
  150. {
  151. spl_autoload_register('Autoloader::load', true, true);
  152. }
  153. /**
  154. * Returns the class with namespace prefix when available
  155. *
  156. * @param string
  157. * @return bool|string
  158. */
  159. protected static function find_core_class($class)
  160. {
  161. foreach (static::$core_namespaces as $ns)
  162. {
  163. if (array_key_exists($ns_class = $ns.'\\'.$class, static::$classes))
  164. {
  165. return $ns_class;
  166. }
  167. }
  168. return false;
  169. }
  170. /**
  171. * Add a namespace for which classes may be used without the namespace prefix and
  172. * will be auto-aliased to the global namespace.
  173. * Prefixing the classes will overwrite core classes and previously added namespaces.
  174. *
  175. * @param string
  176. * @param bool
  177. * @return void
  178. */
  179. public static function add_core_namespace($namespace, $prefix = true)
  180. {
  181. if ($prefix)
  182. {
  183. array_unshift(static::$core_namespaces, $namespace);
  184. }
  185. else
  186. {
  187. array_push(static::$core_namespaces, $namespace);
  188. }
  189. }
  190. /**
  191. * Loads a class.
  192. *
  193. * @param string $class Class to load
  194. * @return bool If it loaded the class
  195. */
  196. public static function load($class)
  197. {
  198. // deal with funny is_callable('static::classname') side-effect
  199. if (strpos($class, 'static::') === 0)
  200. {
  201. // is called from within the class, so it's already loaded
  202. return true;
  203. }
  204. $loaded = false;
  205. $class = ltrim($class, '\\');
  206. $namespaced = ($pos = strripos($class, '\\')) !== false;
  207. if (empty(static::$auto_initialize))
  208. {
  209. static::$auto_initialize = $class;
  210. }
  211. if (isset(static::$classes[$class]))
  212. {
  213. include str_replace('/', DS, static::$classes[$class]);
  214. static::init_class($class);
  215. $loaded = true;
  216. }
  217. elseif ($full_class = static::find_core_class($class))
  218. {
  219. if ( ! class_exists($full_class, false) and ! interface_exists($full_class, false))
  220. {
  221. include static::prep_path(static::$classes[$full_class]);
  222. }
  223. class_alias($full_class, $class);
  224. static::init_class($class);
  225. $loaded = true;
  226. }
  227. else
  228. {
  229. $full_ns = substr($class, 0, $pos);
  230. if ($full_ns)
  231. {
  232. foreach (static::$namespaces as $ns => $path)
  233. {
  234. $ns = ltrim($ns, '\\');
  235. if (stripos($full_ns, $ns) === 0)
  236. {
  237. $path .= static::class_to_path(
  238. substr($class, strlen($ns) + 1),
  239. array_key_exists($ns, static::$psr_namespaces)
  240. );
  241. if (is_file($path))
  242. {
  243. require $path;
  244. static::init_class($class);
  245. $loaded = true;
  246. break;
  247. }
  248. }
  249. }
  250. }
  251. if ( ! $loaded)
  252. {
  253. $path = APPPATH.'classes/'.static::class_to_path($class);
  254. if (file_exists($path))
  255. {
  256. include $path;
  257. static::init_class($class);
  258. $loaded = true;
  259. }
  260. }
  261. }
  262. // Prevent failed load from keeping other classes from initializing
  263. if (static::$auto_initialize == $class)
  264. {
  265. static::$auto_initialize = null;
  266. }
  267. return $loaded;
  268. }
  269. /**
  270. * Reset the auto initialize state after an autoloader exception.
  271. * This method is called by the exception handler, and is considered an
  272. * internal method!
  273. *
  274. * @access protected
  275. */
  276. public static function _reset()
  277. {
  278. static::$auto_initialize = null;
  279. }
  280. /**
  281. * Takes a class name and turns it into a path. It follows the PSR-0
  282. * standard, except for makes the entire path lower case, unless you
  283. * tell it otherwise.
  284. *
  285. * Note: This does not check if the file exists...just gets the path
  286. *
  287. * @param string $class Class name
  288. * @param bool $psr Whether this is a PSR-0 compliant class
  289. * @return string Path for the class
  290. */
  291. protected static function class_to_path($class, $psr = false)
  292. {
  293. $file = '';
  294. if ($last_ns_pos = strripos($class, '\\'))
  295. {
  296. $namespace = substr($class, 0, $last_ns_pos);
  297. $class = substr($class, $last_ns_pos + 1);
  298. $file = str_replace('\\', DS, $namespace).DS;
  299. }
  300. $file .= str_replace('_', DS, $class).'.php';
  301. if ( ! $psr)
  302. {
  303. $file = strtolower($file);
  304. }
  305. return $file;
  306. }
  307. /**
  308. * Prepares a given path by making sure the directory separators are correct.
  309. *
  310. * @param string $path Path to prepare
  311. * @return string Prepped path
  312. */
  313. protected static function prep_path($path)
  314. {
  315. return str_replace(array('/', '\\'), DS, $path);
  316. }
  317. /**
  318. * Checks to see if the given class has a static _init() method. If so then
  319. * it calls it.
  320. *
  321. * @param string the class name
  322. */
  323. protected static function init_class($class)
  324. {
  325. if (static::$auto_initialize === $class)
  326. {
  327. static::$auto_initialize = null;
  328. if (method_exists($class, '_init') and is_callable($class.'::_init'))
  329. {
  330. call_user_func($class.'::_init');
  331. }
  332. }
  333. }
  334. }