/lib/Framework/Autoloader/Autoloader.php

https://github.com/jasondavis/Framework · PHP · 173 lines · 53 code · 15 blank · 105 comment · 9 complexity · 670cccfae2efa894f58e9d439f31c598 MD5 · raw file

  1. <?php
  2. /**
  3. * A simple yet powerful namespace based autoloader for php.
  4. *
  5. *
  6. * <h4>Basic Usage</h4>
  7. * The most basic usage of the autoloader is to map a namespace to a directory like so:
  8. *
  9. * <pre>
  10. * $autoloader = new \Framework\Autoloader();
  11. * $autoloader->addNamespace('MyNameSpace', '/path/to/namespace/root/')
  12. * </pre>
  13. *
  14. * This will autoload all classes that begin with the namespace MyNameSpace to that directory using
  15. * the remainder of the namespace to figure out the path. That is, using the autoloader setup from
  16. * above, the class '\MyNameSpace\Admin\User' would map to '/path/to/namespace/root/Admin/User.php'.
  17. *
  18. * <h4>Mutltiple Directories Per Namespace</h4>
  19. * It is possible to add mulitple directories to a namespace by calling the addNamespace method
  20. * on the same name space more than once. For example:
  21. *
  22. * <pre>
  23. * $autoloader = new \Framework\Autoloader();
  24. * $autoloader->addNamespace('MyNameSpace', '/path/to/namespace/root/')
  25. * $autoloader->addNamespace('MyNameSpace', '/second/path/to/namespace/root/')
  26. * </pre>
  27. *
  28. * Using this setup the autoloader will first look into the first directory provided and if it is
  29. * unable to find the file it will move on to the next directory.
  30. *
  31. *
  32. * <h4>Advanced Usage: Patterns</h4>
  33. * Another way to use the autoloader is by using the simple pattern matching system. You provide
  34. * tokens in the path when you register the namespace that will be replaced using values from the
  35. * class namespace. Here is an example that explains the power of this system.
  36. *
  37. * Assume you have a directory structure like this:
  38. *
  39. * <pre>
  40. * | MyProject
  41. * | -> modules
  42. * | -> Admin
  43. * | -> controllers
  44. * | -> Home.php (class: \MyProject\Admin\Home)
  45. * | -> AccessControl.php (class: \MyProject\Admin\AccessControl)
  46. * </pre>
  47. *
  48. * Lets assume you want to autoload the controllers in the modules folder. We know that the basic
  49. * autoloader would not work because the controllers folder is not in the namespace, that is the
  50. * namespace is not like '\MyProject\Admin\controllers\Home'. To overcome this problem you can write
  51. * a pattern to auto add the controllers directory dynamically.
  52. *
  53. * <pre>
  54. * $autoloader = new \Framework\Autoloader();
  55. * $autoloader->addNamespace('MyProject', '/path/to/MyProject/modules/:1/controllers/')
  56. * </pre>
  57. *
  58. * If you now tried to autoload the \MyProject\Admin\AccessControl is would now map to the directory
  59. * '/path/to/MyProject/modules/Admin/controllers/AccessControl.php' instead of
  60. * '/path/to/MyProject/modules/Admin/AccessControl.php'.
  61. *
  62. * @package Framework\Autoloader
  63. * @version 0.1.0
  64. * @author Tim Oram (mitmaro@mitmaro.ca)
  65. * @copyright Copyright 2010 Tim Oram (<a href="http://www.mitmaro.ca">www.mitmaro.ca</a>)
  66. * @license <a href="http://www.opensource.org/licenses/mit-license.php">The MIT License</a>
  67. */
  68. namespace Framework\Autoloader;
  69. class Autoloader {
  70. /**
  71. * An array of namespaces, directories key value pairs
  72. * @var array
  73. */
  74. protected $namespaces = array();
  75. /**
  76. * Loads framework classes
  77. *
  78. * @param string $class The class name
  79. */
  80. public function classLoader($class) {
  81. $namespace = explode('\\', trim($class, '\\'));
  82. $file = array_pop($namespace) . '.php';
  83. $path = '';
  84. $top = array_shift($namespace);
  85. if (array_key_exists($top, $this->namespaces)) {
  86. foreach ($this->namespaces[$top] as $path) {
  87. // look for replace tokens (ie. :1, :2, :3)
  88. if (preg_match_all('#:[0-9]+#', $path, $matches) > 0) {
  89. // for all the matches found replace the number of the token with the values
  90. // of the namespace at that index
  91. foreach ($matches[0] as $m) {
  92. if (isset($namespace[(int)trim($m, ':') - 1])) {
  93. $path = str_replace($m, $namespace[(int)trim($m, ':') - 1], $path);
  94. }
  95. }
  96. }
  97. // no matches assume, default to doing a simple implode
  98. else if (count($namespace) > 0) {
  99. $path = $path . implode('/', $namespace) . '/';
  100. }
  101. // load the file above if it exists
  102. if (self::fileExists($path . $file)) {
  103. include $path . $file;
  104. return true;
  105. }
  106. }
  107. }
  108. return false;
  109. }
  110. /**
  111. * Adds a namespace, directory key value pair
  112. *
  113. * @param String $namespace The namespace to handle
  114. * @param String $directory The directory to add
  115. */
  116. public function addNamespace($namespace, $directory) {
  117. if (!isset($this->namespaces[(string)$namespace])) {
  118. $this->namespaces[(string)$namespace] = array();
  119. }
  120. $this->namespaces[(string)$namespace][] = rtrim($directory, '/') . '/';
  121. }
  122. /**
  123. * Clears all directories on a namespace
  124. *
  125. * @param String $namespace The namespace to handle
  126. */
  127. public function clearNamespace($namespace) {
  128. unset($this->namespaces[(string)$namespace]);
  129. }
  130. /**
  131. * Returns the namespace map array
  132. *
  133. * @return array The namespace map array
  134. */
  135. public function getNamespaceMap() {
  136. return $this->namespaces;
  137. }
  138. /**
  139. * Check if a file can be found on the include path
  140. *
  141. * @param string $file A file with an optional path
  142. *
  143. * @return boolean True is file found, otherwise false
  144. *
  145. */
  146. public static function fileExists($file) {
  147. // catch paths that don't require the include path (ie. absolute path)
  148. if (is_file($file)) {
  149. return true;
  150. }
  151. foreach(explode(PATH_SEPARATOR, get_include_path()) as $path) {
  152. if (is_file($path . '/' . $file)) {
  153. return true;
  154. }
  155. }
  156. return false;
  157. }
  158. }