Composer Vendor Binaries 完全指南:`vendor/bin` 目录的原理、代理文件与运行环境变量
2026/9/19 6:28:54 网站建设 项目流程

Composer Vendor Binaries 完全指南:vendor/bin目录的原理、代理文件与运行环境变量

【免费下载链接】composerDependency Manager for PHP项目地址: https://gitcode.com/gh_mirrors/co/composer

导读

本文围绕 Composer 的 Vendor Binaries 机制展开,系统讲解如何通过composer.jsonbin字段把包中的命令行脚本暴露给使用者、composer install时 Composer 如何在vendor/bin中生成代理文件(proxy),以及如何在二进制脚本中通过$_composer_autoload_path$_composer_bin_dirCOMPOSER_RUNTIME_BIN_DIR定位 autoloader 与 bin 目录。读完本文,你将能正确地在自己的包中声明并消费二进制脚本,兼容 Windows/WSL,并灵活自定义二进制安装目录。

什么是 Vendor Binary

Vendor binary(供应商二进制脚本)指任何由 Composer 包希望传递给它使用者的命令行脚本。只要一个包内存在需要暴露给使用者的 CLI 程序(例如phpunitphpcspsalm),就应该把它声明为 vendor binary。

与此相对,如果包内还有一些仅服务于包本身开发流程、使用者并不需要的脚本(如构建脚本、编译脚本),则不应该将其声明为 vendor binary——它们应留在包的源码树深处,而不是被安装到使用者的vendor/bin中。

如何在composer.json中声明二进制脚本

在项目(包)的composer.json中通过bin键声明。它是一个数组,因此可以为同一个项目声明多个二进制脚本:

{ "bin": ["bin/my-script", "bin/my-other-script"] }

从 Composer 的 schema 定义看(见 04-schema.md),bin可选的,含义为“一组应当被当作二进制处理并放入bin-dir(来自 config)的文件”。也就是说,数组中的每一项都是相对于包根目录的路径,指向包内一个可执行文件。

声明后会发生什么:声明者与依赖者的区别

在 Composer 中,二进制脚本的安装行为取决于你是声明者还是依赖者

  • 声明者(root package):当 Composer 在你自己的composer.json上运行时(例如composer install),对于该包自身直接声明的二进制脚本,什么都不做。因为这是你的源码树,脚本本来就在那里,无需代理。
  • 依赖者(依赖了该包的项目):Composer 会遍历所有依赖包中声明的二进制脚本,为每个二进制在vendor/bin下生成一个代理文件(proxy file)(在 Windows/WSL 上会生成一个或两个代理文件,详见后文 Windows 小节)。

一个完整的示例

假设包my-vendor/project-a声明了二进制脚本:

{ "name": "my-vendor/project-a", "bin": ["bin/project-a-bin"] }

此时,对这个composer.json运行composer install并不会对bin/project-a-bin做任何事(它是声明者)。

再假设项目my-vendor/project-b依赖了它:

{ "name": "my-vendor/project-b", "require": { "my-vendor/project-a": "*" } }

对 project-b 运行composer install时,Composer 会查看 project-a 声明的所有二进制脚本,并将它们安装到 project-b 的vendor/bin下。具体结果是:vendor/my-vendor/project-a/bin/project-a-bin通过代理文件以vendor/bin/project-a-bin的形式可供调用。

这正是把散落在vendor/目录深处的脚本“提升”到统一入口的便捷机制。

代理文件的生成原理(源码级解析)

在源码层面,二进制代理文件的生成由 BinaryInstaller.php 负责。核心方法installBinaries()(第 52-105 行)的执行逻辑如下:

  1. 通过$package->getBinaries()取回包声明的二进制列表;若为空直接返回。
  2. 逐项检查二进制文件是否存在(不存在或指向目录时会跳过并给出 warning)。
  3. 调用initializeBinDir()确保bin-dir目录存在。
  4. 根据bin-compat配置选择安装方式:
    • full:调用installFullBinaries()——同时生成 Unix 风格代理和.bat代理;
    • 否则:调用installUnixyProxyBinaries()——仅生成 Unix 风格代理。

PHP 二进制:生成 PHP 代理文件

对于内容是 PHP 的脚本,generateUnixyProxyCode()(第 206-367 行)会生成一个PHP 代理文件而非 shell 脚本,这样可以用自定义的 PHP 进程来调用代理。代理文件的关键片段包括:

<?php namespace Composer; $GLOBALS['_composer_bin_dir'] = __DIR__; // bin 目录 $GLOBALS['_composer_autoload_path'] = ...; // 指向 vendor/autoload.php return include $binPathExported; // 真正加载原二进制

也就是说,$_composer_bin_dir$_composer_autoload_path这两个全局变量正是在生成代理文件时写入的。源码还针对带 shebang 的 PHP 文件在 PHP 8 以下做了 stream wrapper(phpvfscomposer://)兼容处理,并对 phpunit 做了进程隔离的特殊处理,这里不再展开。

非 PHP 二进制:生成 shell 代理文件

对于非 PHP 脚本(shell、Perl 等),generateUnixyProxyCode()会生成一个#!/usr/bin/env sh的代理文件:先解析出真实路径,cd到目标目录后用exec执行原脚本,并显式导出COMPOSER_RUNTIME_BIN_DIR环境变量(第 395 行):

export COMPOSER_RUNTIME_BIN_DIR="$(cd "${self%[/\\]*}" > /dev/null; pwd)"

如何判断一个脚本是 PHP 还是其他语言?看determineBinaryCaller()(第 131-145 行):.bat/.exe后缀返回call;读取文件首行,若匹配 shebang 则返回解释器名;否则默认按php处理。

在二进制脚本中定位 Composer autoloader

自 Composer 2.2 起,bin 代理文件会定义一个名为$_composer_autoload_path的全局变量。当你的二进制脚本被执行时,可以借助它轻松定位到项目(依赖者)的 autoloader:

<?php include $_composer_autoload_path ?? __DIR__ . '/../vendor/autoload.php';

需要注意:当二进制由root 包自身定义并运行时,该全局变量并不会被定义,因此必须提供上面这种 fallback。另外,vendor/autoload.php本身并不会设置这个变量(见 07-runtime.md)——它由代理文件注入,如果由 autoloader 自己设置,就等于指回自身,毫无意义。

如果要在自己的包里依赖这个特性,应在composer.json中要求"composer-runtime-api": "^2.2",确保安装方使用的 Composer 版本支持该功能:

{ "require": { "composer-runtime-api": "^2.2" } }

在二进制脚本中定位 Composer bin 目录

自 Composer 2.2.2 起,代理文件还会定义$_composer_bin_dir全局变量,方便脚本定位项目(依赖者)的 bin 目录。对于非 PHP 二进制,自 Composer 2.2.6 起,代理会额外设置名为COMPOSER_RUNTIME_BIN_DIR的环境变量(这正是源码中export COMPOSER_RUNTIME_BIN_DIR一行所做的)。

同样,root 包自身定义的二进制运行时不会得到该全局变量,需要 fallback。PHP 场景的示例:

<?php $binDir = $_composer_bin_dir ?? __DIR__ . '/../vendor/bin';

bash 场景的示例:

#!/bin/bash if [[ -z "$COMPOSER_RUNTIME_BIN_DIR" ]]; then BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" else BIN_DIR="$COMPOSER_RUNTIME_BIN_DIR" fi

依赖该特性的包应要求"composer-runtime-api": "^2.2.2"

{ "require": { "composer-runtime-api": "^2.2.2" } }

相关背景可参考 07-runtime.md:这些变量同样由二进制代理设置,而不是由vendor/autoload.php提供。

Windows 与.bat文件

完全由 Composer 管理的包不需要为了 Windows 兼容性自带任何.bat文件。当 Composer 在 Windows 环境下运行时,会以特殊方式处理二进制的安装:

  • 自动生成一个引用原二进制的.bat文件
  • 同时生成一个与二进制同名的Unix 风格代理文件,供 WSL、Linux 虚拟机等环境使用。

在源码中,installFullBinaries()(第 155-169 行)正是“先装 Unix 代理、再补.bat”的实现,而generateWindowsProxyCode()(第 183-204 行)会生成形如下面的批处理内容(以 PHP 目标为例):

@ECHO OFF setlocal DISABLEDELAYEDEXPANSION SET BIN_TARGET=%~dp0/project-a-bin SET COMPOSER_RUNTIME_BIN_DIR=%~dp0 php "%BIN_TARGET%" %*

注意其中的COMPOSER_RUNTIME_BIN_DIR=%~dp0——这正是 Windows 批处理场景下该环境变量的来源。

bin-compat配置

是否生成.bat文件由bin-compat配置决定(默认auto,详见 06-config.md):

  • auto(默认):仅在 Windows 或 WSL 环境下安装.bat代理文件;
  • full:为每个二进制同时安装 Windows 的.bat文件与 Unix 脚本。主要用于“在 Linux 虚拟机里跑 Composer,但希望在 Windows 宿主机上使用.bat代理”的场景;
  • proxy:只创建 bash/Unix 风格代理文件,即使在 Windows/WSL 上也不生成.bat

也可以不修改配置文件,直接用环境变量 COMPOSER_BIN_COMPAT 覆盖bin-compat设置。

另一方面,如果包需要支持“不经由 Composer 分发”的工作流(例如直接从源码仓库使用),欢迎维护自定义.bat文件。这种情况下,包不应再把.bat文件声明为 binary,因为 Composer 会自己生成,无需重复。

让 vendor binaries 安装到其他目录

可以,有两种方式可以把 vendor binary 的安装目录从默认的vendor/bin改到别处:

  1. composer.json中设置config.bin-dir
  2. 设置环境变量COMPOSER_BIN_DIR

bin-dir的默认值是{$vendor-dir}/bin(即vendor/bin,见 Config.php 与 06-config.md)。通过配置文件修改的示例:

{ "config": { "bin-dir": "scripts" } }

对该composer.json运行composer install后,所有 vendor binary 会被安装到scripts/而不是vendor/bin/。还可以把bin-dir设为./,把二进制直接放到项目根目录。

环境变量方式的说明见 03-cli.md:设置COMPOSER_BIN_DIR即可把 bin 目录改为vendor/bin以外的位置。两种方式二选一即可满足定制需求。

小结

  • bin字段把包内的可执行脚本“发布”给依赖者,composer install会为依赖包中的每个二进制在vendor/bin(或其定制目录)生成代理文件。
  • root 包自己声明的二进制不会生成代理,因此脚本中用到运行时全局变量时必须提供 fallback。
  • PHP 二进制会得到 PHP 代理并注入$_composer_autoload_path$_composer_bin_dir全局变量;非 PHP 二进制则通过COMPOSER_RUNTIME_BIN_DIR环境变量感知 bin 目录。
  • 依赖这些特性的包应声明"composer-runtime-api": "^2.2"(或^2.2.2)约束。
  • Windows 下的.bat与 Unix 代理由 Composer 按bin-compatauto/full/proxy)自动生成,包内无需自带。
  • 通过config.bin-dirCOMPOSER_BIN_DIR可以自由定制二进制安装目录。

更详细的机制可以参考 Composer 源码中的 BinaryInstaller.php,以及配套文档 04-schema.md、06-config.md、07-runtime.md 与 03-cli.md。

【免费下载链接】composerDependency Manager for PHP项目地址: https://gitcode.com/gh_mirrors/co/composer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询