根本原因是Composer生成的bin脚本缺少x执行权限位,因file_put_contents不设chmod且Git/Windows环境丢失可执行位;修复方案包括手动chmod +x vendor/bin/*、在composer.json中配置post-install-cmd自动执行,或用PHP原生chmod函数兼容受限环境。

为什么 composer install 会报 “Permission denied” 执行脚本错误
根本原因不是 Composer 本身没权限,而是它生成的可执行脚本(比如 vendor/bin/phpunit)在 Unix/Linux/macOS 上缺少 x(执行)权限位。Composer 默认用 file_put_contents() 写入脚本,不主动设 chmod,尤其当源包的 bin 文件在 Git 中未保留可执行位、或你在 Windows 上打包再传到 Linux 服务器时,这个问题高频出现。
常见报错长这样:sh: vendor/bin/phpunit: Permission denied 或 bash: ./vendor/bin/psalm: Permission denied。
- 不是 PHP 或 Composer 权限配置问题,不用改
sudo composer install - 不是
vendor/目录属主问题(除非你真用了 root 装包) - 本质是脚本文件没
chmod +x,Shell 拒绝执行
快速修复:手动加执行权限(临时方案)
最直接的办法就是补上缺失的权限位。Composer 的 bin 脚本都放在 vendor/bin/ 下,批量处理即可:
chmod +x vendor/bin/*
如果提示 Operation not permitted,说明某些文件被标记了不可修改(比如 macOS 上从 zip 解压或通过某些同步工具传输),可以加 -f 强制:
chmod -f +x vendor/bin/*
- 只对当前项目生效,CI/CD 流水线里每次
composer install后加这一行就行 - 别对整个
vendor/递归 chmod —— 那会破坏 autoload 文件权限,PHP 可能报failed to open stream - Windows 用户通常不触发此问题(没有执行位概念),但 WSL 或 Docker 容器内仍需处理
一劳永逸:让 Composer 自动设权限(推荐)
Composer 本身不提供全局 chmod 开关,但可以通过 scripts 在 composer.json 里自动补全:
"scripts": {
"post-install-cmd": "chmod -f +x vendor/bin/*",
"post-update-cmd": "chmod -f +x vendor/bin/*"
}
这样每次 composer install 或 composer update 结束后,自动修正权限。注意两点:
- 确保你的 Composer 版本 ≥ 2.0(旧版
post-autoload-dump不够可靠) - 如果
vendor/bin/下有非脚本文件(极少见),chmod +x对它们无害,只是多设个位 - 某些共享主机禁用
chmod系统调用,这时得换用纯 PHP 方式(见下一条)
兼容性更强的 PHP 方式(绕过 shell 权限限制)
如果你的运行环境禁用了 chmod(如部分 cPanel 主机),可以用 PHP 原生函数替代:
"scripts": {
"post-install-cmd": "php -r \"foreach(glob('vendor/bin/*') as \$f) @chmod(\$f, 0755);\"",
"post-update-cmd": "php -r \"foreach(glob('vendor/bin/*') as \$f) @chmod(\$f, 0755);\""
}
这个写法不依赖 shell,也避免了 chmod 命令不存在或被禁用的问题。
-
@chmod前的@是为了抑制警告(比如遇到目录时报错) -
0755是安全默认值:所有者可读写执行,组和其他人可读可执行 - 不要用
0777—— 这会让脚本对所有人可写,存在安全风险
真正麻烦的不是加权限这一步,而是很多人在 CI 环境里反复重装 vendor/ 却忘了加这行脚本,结果测试总卡在“找不到命令”或“权限拒绝”上。把 chmod 或 php -r 补进 post-install-cmd 就能彻底避开这个坑。


















