项目 vendor/bin 优先级低于全局 bin,因 PATH 从左到右查找,通常全局路径在前;应显式使用 ./vendor/bin/命令或调整 PATH 顺序,并确保 IDE 使用项目 autoload.php。

全局 bin 和项目 vendor/bin 路径谁优先?
命令行执行时,PATH 环境变量决定哪个 phpunit 或 pest 先被调到。系统默认按 PATH 从左到右查找,通常全局 Composer bin(如 ~/.composer/vendor/bin)在前,项目 vendor/bin 在后 —— 这就是冲突根源。
- 检查当前生效路径:
echo $PATH(Linux/macOS)或echo %PATH%(Windows),确认vendor/bin是否在全局 bin 之后 - 临时绕过:进项目目录后显式用
./vendor/bin/phpunit,不依赖 PATH 查找 - 长期方案:把
./vendor/bin加到PATH最前面(例如在~/.zshrc中加export PATH="./vendor/bin:$PATH"),但注意这仅对当前 shell 有效,且不能解决多项目并行场景
composer global require 和 composer require --dev 版本打架怎么办?
比如全局装了 phpstan/phpstan v1.10,项目里 require-dev 指定 v1.12,运行 phpstan 命令却报 Class not found 或行为不一致 —— 因为全局 autoloader 和项目 autoloader 隔离,但 CLI 工具二进制文件可能混用不同版本的类加载逻辑。
- 根本原因:全局工具的
bin文件本质是带 shebang 的 PHP 脚本,它会加载全局autoload.php;而项目工具依赖项目vendor/autoload.php - 验证方式:运行
which phpstan,再看该文件头是否包含#!/usr/bin/env php及后续require __DIR__.'/../vendor/autoload.php' - 安全做法:项目内一律用
./vendor/bin/phpstan,彻底避开全局版本干扰;CI/CD 脚本中也显式写全路径,不依赖 shell 查找
为什么 composer global update 会悄悄破坏项目工具链?
全局更新不是“只升级工具”,而是重装整个全局 vendor 目录。如果某个全局工具(如 laravel/installer)依赖的底层包(如 symfony/console)和你某项目 lock 文件里的版本冲突,后续你在该项目里跑 php artisan 就可能因 class 重复定义或方法不存在而失败 —— 尤其当项目用了 class_alias 或反射操作时。
- 全局
composer.json不受项目约束,global update会无视任何项目composer.lock - 避免方案:除非明确需要新特性,否则不要随意
composer global update;改用composer global require tool-name:^x.y锁定小版本 - 补救办法:出问题后可删掉
~/.composer/vendor重装关键工具,或改用phive管理全局 PHAR 工具(如phpstan、psalm),完全隔离依赖
用 alias 替代 PATH 是不是更稳妥?
直接 alias phpunit='vendor/bin/phpunit' 看似简单,但实际容易失效:alias 只在交互式 shell 生效,脚本里、IDE 终端、CI 环境都不认;而且一旦切换目录,vendor/bin 路径就错了。
- 真正可靠的 alias 应该是函数形式:
phpunit() { [ -f "./vendor/bin/phpunit" ] && ./vendor/bin/phpunit "$@" || echo "No phpunit in current project"; } - 但更推荐在项目根目录放一个简单的
run脚本(如run test),内部统一调用./vendor/bin/xxx,既可复用又不会污染环境 - 别忽略 IDE:PhpStorm 的 CLI interpreter 设置里,要手动指定 “Use composer autoloader” 并指向项目
vendor/autoload.php,否则调试时仍可能加载全局类


















