最有效禁用扩展的方式是手动删除 vendor/作者名/扩展名 目录并执行 composer dump-autoload;插件则需通过 COMPOSER_DISABLE_PLUGINS=1 临时禁用。

composer禁用某个扩展:直接删掉 vendor 里的对应目录最有效
Composer 本身没有“禁用扩展”的开关,composer install 或 composer update 后,所有依赖都会被加载进 vendor。所谓“禁用”,本质是不让某扩展的代码被自动加载或执行。
常见错误现象:改了 composer.json 但 vendor 里还留着旧扩展,autoload 还在加载它,甚至类名冲突报 Class not found 或 Cannot redeclare class。
- 最干净的做法:手动删掉
vendor/作者名/扩展名目录(比如vendor/myclabs/deep-copy) - 顺手清空 Composer 的 autoloader 缓存:
composer dump-autoload - 如果该扩展被其他包硬依赖(
require而非require-dev),删完后运行composer install会立刻报错 —— 这说明它不是可选的,得从源头移除依赖
临时停用插件:靠 COMPOSER_DISABLE_PLUGINS 环境变量
Composer 插件(如 hirak/prestissimo、phpstan/extension-installer)会在命令执行前自动激活。想临时绕过它们,不用改配置、也不用卸载。
使用场景:调试插件冲突、CI 环境规避网络插件、本地验证是否是插件导致的 composer install 卡住。
- Linux/macOS:
COMPOSER_DISABLE_PLUGINS=1 composer install - Windows(cmd):
set COMPOSER_DISABLE_PLUGINS=1 && composer install - Windows(PowerShell):
$env:COMPOSER_DISABLE_PLUGINS="1"; composer install - 注意:这个变量只影响当前命令,不影响已安装的扩展代码逻辑(比如运行时 require 的类)
为什么不能只注释 composer.json 里的 require?
注释掉某行 "monolog/monolog": "^2.0" 后不运行 composer update,vendor 里依然存在该包 —— Composer 不会主动清理未声明的依赖。
后果很直接:类还在自动加载路径里,class_exists('Monolog\Logger') 仍返回 true,甚至可能被其他包意外调用。
- 注释 +
composer update才能真正移除,但这是“卸载”,不是“禁用” - 如果只是想跳过 autoload,可以手动删
vendor/composer/autoload_psr4.php里对应条目(不推荐,下次dump-autoload就恢复) -
require-dev里的扩展,在生产环境用composer install --no-dev可天然跳过,这才是设计好的“条件启用”方式
插件 vs 扩展:别混淆这两个概念
很多人搜“禁用扩展”实际想停的是插件,但 Composer 里二者机制完全不同:
-
扩展(package):普通 PHP 包,比如
guzzlehttp/guzzle,禁用 = 不让它出现在vendor或不被 autoload 加载 -
插件(plugin):实现了
Composer\Plugin\PluginInterface的包,比如composer/installers,它会劫持install/update流程 —— 这才是COMPOSER_DISABLE_PLUGINS管的范围 - 一个包可以既是扩展又是插件(如
phpstan/extension-installer),此时禁用插件行为 ≠ 卸载扩展本身
容易被忽略的是:有些插件会在 post-autoload-dump 钩子里写文件、改配置,哪怕你设了 COMPOSER_DISABLE_PLUGINS,只要钩子没被禁用(它属于脚本,不是插件生命周期),仍可能生效。真要彻底隔离,得结合 --no-scripts。










