classmap是遗留代码自动加载的唯一可行起点,适用于无命名空间、类名含下划线、文件分散的老项目;需在composer.json中配置classmap路径,运行composer dump-autoload生成映射,并确保入口统一加载vendor/autoload.php。

classmap 是遗留代码自动加载的唯一可行起点
旧项目没有命名空间、类名含下划线(如 User_model)、文件散落在 lib/ 或 classes/ 下——这时别碰 psr-4,它会直接报错“Class not found”,因为路径和命名空间对不上。唯一能绕过规则、强行让 Composer 认出这些类的,就是 classmap。
操作上分三步:
- 在
composer.json的autoload段写死路径:"classmap": ["lib/", "classes/", "legacy/"] - 运行
composer dump-autoload(不是install)生成映射表;加-o可选,但首次调试建议不加,避免缓存掩盖路径错误 - 入口文件第一行必须是
require __DIR__ . '/vendor/autoload.php';,且要删掉所有旧的require或_autoload()函数,否则类重复定义 fatal error
注意:classmap 不会实时扫描新增文件,每次加了新类,都得再跑一次 dump-autoload。
files 加载全局函数时,顺序和路径容错性极低
遗留系统常有一堆 functions.php、helpers.php 这类无类结构的文件,它们不能塞进 classmap,必须走 files 字段。但这里极易出错:
-
files数组里的路径是相对于composer.json的,写成"src/functions.php"没问题,但写成"./src/functions.php"或"/var/www/src/functions.php"就会静默失败 - 多个函数文件有依赖关系(比如
db.php依赖config.php),必须按执行顺序排列,否则Undefined constant或Call to undefined function - 不支持通配符,想加载整个目录?得自己写个
bootstrap.php,用glob(__DIR__ . '/helpers/*.php')遍历require_once,再把这个bootstrap.php放进files
常见现象:加了 files 却提示函数不存在——八成是路径写错,或没删干净旧的 include 语句,导致加载两次。
require 字段里不能直接填旧包的 ZIP 或本地路径
看到一个老 CMS 插件只有 ZIP 包,想“先放 vendor 再手动 require”?这是最危险的捷径。Composer 不认这种包,后续 update 会清空它,别人拉代码也根本装不上。
正确做法只有两个:
- 如果该包有 GitHub/GitLab 仓库,哪怕没发版,也用
repositories声明:{"type": "vcs", "url": "https://github.com/xxx/old-plugin"},然后require它的 dev-master 分支 - 如果真只有 ZIP,解压后放进项目某目录(如
packages/old-plugin),再在repositories里声明package类型,明确写出name、version、dist地址;否则 Composer 无法解析其元信息
漏掉 repositories 配置就直接 require,结果一定是 Could not find a composer.json file 或 Package not found。
vendor/autoload.php 必须成为唯一加载入口,旧框架逻辑要“收编”而非删除
很多老系统自带一套 navdao.php、core_loader.php 这类引导文件,里面全是 require。这时候不能一把删掉,也不能让它和 vendor/autoload.php 并存——冲突是必然的。
稳妥做法是把旧引导逻辑拆出来,变成一个独立文件(如 legacy/bootstrap.php),然后把它塞进 autoload.files 数组里。这样:
- 所有加载行为统一由 Composer 控制,路径、顺序、执行时机都可预期
- 后续可以逐步把
legacy/bootstrap.php里的require替换为classmap映射,实现渐进式迁移 - 一旦哪天想切掉整套旧逻辑,只需从
files列表里删掉那一行,不用动任何业务代码
最容易被忽略的是:这个“收编”过程必须验证 class_exists('User_model') 和 function_exists('get_user_by_id') 是否返回 true,光看页面不报错不够,得测底层调用链是否真正打通。











