composer why查不到symfony/polyfill-*源头,是因为该包未被直接require或未实际安装;需先执行composer show确认是否存在,再用composer why -t追溯完整依赖路径,且注意dev依赖、provide虚拟替换及冗余引入问题。

composer why symfony/polyfill-* 查不到源头?先确认包是否真被安装
执行 composer why symfony/polyfill-php80 返回 “Package not found” 很常见,但这不等于它没在项目里——只是它没被直接 require,或根本没装进 vendor/。必须满足两个前提:该包已出现在 composer.lock 中,且当前环境(如未加 --no-dev)包含它的安装条件。
常见失效场景包括:
-
symfony/polyfill-mbstring是require-dev里的,但你正用composer install --no-dev部署 - 包被其他库通过
"provide": {"ext-mbstring": "*"}虚拟替代,实际未落地 - 项目还没跑过
composer install,composer.lock缺失或过期
验证是否安装的最快方式是:composer show symfony/polyfill-php80。返回空,就别再查 why —— 它只对已安装包有效。
为什么默认 composer why 只显示一级依赖,却误判 polyfill 来源
composer why symfony/polyfill-mbstring 输出类似 laravel/framework v10.48.12 requires symfony/polyfill-mbstring (^1.28),这容易让人以为 Laravel 是“源头”。但它不是你项目根,只是中间一环。真正决定要不要装这个 polyfill 的,是你自己的 composer.json 或某个插件的依赖声明。
要看到完整路径,必须加 -t(即 --tree):
composer why -t symfony/polyfill-mbstring
输出会是:
my-project
└── laravel/framework ^10.0
└── symfony/polyfill-mbstring ^1.28
注意末尾的 my-project 才是你自己项目的名称,代表真实源头。如果某行结尾带 [dev],说明它是开发依赖,上线时可能被跳过;如果路径中断(比如停在 symfony/console 就没了),大概率是它用 provide 声明替代了目标包,why 不会继续解析。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
polyfill 冗余常来自多个库各自 require 同一个子包
不同组件可能分别声明依赖 symfony/polyfill-php80,比如 doctrine/dbal 和 phpunit/phpunit 都 require 它。Composer 默认不会合并,而是照单全收——哪怕内容完全一样。结果就是:相同函数定义被加载多次,autoload_files.php 多出几条重复路径,启动时多执行几次 bootstrap.php 判断逻辑。
这种冗余不会报错,但会拖慢 require 'vendor/autoload.php' 阶段。排查方法:
- 运行
composer depends symfony/polyfill-php80,看哪些包在拉它 - 检查
vendor/composer/autoload_files.php,搜索polyfill-php80/bootstrap.php出现几次 - 用
strace -e trace=openat php -r "require 'vendor/autoload.php';"观察是否反复打开同一 bootstrap 文件
一旦确认冗余,下一步就是用 replace 干掉它——但前提是你的 PHP 版本已原生支持对应特性,或已启用扩展(如 ext-mbstring)。
replace 替换后为何函数仍存在?因为 polyfill bootstrap 是运行时触发的
在 composer.json 加了 "replace": {"symfony/polyfill-php80": "self.version"} 并运行 composer update 后,symfony/polyfill-php80 会从 vendor/ 消失,但 str_contains() 这类函数依然能用——这不是 bug,是设计使然。
原因在于:bootstrap.php 不是启动即加载,而是靠 function_exists() 检测后按需定义。只要你的代码里第一次调用 str_contains() 时,PHP 还没原生提供(比如 PHP
容易踩的坑:
- 手动
require 'vendor/symfony/polyfill-php80/bootstrap.php'—— 会绕过function_exists()判断,导致 PHP 8+ 环境下重复定义警告 - 在函数调用前就做
function_exists('str_contains')检查 —— 此时它确实还不存在,因为 bootstrap 尚未触发 - 用了 Laravel Octane 或 Swoole 等常驻进程模型 —— bootstrap 只在首个请求执行一次,后续请求复用已定义函数,但若启动时 PHP 版本判断有误,可能漏定义
最稳的验证方式始终是:写一行 var_dump(function_exists('str_contains'));,在真实请求上下文中执行,而不是在 CLI 脚本里静态检查。










