Skip to content

composer

composer是php的依赖管理工具,学习使用composer是学习php的一大助力。

为什么需要自动加载?

因为在大型php项目开发过程中,需要引入大量的php文件。如果所有文件都要手动引入的话会比较麻烦,且项目代码难以管理,所以使用composer自动加载php文件依赖。

php的自动加载函数

php

/**
 * php 自动加载函数
 * @param callable $callback;
 * @Param bool $throw = true;
 * @Param bool $prepend = false;
 */
spl_autoload_register(function (string $class) {
    $file = $class . ".php";
    if (!is_file($file)) {
        throw new \Exception('file does not exists');
    }
    require $file;
});

//类写法
class Bind
{
  //非静态函数
    public function _autoload($class)
    {
        $file = $class . ".php";
        if (!is_file($file)) {
            throw new \Exception('file does not exists');
        }
        require $file;
    }
}

spl_autoload_register([new Bind,'_autoload']);

PSR标准:

PSR标准是php社区制定的自动加载标准,旨在规范文件的自动加载行为,确保代码的可移植性。PSR-4是推出的第5个规范,制定了文件路径从何自动加载类定义,同时规范了自动加载文件的位置。

composer如何实现自动加载

源文件认识

php文件的命名空间要符合PSR-4命名空间的规范。

先了解composer自动加载所用到的源文件的功能或作用。

  1. Autoload_real:自动加载功能的引导类。
  2. ClassLoader.php:composer加载类, 自动加载功能的核心类
  3. Autoload_static.php:顶级命名空间初始化类,用于给核心类初始化顶级命名空间。
  4. Autoload_classmap.php:自动加载的简单形式,有完整的命名空间和文件路径映射。
  5. Autoload_files.php:用于加载全局函数的文件。存放各个全局函数所在的文件路径名。
  6. Autoload_namespace.php:符合PSR-0标准的自动加载文件,存放着顶级命名空间与文件的映射。
  7. Autoload_psr4.php:符合PSR4的标准的自动加载文件,存放顶级命名空间与文件的映射。

目录结构

初始化composer工程:

php
composer init

修改composer.json配置文件,添加自动加载配置项:

php
"autoload": {
    "psr-4": {
      "App\\": "App"
    },
    "files": [
      "./helper.php"
    ]
},

然后执行(每次修改配置文件之后都要执行):

php
composer install

会自动生成vendor文件夹。

搭建一个只有composer的项目示例,不嵌入任何框架。项目文件示例:

php
.
├── App
   └── Controller
       ├── Bootstrap.php
       └── Order.php
├── composer.json
├── composer.lock
├── helper.php
├── index.php
└── vendor
    ├── autoload.php
    ├── composer
   ├── ClassLoader.php
   ├── InstalledVersions.php
   ├── LICENSE
   ├── autoload_classmap.php
   ├── autoload_files.php
   ├── autoload_namespaces.php
   ├── autoload_psr4.php
   ├── autoload_real.php
   ├── autoload_static.php
   ├── installed.json
   ├── installed.php
   └── platform_check.php
    ├── monolog
   └── monolog
       ├── CHANGELOG.md
       ├── ...

入口文件:类似larvalTPphp框架,通过在入口文件添加以下代码启动composer的自动加载功能。

php
include './vendor/autoload.php';

autoload.php文件代码如下:

php
<?php

// autoload.php @generated by Composer

if (PHP_VERSION_ID < 50600) {
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }
    $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
    if (!ini_get('display_errors')) {
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
            fwrite(STDERR, $err);
        } elseif (!headers_sent()) {
            echo $err;
        }
    }
    trigger_error(
        $err,
        E_USER_ERROR
    );
}
//关键在这里,引入加载器
require_once __DIR__ . '/composer/autoload_real.php';
//并执行getLoader函数
return ComposerAutoloaderInit5b932d2402e1bb39b78eadbca7572089::getLoader();

再看autoload_real.php文件:

php
<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInit5b932d2402e1bb39b78eadbca7572089
{
    private static $loader;

    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    /**
     * @return \Composer\Autoload\ClassLoader
     */
    public static function getLoader()
    {
        //单例模式,确保加载器只被初始化一次
        if (null !== self::$loader) {
            return self::$loader;
        }

        //开发环境检查,检查一些php版本啊啥的
        require __DIR__ . '/platform_check.php';

        spl_autoload_register(array('ComposerAutoloaderInit5b932d2402e1bb39b78eadbca7572089', 'loadClassLoader'), true, true);
        //初始化加载器给并赋值给$loader变量
        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
        //在 ClassLoader 初始化完成后,移除临时注册的 loadClassLoader 方法,因为它已经完成了它的任务(加载 ClassLoader 类)。
        spl_autoload_unregister(array('ComposerAutoloaderInit5b932d2402e1bb39b78eadbca7572089', 'loadClassLoader'));

        //引入autoload_static.php文件,里面定义了顶级命名空间的映射关系。
        require __DIR__ . '/autoload_static.php';
        //1. 先执行 autoload_static.php文件中定义的ComposerStaticInit5b932d2402e1bb39b78eadbca7572089类里面的getInitializer静态方法,该方法返回一个回调函数
        //2.用call_user_fuc调用 此回调函数,将autoload_static.php文件内的三种命名空间映射数据挂载道加载器 $loader对象中
        call_user_func(\Composer\Autoload\ComposerStaticInit5b932d2402e1bb39b78eadbca7572089::getInitializer($loader));

        //注册自动加载核心对象
        //这里才是自动加载的核心功能,上面只是初始化映射关系和顶级命名空间映射。
        //这里会执行一个php的自动加载函数,当执行到不认识的命名空间时,会执行此自动加载函数,在自动加载函数的闭包函数中,先对命名空间进行解析(具体怎么解析可以看ClassLoader 里面的findFile函数),
        //如果解析成功,返回文件路径,通过 include 关键字引入文件。至此完成composer的自动加载流程
        $loader->register(true);

        //这里是自动加载全局函数
        $includeFiles = \Composer\Autoload\ComposerStaticInit5b932d2402e1bb39b78eadbca7572089::$files;
        foreach ($includeFiles as $fileIdentifier => $file) {
            composerRequire5b932d2402e1bb39b78eadbca7572089($fileIdentifier, $file);
        }

        return $loader;
    }
}

/**
 * @param string $fileIdentifier
 * @param string $file
 * @return void
 */
function composerRequire5b932d2402e1bb39b78eadbca7572089($fileIdentifier, $file)
{
    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

        require $file;
    }
}