/library/Zend/Loader/ModuleAutoloader.php

https://github.com/leerbag/zf2 · PHP · 304 lines · 173 code · 22 blank · 109 comment · 29 complexity · 48689aae8d17898f9f3386577bde7d59 MD5 · raw file

  1. <?php
  2. namespace Zend\Loader;
  3. // Grab SplAutoloader interface
  4. require_once __DIR__ . '/SplAutoloader.php';
  5. use SplFileInfo,
  6. Traversable;
  7. class ModuleAutoloader implements SplAutoloader
  8. {
  9. /**
  10. * @var array An array of module paths to scan
  11. */
  12. protected $paths = array();
  13. /**
  14. * @var array An array of modulename => path
  15. */
  16. protected $explicitPaths = array();
  17. /**
  18. * @var array An array of supported phar formats
  19. */
  20. protected $pharExtensions = array(
  21. 'phar',
  22. 'phar.gz',
  23. 'phar.bz2',
  24. 'phar.tar',
  25. 'phar.tar.gz',
  26. 'phar.tar.bz2',
  27. 'phar.zip',
  28. 'tar',
  29. 'tar.gz',
  30. 'tar.bz2',
  31. 'zip',
  32. );
  33. /**
  34. * @var array An array of module classes to their containing files
  35. */
  36. protected $moduleClassMap = array();
  37. /**
  38. * Constructor
  39. *
  40. * Allow configuration of the autoloader via the constructor.
  41. *
  42. * @param null|array|Traversable $options
  43. * @return void
  44. */
  45. public function __construct($options = null)
  46. {
  47. if (null !== $options) {
  48. $this->setOptions($options);
  49. }
  50. }
  51. /**
  52. * Configure the autoloader
  53. *
  54. * In most cases, $options should be either an associative array or
  55. * Traversable object.
  56. *
  57. * @param array|Traversable $options
  58. * @return SplAutoloader
  59. */
  60. public function setOptions($options)
  61. {
  62. $this->registerPaths($options);
  63. return $this;
  64. }
  65. /**
  66. * Autoload a class
  67. *
  68. * @param $class
  69. * @return mixed
  70. * False [if unable to load $class]
  71. * get_class($class) [if $class is successfully loaded]
  72. */
  73. public function autoload($class)
  74. {
  75. // Limit scope of this autoloader
  76. if (substr($class, -7) !== '\Module') {
  77. return false;
  78. }
  79. $moduleName = substr($class, 0, -7);
  80. if (isset($this->explicitPaths[$moduleName])) {
  81. if ($classLoaded = $this->loadModuleFromDir($this->explicitPaths[$moduleName], $class)) {
  82. return $classLoaded;
  83. } elseif ($classLoaded = $this->loadModuleFromPhar($this->explicitPaths[$moduleName], $class)) {
  84. return $classLoaded;
  85. }
  86. }
  87. $moduleClassPath = str_replace('\\', DIRECTORY_SEPARATOR, $moduleName);
  88. foreach ($this->paths as $path) {
  89. $path = $path . $moduleClassPath;
  90. if ($classLoaded = $this->loadModuleFromDir($path, $class)) {
  91. return $classLoaded;
  92. }
  93. // No directory with Module.php, searching for phars
  94. //$moduleName = substr($class, 0, strpos($class, '\\'));
  95. // Find executable phars
  96. $matches = glob($path . '.{' . implode($this->pharExtensions, ',') . '}', GLOB_BRACE);
  97. foreach ($matches as $phar) {
  98. if ($classLoaded = $this->loadModuleFromPhar($phar, $class)) {
  99. return $classLoaded;
  100. }
  101. }
  102. }
  103. return false;
  104. }
  105. /**
  106. * loadModuleFromDir
  107. *
  108. * @param string $dirPath
  109. * @param string $class
  110. * @return mixed
  111. * False [if unable to load $class]
  112. * get_class($class) [if $class is successfully loaded]
  113. */
  114. protected function loadModuleFromDir($dirPath, $class)
  115. {
  116. $file = new SplFileInfo($dirPath . '/Module.php');
  117. if ($file->isReadable() && $file->isFile()) {
  118. // Found directory with Module.php in it
  119. require_once $file->getRealPath();
  120. if (class_exists($class)) {
  121. $this->moduleClassMap[$class] = $file->getRealPath();
  122. return $class;
  123. }
  124. }
  125. return false;
  126. }
  127. /**
  128. * loadModuleFromPhar
  129. *
  130. * @param string $pharPath
  131. * @param string $class
  132. * @return mixed
  133. * False [if unable to load $class]
  134. * get_class($class) [if $class is successfully loaded]
  135. */
  136. protected function loadModuleFromPhar($pharPath, $class)
  137. {
  138. $pharPath = static::normalizePath($pharPath, false);
  139. $file = new SplFileInfo($pharPath);
  140. if (!$file->isReadable() || !$file->isFile()) {
  141. return false;
  142. }
  143. // Phase 0: Check for executable phar with Module class in stub
  144. if (strpos($file->getRealPath(), '.phar') !== false) {
  145. // First see if the stub makes the Module class available
  146. require_once $file->getRealPath();
  147. if (class_exists($class)) {
  148. $this->moduleClassMap[$class] = $file->getRealPath();
  149. return $class;
  150. }
  151. }
  152. // Phase 1: Not executable phar, no stub, or stub did not provide Module class; try Module.php directly
  153. $moduleClassFile = 'phar://' . $file->getRealPath() . '/Module.php';
  154. $moduleFile = new SplFileInfo($moduleClassFile);
  155. if ($moduleFile->isReadable() && $moduleFile->isFile()) {
  156. require_once $moduleClassFile;
  157. if (class_exists($class)) {
  158. $this->moduleClassMap[$class] = $moduleClassFile;
  159. return $class;
  160. }
  161. }
  162. // Phase 2: Check for nested module directory within archive
  163. // Checks for /path/to/MyModule.tar/MyModule/Module.php
  164. // (shell-integrated zip/tar utilities wrap directories like this)
  165. $pharBaseName = $this->pharFileToModuleName($file->getRealPath());
  166. $moduleClassFile = 'phar://' . $file->getRealPath() . '/' . $pharBaseName . '/Module.php';
  167. $moduleFile = new SplFileInfo($moduleClassFile);
  168. if ($moduleFile->isReadable() && $moduleFile->isFile()) {
  169. require_once $moduleClassFile;
  170. if (class_exists($class)) {
  171. $this->moduleClassMap[$class] = $moduleClassFile;
  172. return $class;
  173. }
  174. }
  175. return false;
  176. }
  177. /**
  178. * Register the autoloader with spl_autoload registry
  179. *
  180. * @return void
  181. */
  182. public function register()
  183. {
  184. spl_autoload_register(array($this, 'autoload'));
  185. }
  186. /**
  187. * Unregister the autoloader with spl_autoload registry
  188. *
  189. * @return void
  190. */
  191. public function unregister()
  192. {
  193. $test = spl_autoload_unregister(array($this, 'autoload'));
  194. }
  195. /**
  196. * registerPaths
  197. *
  198. * @param array|Traversable $paths
  199. * @return ModuleLoader
  200. */
  201. public function registerPaths($paths)
  202. {
  203. if (is_array($paths) || $paths instanceof Traversable) {
  204. foreach ($paths as $module => $path) {
  205. if (is_string($module)) {
  206. $this->registerPath($path, $module);
  207. } else {
  208. $this->registerPath($path);
  209. }
  210. }
  211. } else {
  212. throw new \InvalidArgumentException(
  213. 'Parameter to \\Zend\\Loader\\ModuleAutoloader\'s '
  214. . 'registerPaths method must be an array or '
  215. . 'implement the \\Traversable interface'
  216. );
  217. }
  218. return $this;
  219. }
  220. /**
  221. * registerPath
  222. *
  223. * @param string $path
  224. * @param string $moduleName
  225. * @return ModuleLoader
  226. */
  227. public function registerPath($path, $moduleName = false)
  228. {
  229. if (!is_string($path)) {
  230. throw new \InvalidArgumentException(sprintf(
  231. 'Invalid path provided; must be a string, received %s',
  232. gettype($path)
  233. ));
  234. }
  235. if ($moduleName) {
  236. $this->explicitPaths[$moduleName] = static::normalizePath($path);
  237. } else {
  238. $this->paths[] = static::normalizePath($path);
  239. }
  240. return $this;
  241. }
  242. /**
  243. * getPaths
  244. *
  245. * This is primarily for unit testing, but could have other uses.
  246. *
  247. * @return array
  248. */
  249. public function getPaths()
  250. {
  251. return $this->paths;
  252. }
  253. /**
  254. * Returns the base module name from the path to a phar
  255. *
  256. * @param string $pharPath
  257. * @return string
  258. */
  259. protected function pharFileToModuleName($pharPath)
  260. {
  261. do {
  262. $pathinfo = pathinfo($pharPath);
  263. $pharPath = $pathinfo['filename'];
  264. } while (isset($pathinfo['extension']));
  265. return $pathinfo['filename'];
  266. }
  267. /**
  268. * Normalize a path for insertion in the stack
  269. *
  270. * @param string $path
  271. * @return string
  272. */
  273. public static function normalizePath($path, $trailingSlash = true)
  274. {
  275. $path = rtrim($path, '/');
  276. $path = rtrim($path, '\\');
  277. if ($trailingSlash) {
  278. $path .= DIRECTORY_SEPARATOR;
  279. }
  280. return $path;
  281. }
  282. }