Composer create-project 仅识别 type: "project" 的包作为模板,否则会误装入 vendor/;必须在模板包的 composer.json 中正确定义 type 和 post-create-project-cmd 脚本,且通过合法 VCS 仓库发布。

composer create-project 本身并不支持“模板文件”这个概念,它只认包(package)。你得把模板做成一个可安装的 type: "project" 包,否则所有所谓的“自定义模板”操作都会失败,或者干脆被误装进 vendor 目录。
为什么你的模板总是被装进 vendor/ 而不是生成新项目?
问题其实出在包的 "type" 字段上。Composer 只有在识别到 "type": "project" 时,才会触发 create-project 的骨架复制逻辑;否则一律按普通库处理,直接 require 进 vendor/。这就像你让快递员送一个“项目模板”,结果他一看标签没写清楚,直接当成普通包裹塞进了仓库。
"type": "library"(默认值)或未声明type→ 模板包被当成依赖装进vendor/your-vendor/template,毫无意义。"type": "project"→ Composer 会克隆整个仓库到目标目录,清空 .git,执行脚本,这才是你想要的效果。- 别信“只要带
composer.json就能当模板”——没有type,就等于没有身份证。
post-create-project-cmd 脚本不执行?检查这三个地方
这个钩子只在新项目(即目标目录)的 composer.json 里定义才生效,而不是在模板源码的 composer.json 里写。不少开发者在这里栽了跟头。
- 脚本必须写在模板包自己的
composer.json中,且字段名是"post-create-project-cmd"(注意拼写和大小写)。 - 执行时工作目录是新建项目的根目录,原始模板仓库已不存在——不能用
../template/src这类相对路径读文件。 - 命令中若调用 CLI 工具(如
php artisan key:generate),需确保该工具已在新项目中可用(即已通过require声明或自带)。
私有模板怎么用?别硬塞 --repository
用 --repository 手动指定 URL 容易出错,尤其当模板未发布到 Packagist 时,更推荐显式声明 VCS 类型 + 使用完整包名。这就像你给导航软件一个模糊地址,它大概率会把你带偏。
- 正确姿势:
composer create-project your-vendor/my-template my-app --repository='{"type":"vcs","url":"https://gitlab.internal/my-template"}' - 错误姿势:
composer create-project --repository=https://gitlab.internal/my-template.git ...(URL 不是合法 repository 配置) - 如果模板在 GitHub 私有库,确保当前机器已配置 SSH key 或 token 认证,否则 clone 会失败并静默退出。
占位符替换别靠用户手动改
模板里写 "App\": "src/" 没问题,但像 APP_NAME=MyProject 这种变量,不能指望开发者去改 .env。好的工具应该自动完成这些繁琐工作。
- 用
post-create-project-cmd自动 copy + 替换:@php -r "file_put_contents('.env', str_replace('MY_APP', 'my-app', file_get_contents('.env.example')));" - 避免在模板中写
"require": { "your-vendor/my-template": "*" }—— 这会造成循环依赖,create-project直接报错。 - 打 tag 要明确:
v1.0.0比dev-main更稳定;私有库记得同步 tag 到服务器,否则create-project your-vendor/my-template my-app v1.0.0会找不到版本。
真正卡住人的从来不是命令怎么敲,而是搞不清 create-project 实际上是在“安装一个包”,而不是“复制一堆文件”。模板仓库的 composer.json 必须能独立安装、运行、被索引——它就是一个普通包,只是用途特殊而已。