composer package updates
[openemr.git] / vendor / zendframework / zend-loader / src / ModuleAutoloader.php
blob502019cf0bd864d1ba7c1b45ab72111d1ecc369a
1 <?php
2 /**
3 * @see https://github.com/zendframework/zend-loader for the canonical source repository
4 * @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (https://www.zend.com)
5 * @license https://github.com/zendframework/zend-loader/blob/master/LICENSE.md New BSD License
6 */
8 namespace Zend\Loader;
10 // Grab SplAutoloader interface
11 require_once __DIR__ . '/SplAutoloader.php';
13 use GlobIterator;
14 use Phar;
15 use PharFileInfo;
16 use SplFileInfo;
17 use Traversable;
19 class ModuleAutoloader implements SplAutoloader
21 /**
22 * @var array An array of module paths to scan
24 protected $paths = [];
26 /**
27 * @var array An array of modulename => path
29 protected $explicitPaths = [];
31 /**
32 * @var array An array of namespaceName => namespacePath
34 protected $namespacedPaths = [];
36 /**
37 * @var string Will contain the absolute phar:// path to the executable when packaged as phar file
39 protected $pharBasePath = "";
41 /**
42 * @var array An array of supported phar extensions (filled on constructor)
44 protected $pharExtensions = [];
46 /**
47 * @var array An array of module classes to their containing files
49 protected $moduleClassMap = [];
51 /**
52 * Constructor
54 * Allow configuration of the autoloader via the constructor.
56 * @param null|array|Traversable $options
58 public function __construct($options = null)
60 if (extension_loaded('phar')) {
61 $this->pharBasePath = Phar::running(true);
62 $this->pharExtensions = [
63 'phar',
64 'phar.tar',
65 'tar',
68 // ext/zlib enabled -> phar can read gzip & zip compressed files
69 if (extension_loaded('zlib')) {
70 $this->pharExtensions[] = 'phar.gz';
71 $this->pharExtensions[] = 'phar.tar.gz';
72 $this->pharExtensions[] = 'tar.gz';
74 $this->pharExtensions[] = 'phar.zip';
75 $this->pharExtensions[] = 'zip';
78 // ext/bzip2 enabled -> phar can read bz2 compressed files
79 if (extension_loaded('bzip2')) {
80 $this->pharExtensions[] = 'phar.bz2';
81 $this->pharExtensions[] = 'phar.tar.bz2';
82 $this->pharExtensions[] = 'tar.bz2';
86 if (null !== $options) {
87 $this->setOptions($options);
91 /**
92 * Configure the autoloader
94 * In most cases, $options should be either an associative array or
95 * Traversable object.
97 * @param array|Traversable $options
98 * @return ModuleAutoloader
100 public function setOptions($options)
102 $this->registerPaths($options);
103 return $this;
107 * Retrieves the class map for all loaded modules.
109 * @return array
111 public function getModuleClassMap()
113 return $this->moduleClassMap;
117 * Sets the class map used to speed up the module autoloading.
119 * @param array $classmap
120 * @return ModuleAutoloader
122 public function setModuleClassMap(array $classmap)
124 $this->moduleClassMap = $classmap;
126 return $this;
130 * Autoload a class
132 * @param $class
133 * @return mixed
134 * False [if unable to load $class]
135 * get_class($class) [if $class is successfully loaded]
137 public function autoload($class)
139 // Limit scope of this autoloader
140 if (substr($class, -7) !== '\Module') {
141 return false;
144 if (isset($this->moduleClassMap[$class])) {
145 require_once $this->moduleClassMap[$class];
146 return $class;
149 $moduleName = substr($class, 0, -7);
150 if (isset($this->explicitPaths[$moduleName])) {
151 $classLoaded = $this->loadModuleFromDir($this->explicitPaths[$moduleName], $class);
152 if ($classLoaded) {
153 return $classLoaded;
156 $classLoaded = $this->loadModuleFromPhar($this->explicitPaths[$moduleName], $class);
157 if ($classLoaded) {
158 return $classLoaded;
162 if (count($this->namespacedPaths) >= 1) {
163 foreach ($this->namespacedPaths as $namespace => $path) {
164 if (false === strpos($moduleName, $namespace)) {
165 continue;
168 $moduleNameBuffer = str_replace($namespace . "\\", "", $moduleName);
169 $path .= DIRECTORY_SEPARATOR . $moduleNameBuffer . DIRECTORY_SEPARATOR;
171 $classLoaded = $this->loadModuleFromDir($path, $class);
172 if ($classLoaded) {
173 return $classLoaded;
176 $classLoaded = $this->loadModuleFromPhar($path, $class);
177 if ($classLoaded) {
178 return $classLoaded;
183 $moduleClassPath = str_replace('\\', DIRECTORY_SEPARATOR, $moduleName);
185 $pharSuffixPattern = null;
186 if ($this->pharExtensions) {
187 $pharSuffixPattern = '(' . implode('|', array_map('preg_quote', $this->pharExtensions)) . ')';
190 foreach ($this->paths as $path) {
191 $path = $path . $moduleClassPath;
193 if ($path == '.' || substr($path, 0, 2) == './' || substr($path, 0, 2) == '.\\') {
194 if (! $basePath = $this->pharBasePath) {
195 $basePath = realpath('.');
198 if (false === $basePath) {
199 $basePath = getcwd();
202 $path = rtrim($basePath, '\/\\') . substr($path, 1);
205 $classLoaded = $this->loadModuleFromDir($path, $class);
206 if ($classLoaded) {
207 return $classLoaded;
210 // No directory with Module.php, searching for phars
211 if ($pharSuffixPattern) {
212 foreach (new GlobIterator($path . '.*') as $entry) {
213 if ($entry->isDir()) {
214 continue;
217 if (! preg_match('#.+\.' . $pharSuffixPattern . '$#', $entry->getPathname())) {
218 continue;
221 $classLoaded = $this->loadModuleFromPhar($entry->getPathname(), $class);
222 if ($classLoaded) {
223 return $classLoaded;
229 return false;
233 * loadModuleFromDir
235 * @param string $dirPath
236 * @param string $class
237 * @return mixed
238 * False [if unable to load $class]
239 * get_class($class) [if $class is successfully loaded]
241 protected function loadModuleFromDir($dirPath, $class)
243 $modulePath = $dirPath . '/Module.php';
244 if (substr($modulePath, 0, 7) === 'phar://') {
245 $file = new PharFileInfo($modulePath);
246 } else {
247 $file = new SplFileInfo($modulePath);
250 if (($file->isReadable() && $file->isFile())) {
251 // Found directory with Module.php in it
252 $absModulePath = $this->pharBasePath ? (string) $file : $file->getRealPath();
253 require_once $absModulePath;
254 if (class_exists($class)) {
255 $this->moduleClassMap[$class] = $absModulePath;
256 return $class;
259 return false;
263 * loadModuleFromPhar
265 * @param string $pharPath
266 * @param string $class
267 * @return mixed
268 * False [if unable to load $class]
269 * get_class($class) [if $class is successfully loaded]
271 protected function loadModuleFromPhar($pharPath, $class)
273 $pharPath = static::normalizePath($pharPath, false);
274 $file = new SplFileInfo($pharPath);
275 if (! $file->isReadable() || ! $file->isFile()) {
276 return false;
279 $fileRealPath = $file->getRealPath();
281 // Phase 0: Check for executable phar with Module class in stub
282 if (strpos($fileRealPath, '.phar') !== false) {
283 // First see if the stub makes the Module class available
284 require_once $fileRealPath;
285 if (class_exists($class)) {
286 $this->moduleClassMap[$class] = $fileRealPath;
287 return $class;
291 // Phase 1: Not executable phar, no stub, or stub did not provide Module class; try Module.php directly
292 $moduleClassFile = 'phar://' . $fileRealPath . '/Module.php';
293 $moduleFile = new SplFileInfo($moduleClassFile);
294 if ($moduleFile->isReadable() && $moduleFile->isFile()) {
295 require_once $moduleClassFile;
296 if (class_exists($class)) {
297 $this->moduleClassMap[$class] = $moduleClassFile;
298 return $class;
302 // Phase 2: Check for nested module directory within archive
303 // Checks for /path/to/MyModule.tar/MyModule/Module.php
304 // (shell-integrated zip/tar utilities wrap directories like this)
305 $pharBaseName = $this->pharFileToModuleName($fileRealPath);
306 $moduleClassFile = 'phar://' . $fileRealPath . '/' . $pharBaseName . '/Module.php';
307 $moduleFile = new SplFileInfo($moduleClassFile);
308 if ($moduleFile->isReadable() && $moduleFile->isFile()) {
309 require_once $moduleClassFile;
310 if (class_exists($class)) {
311 $this->moduleClassMap[$class] = $moduleClassFile;
312 return $class;
316 return false;
320 * Register the autoloader with spl_autoload registry
322 * @return void
324 public function register()
326 spl_autoload_register([$this, 'autoload']);
330 * Unregister the autoloader with spl_autoload registry
332 * @return void
334 public function unregister()
336 spl_autoload_unregister([$this, 'autoload']);
340 * registerPaths
342 * @param array|Traversable $paths
343 * @throws \InvalidArgumentException
344 * @return ModuleAutoloader
346 public function registerPaths($paths)
348 if (! is_array($paths) && ! $paths instanceof Traversable) {
349 require_once __DIR__ . '/Exception/InvalidArgumentException.php';
350 throw new Exception\InvalidArgumentException(
351 'Parameter to \\Zend\\Loader\\ModuleAutoloader\'s '
352 . 'registerPaths method must be an array or '
353 . 'implement the Traversable interface'
357 foreach ($paths as $module => $path) {
358 if (is_string($module)) {
359 $this->registerPath($path, $module);
360 } else {
361 $this->registerPath($path);
365 return $this;
369 * registerPath
371 * @param string $path
372 * @param bool|string $moduleName
373 * @throws \InvalidArgumentException
374 * @return ModuleAutoloader
376 public function registerPath($path, $moduleName = false)
378 if (! is_string($path)) {
379 require_once __DIR__ . '/Exception/InvalidArgumentException.php';
380 throw new Exception\InvalidArgumentException(sprintf(
381 'Invalid path provided; must be a string, received %s',
382 gettype($path)
385 if ($moduleName) {
386 if (in_array(substr($moduleName, -2), ['\\*', '\\%'])) {
387 $this->namespacedPaths[substr($moduleName, 0, -2)] = static::normalizePath($path);
388 } else {
389 $this->explicitPaths[$moduleName] = static::normalizePath($path);
391 } else {
392 $this->paths[] = static::normalizePath($path);
394 return $this;
398 * getPaths
400 * This is primarily for unit testing, but could have other uses.
402 * @return array
404 public function getPaths()
406 return $this->paths;
410 * Returns the base module name from the path to a phar
412 * @param string $pharPath
413 * @return string
415 protected function pharFileToModuleName($pharPath)
417 do {
418 $pathinfo = pathinfo($pharPath);
419 $pharPath = $pathinfo['filename'];
420 } while (isset($pathinfo['extension']));
421 return $pathinfo['filename'];
425 * Normalize a path for insertion in the stack
427 * @param string $path
428 * @param bool $trailingSlash Whether trailing slash should be included
429 * @return string
431 public static function normalizePath($path, $trailingSlash = true)
433 $path = rtrim($path, '/');
434 $path = rtrim($path, '\\');
435 if ($trailingSlash) {
436 $path .= DIRECTORY_SEPARATOR;
438 return $path;