[关闭]
@Chiang 2020-05-02T17:05:54.000000Z 字数 5683 阅读 849

Composer 源码分析

Composer 2020-05


写在前面(官方基本使用)

https://getcomposer.org/doc/01-basic-usage.md

目的(解决的问题)

在 PHP 开发过程中,如果希望从外部引入一个 class,通常会使用 include 和 require 方法,去把定义这个 class 的文件包含进来。这个在小规模开发的时候,没什么大问题。但在大型的开发项目中,使用这种方式会带来一些隐含的问题:如果一个 PHP 文件需要使用很多其它类,那么就需要很多的 require/include 语句,这样有可能会造成遗漏或者包含进不必要的类文件。如果大量的文件都需要使用其它的类,那么要保证每个文件都包含正确的类文件肯定是一个噩梦, 况且 require_once 的代价很大。

PHP5 为这个问题提供了一个解决方案,这就是类的自动装载 (autoload) 机制。 autoload 机制可以使得 PHP 程序有可能在使用类时才自动包含类文件,而不是一开始就将所有的类文件 include 进来,这种机制也称为 lazy loading。

总结起来,自动加载功能带来了几处优点:

  • 用类之前无需 include 或者 require。
  • 使用类的时候才会 require/include 文件,实现了 lazy loading,避免了 require/include 多余文件。
  • 无需考虑引入类的实际磁盘地址,实现了逻辑和实体文件的分离。

设计思想

使用PHP 的自动加载函数 __autoload()SPL Autoload (Standard PHP Library) 还有 Namespace 命名空间 实现类名与实际的磁盘文件映射,实现了逻辑和实体文件的分离,这样编码的逻辑更加灵活.实现的必要条件有了,实现规则有很多,我们还需要PSR 标准来统一编码规范.

composer.json

可以通过composer init 命令行创建,也可以自定义

eg.

这里展示laravelcomposer

  1. {
  2. "name": "laravel/laravel",
  3. "type": "project",
  4. "description": "The Laravel Framework.",
  5. "keywords": [
  6. "framework",
  7. "laravel"
  8. ],
  9. "license": "MIT",
  10. "require": {
  11. "php": "^7.2",
  12. "fideloper/proxy": "^4.0",
  13. "laravel/framework": "^6.2",
  14. "laravel/tinker": "^2.0"
  15. },
  16. "require-dev": {
  17. "facade/ignition": "^1.4",
  18. "fzaninotto/faker": "^1.4",
  19. "mockery/mockery": "^1.0",
  20. "nunomaduro/collision": "^3.0",
  21. "phpunit/phpunit": "^8.0"
  22. },
  23. "config": {
  24. "optimize-autoloader": true,
  25. "preferred-install": "dist",
  26. "sort-packages": true
  27. },
  28. "extra": {
  29. "laravel": {
  30. "dont-discover": []
  31. }
  32. },
  33. "autoload": {
  34. "psr-4": {
  35. "App\\": "app/"
  36. },
  37. "classmap": [
  38. "database/seeds",
  39. "database/factories"
  40. ]
  41. },
  42. "autoload-dev": {
  43. "psr-4": {
  44. "Tests\\": "tests/"
  45. }
  46. },
  47. "minimum-stability": "dev",
  48. "prefer-stable": true,
  49. "scripts": {
  50. "post-autoload-dump": [
  51. "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
  52. "@php artisan package:discover --ansi"
  53. ],
  54. "post-root-package-install": [
  55. "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
  56. ],
  57. "post-create-project-cmd": [
  58. "@php artisan key:generate --ansi"
  59. ]
  60. }
  61. }

composer.lock

  • 参考laravel 的 文件
  • 这里的说明很清楚了
  • 具体也可以查看链接,解释了是否要把composer.lock放入git repo中
  1. {
  2. "_readme": [
  3. "This file locks the dependencies of your project to a known state",
  4. "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
  5. "This file is @generated automatically"
  6. ],
  7. ...
  8. }

installed.json

  • What's the difference between composer.lock and installed.json?
  • composer.lock is generated when installing for the first time or updating. It contains references to the exact versions used. It should be committed into the version tracking repository to allow restoring this exact combination of libraries.
  • installed.json is an internal file of Composer. It's used when you remove a package manually from composer.json to remove the files from the vendor directory. Otherwise, the old vendor package would be around forever.

下载一个最小化的源码包

  1. //创建 composer.json 文件
  2. composer init
  3. //安装项目
  4. composer install

comoposer 相关命令

文件结构

  1. .
  2. ├── composer.json // composer init 生成的配置文件
  3. └── vendor
  4. ├── autoload.php // composer 入口文件
  5. └── composer
  6. ├── ClassLoader.php // composer 核心类文件
  7. ├── LICENSE
  8. ├── autoload_classmap.php // 类名与磁盘路径直接匹配映射
  9. ├── autoload_namespaces.php // 为需要加载的命名空间前缀定义路径
  10. ├── autoload_psr4.php // 根据 Psr4 规范定义命名空间前缀和路径
  11. ├── autoload_real.php // composer 加载配置逻辑
  12. ├── autoload_static.php // 包含所有需要加载的文件、类,内容包含以上文件
  13. ├── autoload_files.php // 定义所有需要加载的配置文件
  14. └── installed.json // 参看上文解释
  • autoload_real.php:自动加载功能的引导类。任务是 composer 加载类的初始化 (顶级命名空间与文件路 径映射初始化) 和注册 (spl_autoload_register ())。
  • ClassLoader.php:composer 加载类。composer 自动加载功能的核心类。
    autoload_static.php:顶级命名空间初始化类,用于给核心类初始化顶级命名空间。
  • autoload_classmap.php:自动加载的最简单形式,有完整的命名空间和文件目录的映射;
  • autoload_files.php:用于加载全局函数的文件,存放各个全局函数所在的文件路径名;
  • autoload_namespaces.php:符合 PSR0 标准的自动加载文件,存放着顶级命名空间与文件的映射;
  • autoload_psr4.php:符合 PSR4 标准的自动加载文件,存放着顶级命名空间与文件的映射;

PSR-0 规范

https://www.php-fig.org/psr/psr-0/

PSR-4 规范

https://www.php-fig.org/psr/psr-4/

自定义函数文件的引入

这些文件是随着项目全局加载的

  1. {
  2. "autoload": {
  3. "files": [
  4. "src/Illuminate/Foundation/helpers.php",
  5. "src/Illuminate/Support/helpers.php"
  6. ],
  7. "psr-4": {
  8. "Illuminate\\": "src/Illuminate/"
  9. }
  10. },
  11. "autoload-dev": {
  12. "files": [
  13. "tests/Database/stubs/MigrationCreatorFakeMigration.php"
  14. ],
  15. "psr-4": {
  16. "Illuminate\\Tests\\": "tests/"
  17. }
  18. }
  19. }

源码逻辑

这里我们通过laravel项目的composer来解释,上面下载的composer包没有自定义文件的引入代码

通过对比发现:

  • autoload_real.php 文件多了一些代码
  1. public static function getLoader()
  2. {
  3. if ($useStaticLoader) {
  4. $includeFiles = Composer\Autoload\ComposerStaticInit5620d294729feab5b1620b129a756f81::$files;
  5. } else {
  6. $includeFiles = require __DIR__ . '/autoload_files.php';
  7. }
  8. foreach ($includeFiles as $fileIdentifier => $file) {
  9. composerRequire5620d294729feab5b1620b129a756f81($fileIdentifier, $file);
  10. }
  11. }
  12. function composerRequire5620d294729feab5b1620b129a756f81($fileIdentifier, $file)
  13. {
  14. if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
  15. require $file;
  16. $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
  17. }
  18. }
  • composer 文件夹下多了autoload_files.php文件

实现实例:

创建自定义辅助函数

在这里我们把函数放在app/Support/Helpers/CustomHelper.php内:

  1. <?php
  2. if (! function_exists('test_function')) {
  3. function test_function() {
  4. echo "我是一个自定义辅助函数";
  5. }
  6. }

辅助函数文件载入

创建文件app/Support/Helpers/Helpers.php,并载入包含有自定义函数的文件:

  1. <?php
  2. $helpers = [
  3. 'CustomHelper.php'
  4. ];
  5. // 载入
  6. foreach ($helpers as $helperFileName) {
  7. include __DIR__ . '/' .$helperFileName;
  8. }

在composer.json中自动载入Helpers.php文件

  1. /*composer.json*/
  2. {
  3. "autoload": {
  4. "classmap": [
  5. "database"
  6. ],
  7. "psr-4": {
  8. "App\\": "app/"
  9. },
  10. "files": [
  11. "app/Support/Helpers/helpers.php"
  12. ]
  13. }
  14. }

重新编译autoload.php文件

运行如下命令:

  1. composerdump-autoload

运行后就可以在任意地方调用你的自定义函数了

文件源码解析

自定义包的发布

https://packagist.org/


参考资料:
composer 自动加载
Composer 自动加载原理
由浅入深的了解一下composer自动加载机制
Composer自动加载源码解析
PHP 自动加载功能原理解析
Composer 的 Autoload 源码实现——启动与初始化
Composer 的 Autoload 源码实现——注册与运行
PHP中PSR-[0-4]规范
composer.json 架构
Composer.json配置文件说明
为Laravel 5 添加自定义辅助函数
深入解析 composer 的自动加载原理
为什么Composer在生产环境要使用dumpautoload
What's the difference between composer.lock and installed.json?

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注