尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

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

Composer Vendor Binaries 完全指南:`vendor/bin` 目录的原理、代理文件与运行环境变量 Composer Vendor Binaries 完全指南vendor/bin目录的原理、代理文件与运行环境变量【免费下载链接】composerDependency Manager for PHP项目地址: https://gitcode.com/gh_mirrors/co/composer导读本文围绕 Composer 的 Vendor Binaries 机制展开系统讲解如何通过composer.json的bin字段把包中的命令行脚本暴露给使用者、composer install时 Composer 如何在vendor/bin中生成代理文件proxy以及如何在二进制脚本中通过$_composer_autoload_path、$_composer_bin_dir、COMPOSER_RUNTIME_BIN_DIR定位 autoloader 与 bin 目录。读完本文你将能正确地在自己的包中声明并消费二进制脚本兼容 Windows/WSL并灵活自定义二进制安装目录。什么是 Vendor BinaryVendor binary供应商二进制脚本指任何由 Composer 包希望传递给它使用者的命令行脚本。只要一个包内存在需要暴露给使用者的 CLI 程序例如phpunit、phpcs、psalm就应该把它声明为 vendor binary。与此相对如果包内还有一些仅服务于包本身开发流程、使用者并不需要的脚本如构建脚本、编译脚本则不应该将其声明为 vendor binary——它们应留在包的源码树深处而不是被安装到使用者的vendor/bin中。如何在composer.json中声明二进制脚本在项目包的composer.json中通过bin键声明。它是一个数组因此可以为同一个项目声明多个二进制脚本{ bin: [bin/my-script, bin/my-other-script] }从 Composer 的 schema 定义看见 04-schema.mdbin是可选的含义为“一组应当被当作二进制处理并放入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 行的执行逻辑如下通过$package-getBinaries()取回包声明的二进制列表若为空直接返回。逐项检查二进制文件是否存在不存在或指向目录时会跳过并给出 warning。调用initializeBinDir()确保bin-dir目录存在。根据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 wrapperphpvfscomposer://兼容处理并对 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.mdauto默认仅在 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改到别处在composer.json中设置config.bin-dir设置环境变量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-dir或COMPOSER_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),仅供参考
返回列表