composer分析(一)

本文内容

  • 基于PSR-4规范的自动加载 请结合文档和下面的代码注释
  • spl_autoload_register
  • php闭包Closure简单用法(大体使用情景: 生成回调提供使用, 使用闭包绑定类中的成员属性和方法)

  1. 框架引入composer自动加载器

    require __DIR__ . '/../vendor/autoload.php';
  2. autoload.php文件内容

    <?php
    
    // autoload.php @generated by Composer
    
    require_once __DIR__ . '/composer/autoload_real.php';
    
    // 返回自动加载器 object(Composer\Autoload\ClassLoader)
    return ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a::getLoader();
  3. 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;
    }
  4. 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);
    }
  5. 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源码简单分析(一)的更多相关文章

  1. FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  2. FFmpeg源码简单分析:libswscale的sws_scale()

    ===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...

  3. Django-session中间件源码简单分析

    Django-session中间件源码简单分析 settings里有关中间件的配置 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddlew ...

  4. FFmpeg源码简单分析:结构体成员管理系统-AVOption

    ===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...

  5. negroni-gzip源码简单分析解读

    negroni-gzip源码简单分析解读 这是一个为Negroni设计的gzip压缩处理中间件,需要用到已有的compress中的gzip,阅读了不长的源码之后,总结了一些关键要点和注意点. 检查是否 ...

  6. FFmpeg的HEVC解码器源码简单分析:概述

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  7. FFmpeg的HEVC解码器源码简单分析:解码器主干部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  8. urllib源码简单分析

    对下面这段代码做分析 import urllib params = urllib.urlencode({'wd': 'python'}) f = urllib.urlopen("http:/ ...

  9. CardboardSDK-iOS 源码简单分析

    该项目地址: 地址 克隆地址为 https://github.com/rsanchezsaez/CardboardSDK-iOS.git 目前如果想在iOS设备上实现双目VR的功能,Google 已经 ...

随机推荐

  1. luogu P6091 原根

    LINK:原根 再复习一下原根 防止考场上要NTT求原根的时候不会求... 这道题要求求出n之内的所有原根 根据原根的定义. 原根指 若x对于模n的阶为phi(n)且\(1\leq x\leq n\) ...

  2. “商家参数格式有误”应用切微信H5支付完美解决方案

    一.业务场景发生 最近在跟一些合作公司作业务对接,在对方的APP中接入我们的H5支付,包括微信和支付宝. 那就开搞,进展顺利,貌似一切都在掌握之中,给到对方一个链接即可调起支付.形如: https:/ ...

  3. [Err] 126 - Incorrect key file for table '/tmp/#sql_1cdc_0.MYI'; try to repair it

    网上给出的原因倾向于MySQL的tmpdir的临时目录磁盘空间不足,如果检查确实时空间不足,请调整量tmpdir和slave_load_tmpdir,然后重启mysql服务.调整的方法是 locate ...

  4. Kaggle-pandas(6)

    Renaming-and-combining 教程 通常,数据会以列名,索引名或我们不满意的其他命名约定提供给我们. 在这种情况下,您将学习如何使用pandas函数将有问题的条目的名称更改为更好的名称 ...

  5. 有关vue中v-if和v-show的区别

    其实这两个都是属于根据条件判断元素是否可见,但是还有有区别的哦! v-show:就是无论什么时候它其实都一直存在页面上也就是会渲染在DOM上,只是你写了条件让它可见或不可见而已,因为它本质是把它的cs ...

  6. 薪资高,福利好,会Python的人就是这么豪横!

    很多人可能会有这样的疑问,数据分析Excel挺强大的,会Excel就行,为什么还要去学python? 是的,Excel和python对于数据分析而言,这两者都只是不同的工具而已. 很多人学习pytho ...

  7. LinuX操作系统基础------>软件的安装方式,chmod权限,vi编辑器的使用

    RPM包管理 -rpm命令管理 RPM包管理 -yum在线管理 文件权限管理 vi编辑器的使用和常用的快捷键 1.RPM包管理: 一种用于互联网下载包的打包及安装工具,RPM包管理分为rpm命令管理和 ...

  8. Linux学习笔记之ubuntu如何在vi中写入中文注释

    点击左边设置system settings,再点击Language Suppotr 点击Remind Me Later 选择ibus 然后关闭,在终端写入ibus-setup,弹出设置框,选择INPU ...

  9. Linux学习笔记之ubuntu安装与配置

    1.打开虚拟机,点击新建虚拟机 2.安装向导 选择自定义安装 点击包含一个空白的硬盘 选择linux操作系统,版本是ubuntu 设置虚拟机的名称,可以自己写,还有保存的位置也可自选 根据自己电脑性能 ...

  10. XCTF-WEB-新手练习区(1-4)笔记

    靶场地址:https://adworld.xctf.org.cn/ 1:view_source X老师让小宁同学查看一个网页的源代码,但小宁同学发现鼠标右键好像不管用了. 原理:使用浏览器F12或者B ...