123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- <?php
- namespace Qii\Autoloader;
- use Qii\Exceptions\CallUndefinedClass;
- use Qii\Exceptions\ClassNotFound;
- use Qii\Exceptions\FileNotFound;
- /**
- * Psr4 规范
- *
- */
- class Psr4
- {
- /**
- * 将查找过的文件放入缓存
- */
- protected static $cachedFiles = array();
- /**
- * 是否使用namespace
- */
- protected $useNamespace = array();
- /**
- * 添加namespace前缀对应的目录,只要是以这个前缀开头的文件都在指定目录中去查找
- * 前缀可以对应多个目录,找的时候会去遍历数组
- * @var array
- */
- protected $prefixes = array();
- /**
- * 当前class的初始化
- */
- private static $_instance = null;
- /**
- * @var APP_LOAD_PREFIX 保存类到以APP_LOAD_PREFIX开头的key中
- */
- const APP_LOAD_PREFIX = '__qii_psr4_instance';
- /**
- * @var array $_loadedClass 保存加载过的类
- */
- protected static $_loadedClass = array();
- protected static $_loadedClassParams = array();
- /**
- * @var array $_realpath 将转换后的路径存放到此变量中
- */
- protected static $_realpath = array();
- /**
- * 最后一次没有加载到文件的错误路径
- * @var array $lastErrorLoadedFile
- */
- protected static $lastErrorLoadedFile = array();
- /**
- * 注册自动加载类
- *
- */
- private function __construct()
- {
- }
- /**
- * 单例模式
- */
- public static function getInstance()
- {
- if (self::$_instance == null) {
- self::$_instance = new self();
- }
- return self::$_instance;
- }
- /**
- * 注册自动加载类
- *
- * @return $this
- */
- public function register()
- {
- spl_autoload_register(array($this, 'loadFileByClass'));
- return $this;
- }
- /**
- * Setting is use namespaces for class
- *
- * @param array $arr
- * @return object $this
- */
- public function setUseNamespaces($arr)
- {
- if(!is_array($arr))
- {
- return $this;
- }
- foreach($arr as $namespace)
- {
- call_user_func_array(array($this, 'setUseNamespace'), $namespace);
- }
- return $this;
- }
- /**
- * Setting is use namespace for class
- *
- * @param string $prefix 以prefix前缀开头的使用namespace
- * @param bool $useNamespace
- * @return object $this
- */
- public function setUseNamespace($prefix, $useNamespace = true)
- {
- $this->useNamespace[$prefix] = $useNamespace;
- return $this;
- }
- /**
- * Adds a base directory for namespace prefix
- * @param $arr
- * @return $this
- */
- public function addNamespaces($arr)
- {
- if(!is_array($arr))
- {
- return $this;
- }
- foreach ($arr as $namespace)
- {
- call_user_func_array(array($this, 'addNamespace'), $namespace);
- }
- return $this;
- }
- /**
- * Adds a base directory for a namespace prefix.
- *
- * @param string $prefix The namespace prefix.
- * @param string $baseDir A base directory for class files in the
- * namespace.
- * @param bool $prepend If true, prepend the base directory to the stack
- * instead of appending it; this causes it to be searched first rather
- * than last.
- * @return void
- */
- public function addNamespace($prefix, $baseDir, $prepend = false)
- {
- // normalize namespace prefix
- $prefix = trim($prefix, '\\') . '\\';
- // normalize the base directory with a trailing separator
- $baseDir = rtrim($baseDir, '/') . DIRECTORY_SEPARATOR;
- $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
- // initialize the namespace prefix array
- if (isset($this->prefixes[$prefix]) === false) {
- $this->prefixes[$prefix] = array();
- }
- if (in_array($baseDir, $this->prefixes[$prefix])) {
- return $this;
- }
- // retain the base directory for the namespace prefix
- if ($prepend) {
- array_unshift($this->prefixes[$prefix], $baseDir);
- } else {
- $this->prefixes[$prefix][] = $baseDir;
- }
- return $this;
- }
- /**
- * 移除某一个namespace下的指定路径
- * @param string $prefix 前缀
- * @param string $baseDir 路径
- * @return array
- */
- public function removeNameSpace($prefix, $baseDir)
- {
- // normalize namespace prefix
- $prefix = trim($prefix, '\\') . '\\';
- // normalize the base directory with a trailing separator
- $baseDir = rtrim($baseDir, '/') . DIRECTORY_SEPARATOR;
- $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
- // initialize the namespace prefix array
- if (isset($this->prefixes[$prefix]) === false) {
- return false;
- }
- foreach ($this->prefixes[$prefix] AS $key => $dir) {
- if ($dir == $baseDir) unset($this->prefixes[$prefix][$key]);
- }
- return $this->prefixes;
- }
- /**
- * 返回namespace路径
- */
- public function getNamespace($prefix)
- {
- // normalize namespace prefix
- $prefix = trim($prefix, '\\') . '\\';
- if (isset($this->prefixes[$prefix])) return $this->prefixes[$prefix];
- return '';
- }
- /**
- * 通过文件名返回路径
- * @param string $fileName 文件名
- * @return string
- */
- public function getFileByPrefix($fileName)
- {
- $dirs = $this->getRealFolderPath($fileName);
- foreach ($dirs as $baseDir) {
- if (is_file($baseDir . DS . $fileName)) {
- return $baseDir . DS . $fileName;
- }
- }
- return $fileName;
- }
- /**
- * 获取指定文件夹路径
- * @param string $folder 路径
- * @return string 路径
- */
- public function getFolderByPrefix($folder)
- {
- $dirs = $this->getRealFolderPath($folder);
- foreach ($dirs as $baseDir) {
- return $baseDir . DS . $folder;
- }
- return $folder;
- }
- /**
- * 获取文件或文件夹的所有可能的路径
- *
- * @param string $fileOrFolder 文件/文件夹
- * @return array|mixed
- */
- public function getRealFolderPath(string $fileOrFolder)
- {
- $fileName = str_replace(array('/', '\\'), DS, $fileOrFolder);
- $prefixes = explode(DS, $fileName, 2);
- $dirs = isset($this->prefixes['workspace\\']) ? $this->prefixes['workspace\\'] : array();
- if (count($prefixes) == 2) {
- if (isset($this->prefixes[$prefixes[0]])) $dirs = $this->prefixes[$prefixes[0]];
- }
- return $dirs;
- }
- /**
- * 从Map中获取文件
- */
- public function searchMappedFile($class)
- {
- $prefix = $class;
- // work backwards through the namespace names of the fully-qualified
- // class name to find a mapped file name
- while (false !== $pos = strrpos($prefix, '\\')) {
- // retain the trailing namespace separator in the prefix
- $prefix = substr($class, 0, $pos + 1);
- // the rest is the relative class name
- $relativeClass = substr($class, $pos + 1);
- // try to load a mapped file for the prefix and relative class
- $mappedFile = $this->loadMappedFile($prefix, $relativeClass);
- if ($mappedFile) {
- return $mappedFile;
- }
- $prefix = rtrim($prefix, '\\');
- };
- //如果没有找到就在workspace中去找对应的文件 额外添加的方法
- $mappedFile = $this->loadMappedFile('workspace\\', $class);
- if ($mappedFile) {
- return $mappedFile;
- }
- return false;
- }
- /**
- * 通过类名加载文件
- * @param string $class 类名
- * @return string 文件路径
- */
- public function loadFileByClass($class)
- {
- // the current namespace prefix
- //replace "_" to "\" use common method to load class
- $class = str_replace("_", "\\", $class);
- if(!$this->searchMappedFile($class))
- {
- $notLoaded = isset(self::$lastErrorLoadedFile[$class]) ? self::$lastErrorLoadedFile[$class] : self::getClassName($class);
- throw new FileNotFound($notLoaded, 404);
- }
- }
- /**
- * loadClass返回真正的类名
- *
- * @param string $class 类名
- */
- public function getClassName($class)
- {
- // the current namespace prefix
- //replace "_" to "\" use common method to load class
- $class = str_replace("_", "\\", $class);
- $class = str_replace("/", "\\", $class);
- if($this->searchMappedFile($class))
- {
- return $class;
- }
- return str_replace('\\', '_', $class);
- }
- /**
- * Loads the class file for a given class name.
- *
- * @param string $class The fully-qualified class name.
- * @return mixed The mapped file name on success, or boolean false on
- * failure.
- */
- public function loadClass($class)
- {
- $args = func_get_args();
- if (count($args) == 0) {
- return;
- }
- //去掉第一个斜杠
- $class = $args[0];
- $class = preg_replace("/^\\\\/", "", $class);
- $class = $this->getClassName($class);
- if (class_exists($class, false)) {
- return call_user_func_array(array($this, 'instance'), $args);
- }
- if ($this->loadFileByClass($class)) {
- return call_user_func_array(array($this, 'instance'), $args);
- }
- throw new ClassNotFound(\Qii::i(1103, $class), __LINE__);
- }
- /**
- * 调用静态的方法
- * @param string $class 类名
- * @param string $method 方法名
- * @return mixed
- */
- public static function loadStatic($class, $method)
- {
- $args = func_get_args();
- $class = \Qii\Autoloader\Psr4::getInstance()->getClassName(array_shift($args));
- $method = array_shift($args);
- return call_user_func_array(array($class, $method), $args);
- }
- /**
- * 获取文件的绝对路径
- * @param string $path
- * @param bool $exists 是否使用realpath
- * @return string 真实路径
- */
- public static function realpath($path)
- {
- if (isset(self::$_realpath[$path])) return self::$_realpath[$path];
- $drive = '';
- if (OS === 'WIN') {
- $path = preg_replace('/[\\\\\/]/', DIRECTORY_SEPARATOR, $path);
- if (preg_match('/(phar\:\\\\|[a-zA-Z]\:)(.*)/', $path, $matches)) {
- list(, $drive, $path) = $matches;
- } else {
- $cwd = getcwd();
- $drive = substr($cwd, 0, 2);
- if (substr($path, 0, 1) != DIRECTORY_SEPARATOR) {
- $path = substr($cwd, 3) . DIRECTORY_SEPARATOR . $path;
- }
- }
- } elseif (substr($path, 0, 1) != DIRECTORY_SEPARATOR) {
- $path = getcwd() . DIRECTORY_SEPARATOR . $path;
- }
- $stack = array();
- $parts = explode(DIRECTORY_SEPARATOR, $path);
- foreach ($parts as $dir) {
- if (strlen($dir) && $dir !== '.') {
- if ($dir == '..') {
- array_pop($stack);
- } else {
- array_push($stack, $dir);
- }
- }
- }
- $realPath = str_replace(DIRECTORY_SEPARATOR, '/', $drive . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $stack));
- self::$_realpath[$path] = $realPath;
- return $realPath;
- }
- /**
- * Load the mapped file for a namespace prefix and relative class.
- *
- * @param string $prefix The namespace prefix.
- * @param string $relativeClass The relative class name.
- * @return mixed Boolean false if no mapped file can be loaded, or the
- * name of the mapped file that was loaded.
- */
- protected function loadMappedFile($prefix, $relativeClass)
- {
- if (isset(self::$cachedFiles[$prefix . '_' . $relativeClass])) return self::$cachedFiles[$prefix . '_' . $relativeClass];
- // are there any base directories for this namespace prefix?
- if (isset($this->prefixes[$prefix]) === false) {
- //if there any base directories , add self to prefix
- $this->addNamespace($prefix, $prefix);
- //return false;
- }
- $prefix = trim($prefix, '\\') . '\\';
- $file = '';
- // look through base directories for this namespace prefix
- foreach ($this->prefixes[$prefix] as $baseDir) {
- $path = $baseDir . $relativeClass. '.php';
- $file = $this->replaceDash($path);
- // if the mapped file exists, require it
- if ($this->requireFile($file)) {
- self::$cachedFiles[$prefix . '_' . $relativeClass] = $file;
- return $file;
- }
- }
- self::$lastErrorLoadedFile[$relativeClass] = $file;
- // never found it
- return false;
- }
- /**
- * 替换路径中多余的
- * @param string $path 路径
- * @return mixed
- */
- protected function replaceDash($path)
- {
- $path = str_replace('\\', DS, $path);
- $path = str_replace('\\\\', DS, $path);
- $path = str_replace('/', DS, $path);
- $path = str_replace('//', DS, $path);
- return $path;
- }
- /**
- * If a file exists, require it from the file system.
- *
- * @param string $file The file to require.
- * @return bool True if the file exists, false if not.
- */
- protected function requireFile($file)
- {
- return \Qii\Autoloader\Import::requires($file);
- }
- /**
- * instance class
- * @param string $class
- * @return object
- */
- public function instance()
- {
- $args = func_get_args();
- $class = array_shift($args);
- $className = $this->getClassName($class);
- //如果实例化的参数发生变化,就重新实例化
- $paramsHash = md5(print_r($args, true));
- if (isset(self::$_loadedClass[$className]) && isset(self::$_loadedClassParams[$className])
- && self::$_loadedClassParams[$className] == $paramsHash) return self::$_loadedClass[$className];
-
- self::$_loadedClassParams[$className] = $paramsHash;
- if (!class_exists($className, false)) {
- throw new CallUndefinedClass(\Qii::i('1105', $className), __LINE__);
- }
- $refClass = new \ReflectionClass($className);
- self::$_loadedClass[$className] = $instance = $refClass->newInstanceArgs($args);
- //如果有_initialize方法就自动调用_initialize方法,并将参数传递给_initialize方法
- if ($refClass->hasMethod('_initialize')) {
- call_user_func_array(array($instance, '_initialize'), $args);
- }
- return $instance;
- }
- }
|