composer源码简单分析(一)
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(Composer\Autoload\ClassLoader)
return ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a::getLoader();
getLoader方法
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
// 单例
if (null !== self::$loader) {
return self::$loader;
} // spl_autoload_register方法第一个参数为用户自定义的自动加载器,为一个callable,接收的参数为要加载类名
// 如self::$loader = $loader = new \Composer\Autoload\ClassLoader(); 此脚本中并没有ClassLoader这个类,由于注册了自动加载器loadClassLoader回调,
// 此方法就会接收到Composer\Autoload\ClassLoader这个字符串 手动require到本文件中 // 第二个参数true表示注册加载器失败时抛出异常
// 第三个参数true表示 将此自动加载器放到自动加载队列的首部,laravel中还使用此函数进行了类别名注册
spl_autoload_register(array('ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a', 'loadClassLoader'), true, true);
// 给loader赋值
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
// 卸载注册的自动加载回调
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(\Composer\Autoload\ComposerStaticInit251f259405d38ea713d5c2b4f861292a::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 = Composer\Autoload\ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$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); // Illuminate\Foundation\Application
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
// 处理文件名
// Illuminate\Foundation\Application 假设路径映射中不存在此类名
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
// 拿到文件名的第一个字母 依然去找映射关系
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class; // Illuminate\Foundation\Application
// 通过此步骤拿到类名的所有层级的命名空间 Illuminate\Foundation
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos); // Illuminate\Foundation
$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示例讲解
composer源码简单分析(一)的更多相关文章
- FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分
===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...
- FFmpeg源码简单分析:libswscale的sws_scale()
===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...
- Django-session中间件源码简单分析
Django-session中间件源码简单分析 settings里有关中间件的配置 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddlew ...
- FFmpeg源码简单分析:结构体成员管理系统-AVOption
===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...
- negroni-gzip源码简单分析解读
negroni-gzip源码简单分析解读 这是一个为Negroni设计的gzip压缩处理中间件,需要用到已有的compress中的gzip,阅读了不长的源码之后,总结了一些关键要点和注意点. 检查是否 ...
- FFmpeg的HEVC解码器源码简单分析:概述
===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...
- FFmpeg的HEVC解码器源码简单分析:解码器主干部分
===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...
- urllib源码简单分析
对下面这段代码做分析 import urllib params = urllib.urlencode({'wd': 'python'}) f = urllib.urlopen("http:/ ...
- CardboardSDK-iOS 源码简单分析
该项目地址: 地址 克隆地址为 https://github.com/rsanchezsaez/CardboardSDK-iOS.git 目前如果想在iOS设备上实现双目VR的功能,Google 已经 ...
随机推荐
- CF R 635 div2 1337D Xenia and Colorful Gems 贪心 二分 双指针
LINK:Xenia and Colorful Gems 考试的时候没想到一个很好的做法. 赛后也有一个想法. 可以考虑答案的样子 x,y,z 可以发现 一共有 x<=y<=z,z< ...
- luogu P3264 [JLOI2015]管道连接
LINK:管道连接 一张无向图 有P个关键点 其中有K个集合 各个集合要在图中形成联通块 边有边权 求最小代价. 其实还是生成树问题 某个点要和某个点要在生成树中 类似这个意思. 可以发现 是斯坦纳树 ...
- Latex—参考文献
在写文章的最后最让我头疼的就是参考文献的问题了.网上的资料也有很多,这里整合了很多资料得出了一个用bib文件的方法. 1. 显示确定参考文献(一句没什么用的废话). 2. 利用谷歌学术(镜像),如 ...
- Python编程初学者指南PDF高清电子书免费下载|百度云盘
百度云盘:Python编程初学者指南PDF高清电子书免费下载 提取码:bftd 内容简介 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.Python可以用于很多的领域,从科学计算 ...
- 033_go语言中的打点器
代码演示 package main import "fmt" import "time" func main() { ticker := time.NewTic ...
- Linux安装python 3
方法: 1.官网下载地址:http://www.python.org/download/ 2.rz -y 上传到Linux 解压tar -xvzf Python-3.5.1.tgz 进入目录 ...
- C#LeetCode刷题-极小化极大
极小化极大篇 # 题名 刷题 通过率 难度 375 猜数字大小 II 23.4% 中等 464 我能赢吗 25.5% 中等 486 预测赢家 40.4% 中等 843 猜猜这个单词 2 ...
- C#开发笔记之02-什么时候使用OnXXX方法,这种命名是什么意思?
C#开发笔记概述 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/958 访问. 你也许经常会看到别人写的代码里有OnXX ...
- 滴滴推理引擎IFX:千万规模设备下AI部署实践
桔妹导读:「滴滴技术」将于本月开始,联合各技术团队为大家带来精彩分享.你想了解的技术干货,深度专访,团队及招聘将于每周三与你准时见面.本月为「滴滴云平台事业群分享月」,在今天的内容中,云平台事业群-机 ...
- Butterfly侧边栏引入一言
此教程涉及修改源码 背景 在修改每页显示7篇文章后,出现了这种情况. 这是完美主义(强迫症)的我所不能忍受的,有什么可以占据这里的呢?{% btn 'https://hitokoto.cn/',一言, ...