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