阿里云/腾讯云镜像因同步时使用tar --no-same-permissions解压,丢弃了vendor/bin脚本的可执行位(如phpunit变为-rw-r--r--),导致./vendor/bin/phpunit报permission denied;验证方式为ls -l vendor/bin/phpunit查看是否缺失x位,修复推荐在composer.json中配置post-install-cmd自动补0755权限。

为什么阿里云/腾讯云镜像会让 vendor/bin 脚本没权限
不是你本地 umask 或 chmod 设置错了,而是镜像源在同步 dist 包时丢弃了原始 tar 的 chmod +x 位。官方 packagist.org 的 tar 包保留可执行位,但部分国内镜像(如旧版阿里云、腾讯云)用 tar --no-same-permissions 或 --no-symlinks 解压,强制把所有文件权限重设为 644 或 755 统一值——结果就是 vendor/bin/phpunit 变成 -rw-r--r--,直接运行 ./vendor/bin/phpunit 报 Permission denied。
怎么快速验证是不是镜像导致的权限丢失
别猜,直接看文件权限:
- 运行
ls -l vendor/bin/phpunit—— 如果输出里没有x(比如是-rw-r--r--),就是镜像惹的祸 - 对比官方源:临时切回 packagist.org(
composer config repo.packagist composer https://packagist.org),删掉vendor/和composer.lock后重装,再查同一脚本权限是否恢复-rwxr-xr-x - 查当前镜像配置:
composer config --global repos.packagist,确认是否指向https://mirrors.aliyun.com/composer/或类似地址
临时修复和长期自动补权限
临时救急可以手动加执行位,但每次 composer install 都得重复,不现实。推荐在 composer.json 里加 post-install-cmd 自动处理:
- 只对
vendor/bin/下已存在且不可执行的文件设0755,不碰其他目录,不递归 - Windows 不生效(PHP
is_executable()行为不同),CI 或 Docker 构建时建议放在构建阶段之后执行 - 别用
find vendor/bin -type f -exec chmod +x {} \;—— 会把README这类非脚本也设成可执行,CI 工具可能报 warning
示例脚本(放进 composer.json 的 scripts 段):
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
{"scripts": {"post-install-cmd": ["@php -r \"if(is_dir('vendor/bin')) {\$files=glob('vendor/bin/*');foreach(\$files as \$f) {if(is_executable(\$f)) continue; chmod(\$f, 0755); } }\""], "post-update-cmd": ["@post-install-cmd"]}}
换镜像或禁用 dist 包能绕过问题吗
换镜像不一定管用——只要它做二次解压,就可能丢权限。禁用 dist 包(composer install --prefer-source)确实能绕过,因为 source 模式走 git clone,保留原始权限,但代价是慢、占空间、依赖 git 且某些包无 source。
更实际的做法是:接受镜像带来的权限“降级”,靠 post-install-cmd 补回来;或者干脆不用镜像的 dist 包,改用 composer config --global prefer-stable true + --prefer-source 组合,只在 CI 或部署时权衡速度与确定性。
真正容易被忽略的是:这个问题只影响 vendor/bin/ 下的脚本,不影响 autoload.php 加载或类调用。如果你只用 php vendor/bin/phpunit 而非 ./vendor/bin/phpunit,甚至可能一直没发现权限丢了。










