create-project不支持抽象“模板”,仅识别type: "project"的包;若composer.json缺此字段,包会被当作library装入vendor/而非新建项目。

create-project 本身不支持“模板”这个抽象概念,它只认 type: "project" 的包——你拿一个 type: "library" 的包去试,结果一定是被装进 vendor/,而不是生成新项目。
为什么你的自定义模板总被装进 vendor/ 而不是新建目录
根本原因只有一个:composer.json 里没写 "type": "project"。Composer 在执行 create-project 时,会检查目标包的 type 字段:
- 值为
"project"→ 触发骨架复制逻辑:下载、解压、创建新目录、运行composer install - 值为
"library"、"metapackage"或未声明 → 当作普通依赖处理,直接require进当前项目的vendor/ - 即使包结构完全一样,只要
type不对,行为就彻底不同
如何让 create-project 正确识别并使用私有 Git 仓库作为模板
私有仓库不能靠 composer create-project vendor/name 直接拉取,因为 Packagist 上查不到。必须显式指定来源:
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 用
--repository-url指向仓库地址,例如:composer create-project myorg/my-template my-app --repository-url=https://github.com/myorg/my-template.git - 确保该 URL 对应的默认分支(如
main)根目录下有合法composer.json,且含"name": "myorg/my-template"和"type": "project" - Git 认证要提前配好:SSH 密钥已加载,或 HTTPS URL 中嵌入了 PAT(如
https://<font color="red">ghp_abc123</font>@github.com/myorg/my-template.git) - 别漏掉
--stability=dev—— 如果还没打v1.0.0tag,只有dev-main分支,默认是找不到的
post-create-project-cmd 脚本为什么不执行
这个钩子只在「新生成的项目」的 composer.json 中定义才生效,不是在模板源码的某个子目录里写个脚本就行。
- 必须把
"scripts": { "post-create-project-cmd": [...] }写在模板仓库根目录的composer.json顶层字段中 - 大小写敏感,拼成
post-create-project-cmd,少一个连字符或大小写错都不行 - 脚本执行时的工作目录是新建项目根目录(比如
my-app/),原始模板代码已经复制完毕,不存在于当前路径 - 常见错误:脚本里写
cp ../template/.env.example .env—— 这种相对路径会失败,因为模板源码不在现场
模板里怎么安全替换占位符(如 __VENDOR__、__NAME__)
Composer 原生命令不做任何变量替换,所有“自动重命名”都得靠你自己写的脚本实现,而且得避开几个坑:
- 别用
readline()或交互式输入 ——create-project默认加--no-interaction,终端不可读 - 推荐方式:脚本从命令行参数或环境变量取值,例如:
"php bin/replace-placeholders.php --vendor=myorg --name=my-app" - 判断是否首次运行:检查
vendor/autoload.php是否存在,若不存在说明是刚生成的新项目,才执行替换 - 替换范围要明确:只处理
.env.example → .env、config/app.php等有限文件,避免误改第三方代码 - 别在
autoload或require里硬编码占位符名,那会导致解析失败或循环依赖
最常被忽略的一点:模板包自己的 composer.json 里不能写 "require": { "your-own-template": "*" },这会造成循环依赖,create-project 会卡住或报错。

















