根本原因是系统PATH未包含vendor/bin目录,Composer不会自动将其加入PATH以避免全局污染;可通过export PATH="./vendor/bin:$PATH"临时生效,或用cd函数自动注入、composer exec等安全方式解决。

为什么 vendor/bin 命令在终端里直接敲不生效
根本原因不是 Composer 没装对,而是系统 PATH 没包含项目级的 vendor/bin 目录。Composer 确实把 phpunit、phpcs、larastan 这类工具放进了 vendor/bin,但它不会自动加进你的 shell PATH —— 这是刻意设计,避免全局污染。
常见错误现象:command not found: phpunit,但 ./vendor/bin/phpunit 可以跑;或者 IDE 终端能运行,系统终端不行,说明 PATH 加载时机或范围不对。
- 别往
/etc/environment或系统级 profile 里硬塞项目路径,那会破坏可移植性 - 每个项目应独立管理自己的
vendor/bin,推荐用 shell alias 或 per-project PATH 注入 - Zsh 用户注意:如果用了 Oh My Zsh,默认不读
~/.bashrc,PATH 要加在~/.zshrc里
如何让当前终端立即识别 vendor/bin
最轻量的做法是临时追加 PATH,不改任何配置文件,适合调试或 CI 脚本:
export PATH="./vendor/bin:$PATH"
执行后,当前终端就能直接用 phpunit、psalm 等命令。但关闭终端就失效 —— 这反而是安全的默认行为。
- 确认是否生效:运行
echo $PATH,看开头是否有./vendor/bin - 如果提示
No such file or directory,说明项目还没运行composer install,先补上 - 别写成
$PATH:./vendor/bin(放在末尾),某些工具(如php-cs-fixer)有同名系统命令时会被覆盖
怎样持久化且不污染全局 PATH
推荐用项目根目录下的 .env.local + shell 钩子,而不是无差别追加到 ~/.zshrc。例如,在项目中加一个 cd 后自动注入 PATH 的小机制:
在 ~/.zshrc(或 ~/.bashrc)里加一段:
cd() {
builtin cd "$@" && {
[[ -d ./vendor/bin ]] && export PATH="./vendor/bin:$PATH" || unset PATH
}
}这样每次 cd 进一个含 vendor/bin 的 PHP 项目,PATH 就自动前置;切走就恢复原状。
- 不依赖额外工具(比如 direnv),纯 shell 实现
- 避免多个项目同时激活导致 PATH 膨胀(比如嵌套
cd到子模块) - Windows 用户注意:PowerShell 不支持这种函数重写,建议用
composer exec替代,例如composer exec phpunit
composer exec 是更干净的替代方案吗
是的,尤其适合 CI、脚本或多人协作场景。composer exec 本质是封装了 PATH=./vendor/bin:$PATH 的执行环境,不依赖 shell 配置。
例如:
composer exec phpunit -- --testdox
等价于手动设置 PATH 后运行,但更可靠 —— 它还自动处理 Windows 下的 .bat / .exe 后缀适配问题。
- 不适用于需要交互式启动的命令(比如
phpstan server后台服务) - 不能用于 shell 管道或重定向上游(
composer exec phpunit | grep FAIL可能失败,得用$(composer exec phpunit 2>&1)) - 如果你的
composer.json里定义了scripts,优先走composer run test,它比裸 exec 更易维护
PATH 本身不是 Composer 的责任,它是 shell 的边界。真正容易被忽略的是:不同 shell 初始化文件加载顺序不同,以及 vendor/bin 在 symlink 项目或 Docker 卷挂载时可能路径失效。先确认 ls -l vendor/bin 输出是否真实可执行,再调 PATH。


















