Autoloader.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. <?php
  2. /**
  3. * Hoa
  4. *
  5. *
  6. * @license
  7. *
  8. * New BSD License
  9. *
  10. * Copyright © 2007-2017, Hoa community. All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions are met:
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. * * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the distribution.
  19. * * Neither the name of the Hoa nor the names of its contributors may be
  20. * used to endorse or promote products derived from this software without
  21. * specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. */
  35. namespace Hoa\Consistency;
  36. /**
  37. * Class Hoa\Consistency\Autoloader.
  38. *
  39. * This class is a PSR-4 compliant autoloader.
  40. *
  41. * @copyright Copyright © 2007-2017 Hoa community
  42. * @license New BSD License
  43. */
  44. class Autoloader
  45. {
  46. /**
  47. * Namespace prefixes to base directories.
  48. *
  49. * @var array
  50. */
  51. protected $_namespacePrefixesToBaseDirectories = [];
  52. /**
  53. * Add a base directory for a namespace prefix.
  54. *
  55. * @param string $prefix Namespace prefix.
  56. * @param string $baseDirectory Base directory for this prefix.
  57. * @param bool $prepend Whether the prefix is prepended or
  58. * appended to the prefix' stack.
  59. * @return void
  60. */
  61. public function addNamespace($prefix, $baseDirectory, $prepend = false)
  62. {
  63. $prefix = trim($prefix, '\\') . '\\';
  64. $baseDirectory = rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
  65. if (false === isset($this->_namespacePrefixesToBaseDirectories[$prefix])) {
  66. $this->_namespacePrefixesToBaseDirectories[$prefix] = [];
  67. }
  68. if (true === $prepend) {
  69. array_unshift(
  70. $this->_namespacePrefixesToBaseDirectories[$prefix],
  71. $baseDirectory
  72. );
  73. } else {
  74. array_push(
  75. $this->_namespacePrefixesToBaseDirectories[$prefix],
  76. $baseDirectory
  77. );
  78. }
  79. return;
  80. }
  81. /**
  82. * Try to load the entity file for a given entity name.
  83. *
  84. * @param string $entity Entity name to load.
  85. * @return bool
  86. */
  87. public function load($entity)
  88. {
  89. $entityPrefix = $entity;
  90. $hasBaseDirectory = false;
  91. while (false !== $pos = strrpos($entityPrefix, '\\')) {
  92. $currentEntityPrefix = substr($entity, 0, $pos + 1);
  93. $entityPrefix = rtrim($currentEntityPrefix, '\\');
  94. $entitySuffix = substr($entity, $pos + 1);
  95. $entitySuffixAsPath = str_replace('\\', '/', $entitySuffix);
  96. if (false === $this->hasBaseDirectory($currentEntityPrefix)) {
  97. continue;
  98. }
  99. $hasBaseDirectory = true;
  100. foreach ($this->getBaseDirectories($currentEntityPrefix) as $baseDirectory) {
  101. $file = $baseDirectory . $entitySuffixAsPath . '.php';
  102. if (false !== $this->requireFile($file)) {
  103. return $file;
  104. }
  105. }
  106. }
  107. if (true === $hasBaseDirectory &&
  108. $entity === Consistency::getEntityShortestName($entity) &&
  109. false !== $pos = strrpos($entity, '\\')) {
  110. return $this->runAutoloaderStack(
  111. $entity . '\\' . substr($entity, $pos + 1)
  112. );
  113. }
  114. return null;
  115. }
  116. /**
  117. * Require a file if exists.
  118. *
  119. * @param string $filename File name.
  120. * @return bool
  121. */
  122. public function requireFile($filename)
  123. {
  124. if (false === file_exists($filename)) {
  125. return false;
  126. }
  127. require $filename;
  128. return true;
  129. }
  130. /**
  131. * Check whether at least one base directory exists for a namespace prefix.
  132. *
  133. * @param string $namespacePrefix Namespace prefix.
  134. * @return bool
  135. */
  136. public function hasBaseDirectory($namespacePrefix)
  137. {
  138. return isset($this->_namespacePrefixesToBaseDirectories[$namespacePrefix]);
  139. }
  140. /**
  141. * Get declared base directories for a namespace prefix.
  142. *
  143. * @param string $namespacePrefix Namespace prefix.
  144. * @return array
  145. */
  146. public function getBaseDirectories($namespacePrefix)
  147. {
  148. if (false === $this->hasBaseDirectory($namespacePrefix)) {
  149. return [];
  150. }
  151. return $this->_namespacePrefixesToBaseDirectories[$namespacePrefix];
  152. }
  153. /**
  154. * Get loaded classes.
  155. *
  156. * @return array
  157. */
  158. public static function getLoadedClasses()
  159. {
  160. return get_declared_classes();
  161. }
  162. /**
  163. * Run the entire autoloader stack with a specific entity.
  164. *
  165. * @param string $entity Entity name to load.
  166. * @return void
  167. */
  168. public function runAutoloaderStack($entity)
  169. {
  170. return spl_autoload_call($entity);
  171. }
  172. /**
  173. * Register the autoloader.
  174. *
  175. * @param bool $prepend Prepend this autoloader to the stack or not.
  176. * @return bool
  177. */
  178. public function register($prepend = false)
  179. {
  180. return spl_autoload_register([$this, 'load'], true, $prepend);
  181. }
  182. /**
  183. * Unregister the autoloader.
  184. *
  185. * @return bool
  186. */
  187. public function unregister()
  188. {
  189. return spl_autoload_unregister([$this, 'load']);
  190. }
  191. /**
  192. * Get all registered autoloaders (not only from this library).
  193. *
  194. * @return array
  195. */
  196. public function getRegisteredAutoloaders()
  197. {
  198. return spl_autoload_functions();
  199. }
  200. /**
  201. * Dynamic new, a simple factory.
  202. * It loads and constructs a class, with provided arguments.
  203. *
  204. * @param bool $classname Classname.
  205. * @param array $arguments Arguments for the constructor.
  206. * @return object
  207. */
  208. public static function dnew($classname, array $arguments = [])
  209. {
  210. $classname = ltrim($classname, '\\');
  211. if (false === Consistency::entityExists($classname, false)) {
  212. spl_autoload_call($classname);
  213. }
  214. $class = new \ReflectionClass($classname);
  215. if (empty($arguments) || false === $class->hasMethod('__construct')) {
  216. return $class->newInstance();
  217. }
  218. return $class->newInstanceArgs($arguments);
  219. }
  220. }
  221. /**
  222. * Autoloader.
  223. */
  224. $autoloader = new Autoloader();
  225. $autoloader->addNamespace('Hoa', dirname(__DIR__));
  226. $autoloader->register();