composer分析(一)
本文内容
- 基于PSR-4规范的自动加载 请结合文档和下面的代码注释
- spl_autoload_register
- php闭包Closure简单用法(大体使用情景: 生成回调提供使用, 使用闭包绑定类中的成员属性和方法)
-
框架引入composer自动加载器
require __DIR__ . '/../vendor/autoload.php';
-
autoload.php文件内容
<?php // autoload.php @generated by Composer require_once __DIR__ . '/composer/autoload_real.php'; // 返回自动加载器 object(ComposerAutoloadClassLoader) return ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a::getLoader();
-
getLoader方法
/** * @return ComposerAutoloadClassLoader */ public static function getLoader() { // 单例 if (null !== self::$loader) { return self::$loader; } // spl_autoload_register方法第一个参数为用户自定义的自动加载器,为一个callable,接收的参数为要加载类名 // 如self::$loader = $loader = new ComposerAutoloadClassLoader(); 此脚本中并没有ClassLoader这个类,由于注册了自动加载器loadClassLoader回调, // 此方法就会接收到ComposerAutoloadClassLoader这个字符串 手动require到本文件中 // 第二个参数true表示注册加载器失败时抛出异常 // 第三个参数true表示 将此自动加载器放到自动加载队列的首部,laravel中还使用此函数进行了类别名注册 spl_autoload_register(array('ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a', 'loadClassLoader'), true, true); // 给loader赋值 self::$loader = $loader = new ComposerAutoloadClassLoader(); // 卸载注册的自动加载回调 spl_autoload_unregister(array('ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a', 'loadClassLoader')); // 正常情况使用高版本php 此值恒为true $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { // require此静态加载文件 包含了大量供PSR-4使用的映射关系 后面会讲解composer包下各文件的用处 require_once __DIR__ . '/autoload_static.php'; // 重点在此回调的调用!!! // 调用返回的闭包 注册映射关系到loader中 call_user_func(ComposerAutoloadComposerStaticInit251f259405d38ea713d5c2b4f861292a::getInitializer($loader)); } else { // 如果$useStaticLoader为false 调用loader的set方法 手动注册映射关系到loader中 供后续加载时直接查找require文件 $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } } // 核心loader中存在映射关系后 注册真正的自动加载器 loadClass方法 $loader->register(true); if ($useStaticLoader) { // 找到composer要自动加载的全局函数文件 $includeFiles = ComposerAutoloadComposerStaticInit251f259405d38ea713d5c2b4f861292a::$files; } else { // 依然是找到composer提供的全局函数 和上面的功能一致 $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { composerRequire251f259405d38ea713d5c2b4f861292a($fileIdentifier, $file); } return $loader; }
-
autoload_static.php
public static function getInitializer(ClassLoader $loader) { // Closure bind方法返回的是一个闭包 return Closure::bind(function () use ($loader) { // 绑定映射关系到loader中 $loader->prefixLengthsPsr4 = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$prefixesPsr0; $loader->classMap = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$classMap; }, null, ClassLoader::class); }
-
ClassLoader.php
/** * Registers this instance as an autoloader. * * @param bool $prepend Whether to prepend the autoloader or not */ public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); } /** * Loads the given class or interface. * * @param string $class The name of the class * @return bool|null True if loaded, null otherwise */ public function loadClass($class) { if ($file = $this->findFile($class)) { // 拿到文件绝对路径 includeFile($file); return true; } } /** * Scope isolated include. * * Prevents access to $this/self from included files. */ function includeFile($file) { // 加载文件 include $file; } /** * Finds the path to the file where the class is defined. * * @param string $class The name of the class * * @return string|false The path if found, false otherwise */ public function findFile($class) { // class map lookup // classmap中保存的是类名和其绝对路径的映射关系 if (isset($this->classMap[$class])) { // var_dump($class); // IlluminateFoundationApplication 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; } private function findFileWithExtension($class, $ext) { // PSR-4 lookup // 处理文件名 // IlluminateFoundationApplication 假设路径映射中不存在此类名 $logicalPathPsr4 = strtr($class, '\', DIRECTORY_SEPARATOR) . $ext; // 拿到文件名的第一个字母 依然去找映射关系 $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { $subPath = $class; // IlluminateFoundationApplication // 通过此步骤拿到类名的所有层级的命名空间 IlluminateFoundation while (false !== $lastPos = strrpos($subPath, '\')) { $subPath = substr($subPath, 0, $lastPos); // IlluminateFoundation $search = $subPath . '\'; // prefixDirsPsr4顶级命名空间前缀 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; } } ... return false; }
Closure使用简单示例
<?php
$a = function () {
echo '123';
var_dump($this);
};
// var_dump($a);
// $a(); 报错
$abc = function () {
$this->privateVar = 456;
echo $this->privateVar, PHP_EOL;
};
$cba = function () {
echo $this->privateVar, PHP_EOL;
};
class MyClousre
{
public $callback;
private $privateVar = 123;
// 在类内部可以直接使用 $this
public function zbc(callable $callable)
{
// var_dump($callable);
$this->callback = $callable->bindTo($this, __CLASS__); // bindTo的作用是 将闭包绑定到指定的类 打通内部状态 是闭包中可以访问类的成员
// var_dump($callable);
}
public function zbcc()
{
call_user_func($this->callback);
($this->callback)();
}
}
$mc = new MyClousre();
$mc->zbc($abc);
// $mc->zbc([new Test(), 'aaa']); // 测试结果是 bindTo只能是用在Closure对象上
$mc->zbcc();
// class Test
// {
// public static function aaa()
// {
// echo $this->privateVar, PHP_EOL;
// }
// }
// 可以达到的效果 在闭包中使用对象中的各种成员
// 在类的外部 不能直接绑定$this 要先new
$cba = $cba->bindTo(new MyClousre(), MyClousre::class);
$cba();
// bind的使用
$foo = function () {
echo $this->zbc() . PHP_EOL;
};
class Haiyoushui
{
private function zbc()
{
return 'bayehelie';
}
}
$bar = Closure::bind($foo, new Haiyoushui(), Haiyoushui::class);
$bar();
发现错误欢迎指正,感谢~~
下期预告:composer下的各个文件内容的生成和意义及PSR-4示例讲解