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 已经 ...
随机推荐
- Spring 自动装配机制
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w ...
- ipa包如何打包?ios打包ipa的四种方法分享
今天带来的内容是ios打包ipa的四种方法.总结一下,目前.app包转为.ipa包的方法有以下几种,下面一起来看看吧! 1.Apple推荐的方式,即实用xcode的archive功能 Xco ...
- python2.5项目:个税计算器程序
#开发个税计算器:应纳税所得额=工资收入金额-各项社会保险费-起征点(5000元)应纳税额=应纳税所得额*税率—速算扣除数(税率参考图片)m=float(input("请输入你的税前工资:& ...
- 小伙子自从学会用Python爬取岛国“动作”电影,身体一天不如一天
在互联网的世界里,正确的使用VPN看看外面的世界,多了解了解世界的发展.肉身翻墙后,感受一下外面的肮脏世界.墙内的朋友叫苦不迭,由于某些原因,VPN能用的越来越少.上周我的好朋友狗子和我哭诉说自己常用 ...
- ExtremeNet
- C#LeetCode刷题之#34-在排序数组中查找元素的第一个和最后一个位置(Find First and Last Position of Element in Sorted Array)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4970 访问. 给定一个按照升序排列的整数数组 nums,和一个目 ...
- 01从DataGrid中导入到Excel
01网络上有很多导出数据到Excel的方法,我在网上找到了一种比较简单实用的方法(参考了网友的方法) string fileName = ""; Microsoft.Win32.S ...
- 极简 Node.js 入门 - 2.3 process
极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...
- Solon Ioc 的注解对比Spring及JSR330
注解对比 Solon 1.0.10 Spring JSR 330 @XInject * @Autowired @Inject 字段或参数注入 @XBean * @Component @Named Be ...
- 三分钟秒懂BIO/NIO/AIO区别?
首先来举个例子说明吧,假设你想吃一份盖饭: 同步阻塞:你到饭馆点餐,然后在那等着,还要一边喊:好了没啊! 同步非阻塞:在饭馆点完餐,就去遛狗了.不过溜一会儿,就回饭馆喊一声:好了没啊! 异步阻塞:遛狗 ...