Composer 的autoload 实现
一、全局安装
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
https://docs.phpcomposer.com/00-intro.html
二、代码
require_once './vendor/autoload.php'; 一、autoload.php 加载 composer/autoload_real.php 调用 autoload_real.php 下的 getLoader() 方法 二、autoload_real.php getLoader() 方法 创建 composer\ClassLoader.php 的对象 $loader 1、静态调用 $useStaticLoader 加载 composer\autoload_static.php 回调 autoload_static.php 下的 getInitializer($loader) 方法 getInitializer() 方法 通过匿名函数绑定,为$loader 对象下的私有属性赋值 $prefixLengthsPsr4, $prefixDirsPsr4, $prefixDirsPsr4, $classMap 注册自动加载 $loader->register(true); spl_autoload_register([$this, 'loadClass', true, $prepend]); function loadClass($class) { if ($file = $this->findFile($class)) { includeFile($file) return true; } } // ClassLoader.php 下的方法 public function findFile($class) { // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; } if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { return false; } if (null !== $this->apcuPrefix) { $file = apcu_fetch($this->apcuPrefix.$class, $hit); if ($hit) { return $file; } } $file = $this->findFileWithExtension($class, '.php'); // Search for Hack files if we are running on HHVM if (false === $file && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } if (null !== $this->apcuPrefix) { apcu_add($this->apcuPrefix.$class, $file); } if (false === $file) { // Remember that this class does not exist. $this->missingClasses[$class] = true; } return $file; } function includeFile($file) { include $file; } private function findFileWithExtension($class, $ext) { // PSR-4 lookup $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { $subPath = $class; while (false !== $lastPos = strrpos($subPath, '\\')) { $subPath = substr($subPath, 0, $lastPos); $search = $subPath . '\\'; if (isset($this->prefixDirsPsr4[$search])) { $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); foreach ($this->prefixDirsPsr4[$search] as $dir) { if (file_exists($file = $dir . $pathEnd)) { return $file; } } } } } // PSR-4 fallback dirs foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } // PSR-0 lookup if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; } if (isset($this->prefixesPsr0[$first])) { foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } } } } // PSR-0 fallback dirs foreach ($this->fallbackDirsPsr0 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } // PSR-0 include paths. if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } return false; } 2、非静态调用 略
call_user_func(\Composer\Autoload\ComposerStaticInitd16dd22c4a12ecb92b3a245dff71e1fc::getInitializer($loader)); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInitd16dd22c4a12ecb92b3a245dff71e1fc::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInitd16dd22c4a12ecb92b3a245dff71e1fc::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInitd16dd22c4a12ecb92b3a245dff71e1fc::$prefixesPsr0; $loader->classMap = ComposerStaticInitd16dd22c4a12ecb92b3a245dff71e1fc::$classMap; }, null, ClassLoader::class); } 解读: call_user_func+Closure::bind()的模式,其实这里我们也可以完全按照常规的做法,引入一个配置文件的方式来实现,Closure::bind()返回一个Closure对象,即一个闭包函数,其中一共有三个参数: closure:需要绑定的匿名函数 newthis:需要绑定到匿名函数的对象,或者 NULL 创建未绑定的闭包 newscope:想要绑定给闭包的类作用域,或者 'static' 表示不改变。如果传入一个对象,则使用这个对象的类型名。 第一个参数好理解,第二个和第三个可以配合起来使用,当我们需要在闭包函数中使用$this的时候,我们需要给这个闭包绑定一个对象,如果闭包中使用了$this,那么newthis需要指定一个对应的对象;如果修改的属性是protected或者private,那么第三个参数不能忽略,指定为这个类。 例1: class A{ public $name; protected $age; } $a = new A(); $new_a = Closure::bind(function(){ $this->name = 'nine'; } , null); $new_a(); var_dump($new_a , $a); 此时会报错Using $this when not in object context in ...,说明这个上下文并没有指定$this。 例2: class A{ public $name; protected $age; } $a = new A(); $new_a = Closure::bind(function(){ $this->name = 'nine'; $this->age = 10; } , $a); $new_a(); var_dump($new_a , $a); 此时会报错Cannot access protected property A::$age in...,属性是受保护的。 例3: class A{ public $name; protected $age; } $a = new A(); $new_a = Closure::bind(function(){ $this->name = 'nine'; $this->age = 10; } , $a , A::class); $new_a(); var_dump($new_a , $a); 设置成功。
分情破爱始乱弃,流落天涯思别离。
如花似玉负情意,影如白昼暗自迷。
随风浮沉千叶落,行色匆匆鬓已稀。