玩转 PHP 包管理工具 composer

一些关于 Composer 的资源信息

  1. Composer 的官方网站为 https://getcomposer.org/
  2. Composer 仓库地址为 http://packagist.org/
  3. 由于众所周知的原因,我们使用官方的源经常无法拉取包,因此墙内的开发者可以使用阿里云全量 composer 镜像源 https://developer.aliyun.com/composer

常用命令列表

  • composer create-project --prefer-dist laravel/laravel blog: 创建一个名为 blog 的 Laravel 项目
  • composer require vendor/package:2.* vendor/package2:dev-master: 添加依赖包
    • --dev: 将依赖包添加至require-dev字段
  • composer remove vendor/package: 移除依赖包
  • composer install: 从当前目录读取 composer.json 文件,处理了依赖关系,并把其安装到 vendor 目录下。如果当前目录下存在 composer.lock 文件,它会从此文件读取依赖版本,而不是根据 composer.json 文件去获取依赖。这确保了该库的每个使用者都能得到相同的依赖版本。如果没有 composer.lock 文件,composer 将在处理完依赖关系后创建它。
    • --dev: 安装 require-dev 字段中列出的包(这是一个默认值)
    • --no-dev:: 跳过 require-dev 字段中列出的包,通常用于生产环境,以避免一些开发调试依赖包(如 phpunit)的安装
    • --optimize-autoloader (-o): 转换 PSR-0/4 autoloadingclassmap 可以获得更快的加载支持。特别是在生产环境下建议这么做
  • composer update: 依据composer.json规则更新,并将新的版本号写入composer.lock
    • 只更新指定的包:composer update [vendor/package vendor/package2]
    • 使用通配符批量更新 composer update vendor/*
    • --dev: 更新 require-dev 字段中列出的包(这是一个默认值)
    • --no-dev: 跳过 require-dev 字段中的包更新
  • composer self-update: 更新 composer 版本
  • composer validate: 验证composer.json文件是否有效
  • composer status: 如果你修改了依赖包的代码,该命令可以显示你修改的包文件
  • composer init: 初始化一个新的包,用于创建依赖包
  • composer search monolog: 在 packagist.org 上搜索包
  • composer show: 列出所有可用的包,即composer.lock中的包
    • --installed (-i): 列出已安装的依赖包
    • --self (-s): 仅列出当前项目信息,即composer.json中的项目信息
    • composer show monolog/monolog 1.0.2: 列出指定包版本号的信息

仅将 composer.jsoncomposer.lock 文件添加到版本库中,将 vendor 目录添加到 .gitignore 中,这样可以保证所有开发者的依赖包相同,且项目大小尽可能的小。

composer.json 文件

以下是 Laravel 的 composer.json 文件示例

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

composer.json中的classmap字段会扫描指定目录中的所有.php.inc文件,并加载到vendor/composer/autoload_classmap.php文件中,在该文件中会实现一个具体类与文件映射的关联数组,也可以直接精确指定一个文件。通过 classmap 可以生成不遵循 PSR-0 和 PSR-4 规范的自动加载类库。

对于每次在程序执行时都需要载入的文件,可以通过 files 规范实现自动加载,通常经常使用的函数库文件就使用这种载入方式,如 helpers 函数文件。

composer 生成类的自动加载函数的实现

类的自动加载可以通过魔术方法__autoload(string $class)实现,也可以通过函数spl_autoload_register注册一个自动加载方法。__autoload()魔术方法只能定义一次,而spl_autoload_register则可以将多个类自动加载方法注册到队列中。通过spl_autoload_register()函数加载的自动加载函数可以是全局函数,也可以是某个类实例对象的函数,即通过 array(对象名,函数名) 注册。

通过composer 工具创建依赖管理时,会在 vendor 目录下创建一个autoload.php文件和一个composer文件夹,其中composer文件夹中包含了类自动加载函数注册的相关实现,而autoload.php文件是对外提供的接口,通过包含该文件就可以完成类自动加载函数的注册。通过其中的代码可以看到, autoload.php 文件包含了composer目录下的autoload_real.php 文件,而autoload_real.php 文件定义了一个类,该类由末尾有一串数字的方式定义,并且定义了getLoader()函数,该函数首先实例化Composer\Autoload\ClassLoader类,然后通过该类实例添加相关的文件路径配置,包括命名空间(autoload_namespaces.php 文件定义)配置、PSR-4规范(autoload_psr4.php 文件定义)配置、类映射(autoload_classmap.php 文件定义)配置,接着调用$loader->register(true)注册类自动加载函数,最后加载全局文件,即在autoload_files.php文件中配置的内容。

因为热爱,所以执着。