在现代 PHP 开发中,代码复用和模块化设计已成为高效开发的关键。随着项目的发展,我们常常需要将特定功能从主项目中抽离出来,形成独立的可复用组件。这种抽离不仅提高了代码的可维护性,还能在多个项目间共享功能,避免重复开发。
不过,在实际开发过程中,我们往往面临这样的困境:如何在不影响主项目开发进度的情况下,同时开发和调试这些独立的组件?传统的方法是修改组件代码,发布新版本,然后在主项目中更新依赖,这个过程繁琐且效率低下。
本指南详细介绍了一套完整的解决方案,从功能抽离的策略,到本地开发环境的搭建,再到 Git 独立管理和最终发布到 Packagist,协助开发者实现高效的组件开发和管理。特别推荐使用符号链接结合通配符配置的最佳实践方案,它既保持了项目结构的清晰,又提供了出色的开发体验,同时解决了 Git 独立管理的问题。
在实际开发中,我们常常遇到以下场景:
主项目需要版本控制:如 Laravel 项目有自己的 Git 仓库,需要跟踪项目特定的修改和配置。
依赖包需要独立版本控制:自定义开发的包(如 PHP 扩展包)也需要独立的 Git 仓库,以便单独发布和维护。
开发过程中需要同时修改:在开发过程中,常常需要同时修改主项目和依赖包的代码,并分别提交到各自的仓库。
避免冲突和混乱:需要确保主项目和依赖包的 Git 历史互不干扰,同时又能保持开发的高效性。
传统方法往往无法很好地解决这些问题,要么开发效率低下,要么版本控制混乱。
一名优秀的高级程序员,不仅能够完成当前的业务需求,还能在开发过程中识别可复用的功能模块,并将其抽离为独立的组件。这种能力是区分初级和高级开发者的重大标志之一。
框架相关包是指专门为特定框架(如 Laravel、Symfony 等)设计的扩展包,它们一般依赖于框架的核心功能或生态系统。
抽离示例:
自定义管理后台模块:
your-name/laravel-admin 包,实现一次开发多处使用特定业务领域组件:
your-name/laravel-shop 包框架功能增强:
your-name/laravel-query-extension 包框架无关包是指不依赖于特定框架的独立功能库,它们可以在任何 PHP 项目中使用,具有更广泛的适用性。
抽离示例:
数据处理工具:
your-name/php-data-utils 包API 客户端:
your-name/alipay-sdk 或 your-name/sms-client 包通用算法实现:
your-name/geo-calculator 或 your-name/text-analyzer 包在实际开发中,何时以及如何抽离功能是一个需要技巧的决策过程:
识别复用机会:
渐进式抽离:
接口设计原则:
版本管理策略:
通过合理的功能抽离和包管理,你可以在不影响主业务开发进度的同时,逐步构建自己的可复用组件库,提高团队的整体开发效率,这正是一名高级程序员应具备的素质和思想。
原理:通过 Composer 的 repositories 配置,将本地路径映射为包的来源。
优点:
缺点:
实施步骤:
准备工作目录结构
创建一个包含主项目和依赖包的工作目录:
mkdir -p workspace/laravel-project
mkdir -p workspace/packages/my-utils-package
克隆或创建项目
# 克隆主项目
git clone https://github.com/your-org/laravel-project.git workspace/laravel-project
# 克隆或创建包
git clone https://github.com/your-name/my-utils-package.git workspace/packages/my-utils-package
配置 composer.json
在主项目的 composer.json 中添加 path 存储库配置:
cd workspace/laravel-project
# 编辑 composer.json
# 可以手动编辑或使用以下命令
composer config repositories.local path ../packages/my-utils-package
composer require your-name/my-utils-package:@dev
composer.json 会包含类似以下内容:
{
"require": {
"your-name/my-utils-package": "@dev"
},
"repositories": [
{
"type": "path",
"url": "../packages/my-utils-package"
}
]
}
安装依赖
composer update your-name/my-utils-package
开发与测试
目前可以在 workspace/packages/my-utils-package 中修改代码,修改会立即反映在主项目中。
推荐缘由:
原理:使用 composer link 命令(需要插件)创建符号链接,将本地开发版本链接到项目的 vendor 目录。
优点:
缺点:
实施步骤:
安装 composer-link 插件
composer global require dnoegel/composer-link
准备包目录
mkdir -p ~/projects/my-utils-package
cd ~/projects/my-utils-package
# 初始化包
composer init --name=your-name/my-utils-package
注册包
composer link register
在主项目中使用链接
cd ~/projects/laravel-project
composer link use your-name/my-utils-package
开发与测试
在 ~/projects/my-utils-package 中修改代码,修改会立即反映在主项目中。
不推荐缘由:
原理:直接在项目的 vendor 目录下修改依赖包代码。
优点:
缺点:
实施步骤:
安装依赖包
cd ~/projects/laravel-project
composer require your-name/my-utils-package
直接修改 vendor 目录中的代码
# 编辑文件
vim vendor/your-name/my-utils-package/src/SomeClass.php
测试修改
在主项目中测试修改后的功能。
保存修改(临时方案)
# 复制修改后的文件到安全位置
cp -r vendor/your-name/my-utils-package /tmp/package-backup
强烈不推荐缘由:
composer update 或 composer install 操作都会覆盖你的修改原理:使用 Git Submodules 将依赖包作为子模块引入主项目。
优点:
缺点:
实施步骤:
添加子模块
cd ~/projects/laravel-project
git submodule add https://github.com/your-name/my-utils-package.git packages/my-utils-package
git commit -m "Add my-utils-package as submodule"
配置 composer.json
在主项目的 composer.json 中添加 path 存储库配置:
{
"require": {
"your-name/my-utils-package": "@dev"
},
"repositories": [
{
"type": "path",
"url": "packages/my-utils-package"
}
]
}
安装依赖
composer update your-name/my-utils-package
克隆项目(团队其他成员)
git clone --recursive https://github.com/your-org/laravel-project.git
# 或者对于已克隆的项目
git submodule update --init --recursive
更新子模块
git submodule update --remote
在子模块中工作
cd packages/my-utils-package
# 修改代码
git add .
git commit -m "Update feature"
git push
# 回到主项目
cd ../..
git add packages/my-utils-package
git commit -m "Update my-utils-package submodule"
git push
不推荐缘由:
原理:使用 Git Subtree 将依赖包的代码合并到主项目的子目录中,同时保持对原始仓库的引用。
优点:
缺点:
实施步骤:
添加远程仓库引用
git remote add -f package-remote https://github.com/your-name/my-utils-package.git
添加 subtree
git subtree add --prefix=packages/my-utils-package package-remote main --squash
--squash 参数将包仓库的历史压缩为一个提交,减少主项目历史的膨胀。
配置 composer.json
在主项目的 composer.json 中添加本地路径:
{
"require": {
"your-name/my-utils-package": "dev-main"
},
"repositories": [
{
"type": "path",
"url": "packages/my-utils-package"
}
]
}
更新依赖包
从原始仓库拉取更新:
git subtree pull --prefix=packages/my-utils-package package-remote main --squash
推送更改回原始仓库
git subtree push --prefix=packages/my-utils-package package-remote feature-branch
不推荐缘由:
原理:Git Worktree 允许在同一个仓库的多个分支上同时工作,每个分支可以检出到不同的目录。
优点:
缺点:
实施步骤:
创建包仓库的工作树
假设你已经有一个包仓库:
cd ~/workspace/my-utils-package
git worktree add ../my-utils-package-feature feature-branch
这会在 ../my-utils-package-feature 目录创建一个新的工作树,检出 feature-branch 分支。
在主项目中使用 path 存储库
在主项目的 composer.json 中配置:
{
"require": {
"your-name/my-utils-package": "dev-feature-branch"
},
"repositories": [
{
"type": "path",
"url": "../my-utils-package-feature"
}
]
}
更新依赖
composer update your-name/my-utils-package
在工作树中开发
在 ../my-utils-package-feature 目录中进行开发,提交更改。
清理工作树
完成开发后,可以删除工作树:
cd ~/workspace/my-utils-package
git worktree remove ../my-utils-package-feature
不推荐缘由:
原理:搭建私有 Composer 仓库,频繁发布开发版本。
优点:
缺点:
实施步骤(简要概述):
搭建私有 Composer 仓库
可以使用 Satis、Packagist Private 或其他私有仓库解决方案:
# 使用 Satis 示例
composer create-project composer/satis
cd satis
# 配置 satis.json
cat > satis.json << EOF
{
"name": "My Composer Repository",
"homepage": "http://localhost:8080",
"repositories": [
{ "type": "vcs", "url": "https://github.com/your-name/my-utils-package" }
],
"require-all": true
}
EOF
# 生成静态网站
php bin/satis build satis.json web/
配置主项目使用私有仓库
{
"repositories": [
{
"type": "composer",
"url": "http://localhost:8080"
}
],
"require": {
"your-name/my-utils-package": "dev-main"
}
}
开发流程
# 修改包代码
cd ~/projects/my-utils-package
# 编辑代码...
# 提交并推送
git add .
git commit -m "Update feature"
git push
# 更新私有仓库索引
cd ~/path/to/satis
php bin/satis build satis.json web/
# 在主项目中更新依赖
cd ~/projects/laravel-project
composer update your-name/my-utils-package
不在本指南主要范围的缘由:
对于需要在企业环境中实施私有包管理的团队,提议参考 Composer 官方文档 或咨询专业的 DevOps 团队,根据特定需求选择和配置适合的解决方案。
| 方案 | 开发效率 | 配置复杂度 | 团队协作 | 版本控制 | 最佳实践符合度 |
|---|---|---|---|---|---|
| path 存储库 | ★★★★★ | ★★☆☆☆ | ★★★★☆ | ★★★★★ | ★★★★★ |
| 符号链接 | ★★★★☆ | ★★★☆☆ | ★★★☆☆ | ★★★★☆ | ★★★☆☆ |
| 修改 vendor | ★★★★☆ | ★☆☆☆☆ | ★☆☆☆☆ | ★☆☆☆☆ | ★☆☆☆☆ |
| Git Submodules | ★★★☆☆ | ★★★★☆ | ★★☆☆☆ | ★★★★★ | ★★★☆☆ |
| Git Subtree | ★★★☆☆ | ★★★★★ | ★★☆☆☆ | ★★★☆☆ | ★★☆☆☆ |
| Git Worktree | ★★★☆☆ | ★★★★★ | ★★☆☆☆ | ★★★☆☆ | ★★☆☆☆ |
| 私有仓库 | ★★☆☆☆ | ★★★★★ | ★★★★★ | ★★★★★ | ★★★★☆ |
为了避免混淆,本指南将三种主要推荐方案的区别和适用场景明确说明如下:
这是最基本的推荐方案,适合初次尝试包开发的开发者。该方案使用 Composer 的 path 存储库功能,将本地路径映射为包的来源。
核心特点:
在基础方案的基础上,此方案重点解决版本控制的问题,确保主项目和依赖包的 Git 历史完全独立。
核心特点:
这是三种方案中最灵活、最强劲的解决方案,解决了基础方案和进阶方案中的路径限制问题,特别适合团队协作和复杂项目。
核心特点:
综合思考开发效率、配置复杂度、团队协作和 Git 独立管理等因素,我们推荐使用 Composer 的 path 存储库方式进行本地开发。
准备工作目录结构
创建一个工作目录,包含主项目和依赖包:
workspace/
├── laravel-project/ # Laravel 主项目
│ └── packages/ # 本地开发的包
│ └── my-utils-package/ # 自定义 PHP 工具包
克隆主项目和依赖包
# 克隆主项目
git clone https://github.com/your-org/laravel-project.git workspace/laravel-project
# 克隆依赖包
git clone https://github.com/your-name/my-utils-package.git workspace/packages/my-utils-package
每个仓库都保持独立的 Git 历史。
配置主项目的 composer.json
在主项目的 composer.json 文件中添加 path 存储库配置:
{
"require": {
"your-name/my-utils-package": "dev-main"
},
"repositories": [
{
"type": "path",
"url": "../packages/my-utils-package",
"options": {
"symlink": true
}
}
]
}
说明:
type: "path" 指定使用本地路径作为包源url 指定包的相对路径options.symlink: true 启用符号链接(而不是复制文件)dev-main 指定使用包的 main 分支(可以根据实际分支名调整)安装依赖
cd workspace/laravel-project
composer update your-name/my-utils-package
Composer 会创建一个从 vendor 目录到本地包的符号链接。
开发与测试
目前你可以在 workspace/packages/my-utils-package 中修改代码,修改会立即反映在主项目中,无需重新安装。
独立 Git 管理
主项目和包可以分别进行 Git 操作:
# 主项目的 Git 操作
cd workspace/laravel-project
git add .
git commit -m "更新主项目配置"
git push
# 依赖包的 Git 操作
cd workspace/packages/my-utils-package
git add .
git commit -m "实现新功能"
git push
两个仓库的 Git 历史完全独立,互不影响。
发布包的新版本
开发完成后,给包打上版本标签并发布:
cd workspace/packages/my-utils-package
git tag v1.0.0
git push --tags
生产环境配置
在生产环境中,移除 path 存储库配置,使用正常的 Packagist 源:
{
"require": {
"your-name/my-utils-package": "^1.0.0"
}
}
Composer 的 path 存储库功能通过以下机制工作:
包发现:Composer 扫描指定的本地路径,查找有效的 composer.json 文件。
版本解析:Composer 将本地包的分支映射为开发版本(如 dev-main),或使用 Git 标签作为版本号。
符号链接:启用 symlink 选项后,Composer 不会复制文件,而是创建从 vendor 目录到本地包的符号链接。
依赖解析:Composer 仍会正常解析和安装本地包的依赖项。
优先级:本地 path 存储库的优先级高于远程存储库,确保本地版本被优先使用。
使用 path 存储库方案,可以实现主项目和依赖包的 Git 独立管理:
物理隔离:主项目和依赖包位于不同的物理目录,各自有独立的 .git 目录。
逻辑连接:通过 Composer 的 path 存储库机制,在开发时将它们逻辑连接。
提交策略:
分支管理:
冲突避免:
使用相对路径:使用相对路径而非绝对路径,便于团队协作。
版本控制:
.gitignore 排除这些配置,或使用环境特定的 composer 配置使用 composer.local.json:
# 创建本地配置
echo {"repositories": [...]} > composer.local.json
# 在主配置中引入
composer config extra.merge-plugin.require composer.local.json
团队协作:
多包开发:对于多个相关包的开发,可以使用 monorepo 或为每个包添加 path 存储库配置。
使用 path 存储库方案,可以实现主项目和依赖包的 Git 独立管理:
物理隔离:主项目和依赖包位于不同的物理目录,各自有独立的 .git 目录。
逻辑连接:通过 Composer 的 path 存储库机制,在开发时将它们逻辑连接。
提交策略:
分支管理:
冲突避免:
使用相对路径:使用相对路径而非绝对路径,便于团队协作。
版本控制:
.gitignore 排除这些配置,或使用环境特定的 composer 配置使用 composer.local.json:
# 创建本地配置
echo {"repositories": [...]} > composer.local.json
# 在主配置中引入
composer config extra.merge-plugin.require composer.local.json
团队协作:
多包开发:对于多个相关包的开发,可以使用 monorepo 或为每个包添加 path 存储库配置。
在实践中,我们发现一种更为灵活和优雅的解决方案,可以完美解决本地开发与生产环境配置的问题,同时保持 Git 仓库的独立性。
这个方案的核心思想是:
这样既能在本地开发中直接修改包代码,又能保持 Git 仓库的独立性,同时生产环境不会由于找不到 packages 路径而报错。
设置主项目的 composer.json
在主项目(如 Laravel)的 composer.json 中添加通配符 path 存储库配置:
{
"require": {
"your-name/my-utils-package": "dev-main"
},
"repositories": [
{
"type": "path",
"url": "packages/*",
"options": {
"symlink": true
}
}
]
}
配置 .gitignore
在主项目的 .gitignore 文件中添加以下内容,忽略 packages 目录下的所有内容,但保留 .gitkeep 文件:
# 忽略 packages 目录下的所有内容
/packages/*
# 但不忽略 .gitkeep 文件
!/packages/.gitkeep
创建 packages 目录结构
# 在主项目中创建 packages 目录
mkdir -p packages
# 创建 .gitkeep 文件以保持目录结构
touch packages/.gitkeep
克隆包仓库到独立目录
将包克隆到主项目目录之外的位置:
# 克隆到主项目外的目录
git clone https://github.com/your-name/my-utils-package.git ~/dev/my-utils-package
创建符号链接
创建从 packages 目录到实际包目录的符号链接:
# 在 Windows 上(需要管理员权限)
mklink /D packagesmy-utils-package C:path odevmy-utils-package
# 在 Linux/Mac 上
ln -s ~/dev/my-utils-package packages/my-utils-package
安装依赖
composer update your-name/my-utils-package
独立开发和提交
由于使用了符号链接,你可以通过两种方式修改包代码:
~/dev/my-utils-package 中修改代码vendor/your-name/my-utils-package 或 packages/my-utils-package 目录中直接修改代码,修改会自动同步到原始包目录无论通过哪种方式修改代码,Git 提交都需要在原始包目录进行:
# 切换到原始包目录进行 Git 操作
cd ~/dev/my-utils-package
git add .
git commit -m "实现新功能"
git push
这种方式的优势在于:
主项目的 Git 操作同样保持独立:
cd workspace/laravel-project
git add .
git commit -m "更新主项目配置"
git push
这种方案相比前面介绍的方法有以下优势:
完全独立的 Git 仓库:包的仓库完全独立于主项目,可以自由切换分支、提交代码。
生产环境兼容性:由于在主项目中保留了 packages/.gitkeep 文件,即使在生产环境中找不到实际的包,也不会报错,Composer 会自动从远程仓库拉取。
灵活的目录结构:包可以存放在开发者偏好的任何位置,不必强制放在主项目的特定相对路径下。
多包开发支持:通过通配符配置 packages/*,可以轻松添加多个开发中的包。
团队协作友善:每个开发者可以根据自己的环境设置符号链接,而不影响其他人。
无需修改提交配置:不需要在开发和生产环境之间切换 composer.json 配置。
开发体验优化:可以直接在主项目中修改包代码,同时保持 Git 提交的独立性,提供最佳的开发体验。
这种方案特别适合需要同时维护多个相互依赖的项目,如 Laravel 主项目和自定义的 PHP 扩展包。它既保证了开发的高效性,又维护了版本控制的清晰性。
在使用符号链接结合通配符配置方案时,可以思考以下性能优化措施:
限制通配符范围:如果你有大量包,但只需要本地开发其中几个,可以使用多个具体的 path 配置而不是单一的通配符:
"repositories": [
{"type": "path", "url": "packages/package-a"},
{"type": "path", "url": "packages/package-b"}
]
使用 Composer 缓存:确保启用 Composer 缓存以加快依赖解析速度:
composer config cache-files-ttl 2592000 # 30天缓存
优化自动加载:在开发环境中定期运行优化命令:
composer dump-autoload -o
选择性符号链接:只为正在积极开发的包创建符号链接,其他包使用正常安装方式。
思考使用 prestissimo 插件:在 Composer 1.x 中,可以使用 prestissimo 插件加速包的安装(Composer 2.x 已内置并行下载功能)。
根据你的具体需求,可以选择最适合的方案:
大多数情况下,我们推荐使用"方案三:符号链接结合通配符配置方案",由于它解决了路径限制问题,并提供了最佳的开发体验。
以 Laravel 框架和自定义 PHP 扩展包为例,演示如何使用最佳实践方案(符号链接结合通配符配置)实现独立管理:
目录结构设置
workspace/
├── laravel-project/ # Laravel 主项目
│ └── packages/ # 包的符号链接目录(在 .gitignore 中忽略)
│ └── .gitkeep # 保留目录结构的空文件
└── dev/ # 独立开发的包目录
└── my-utils-package/ # 自定义 PHP 工具包
克隆仓库
# 克隆 Laravel 项目
git clone https://github.com/your-org/laravel-project.git ~/workspace/laravel-project
# 克隆自定义包到独立目录
git clone https://github.com/your-name/my-utils-package.git ~/workspace/dev/my-utils-package
创建符号链接
# 确保 packages 目录存在
mkdir -p ~/workspace/laravel-project/packages
# 创建 .gitkeep 文件
touch ~/workspace/laravel-project/packages/.gitkeep
# 创建符号链接(Windows 需要管理员权限)
# Windows:
mklink /D C:workspacelaravel-projectpackagesmy-utils-package C:workspacedevmy-utils-package
# Linux/Mac:
ln -s ~/workspace/dev/my-utils-package ~/workspace/laravel-project/packages/my-utils-package
配置 Laravel 的 composer.json
{
"require": {
"your-name/my-utils-package": "dev-main"
},
"repositories": [
{
"type": "path",
"url": "packages/*",
"options": {
"symlink": true
}
}
]
}
配置 .gitignore
在 Laravel 项目的 .gitignore 文件中添加:
# 忽略 packages 目录下的所有内容
/packages/*
# 但不忽略 .gitkeep 文件
!/packages/.gitkeep
安装依赖
cd ~/workspace/laravel-project
composer update your-name/my-utils-package
灵活开发工作流
目前你可以采用以下灵活的开发工作流:
在主项目中直接修改包代码:
# 在 Laravel 项目中编辑包代码
vim ~/workspace/laravel-project/vendor/your-name/my-utils-package/src/SomeClass.php
# 或
vim ~/workspace/laravel-project/packages/my-utils-package/src/SomeClass.php
在原始包目录中修改代码:
# 在原始包目录中编辑代码
vim ~/workspace/dev/my-utils-package/src/SomeClass.php
提交包的修改(无论在哪里修改,都在原始目录提交):
cd ~/workspace/dev/my-utils-package
git add .
git commit -m "实现新功能"
git push
提交主项目的修改:
cd ~/workspace/laravel-project
git add .
git commit -m "更新主项目配置"
git push
发布包新版本
cd ~/workspace/dev/my-utils-package
git tag v1.0.0
git push --tags
生产环境部署
在生产环境中,不需要修改 composer.json,由于:
这种方式提供了最佳的开发体验:
完成本地开发和测试后,下一步是将你的包发布到 Packagist 官方仓库,使其可以被全球 PHP 开发者使用。本节将详细介绍如何将本地开发的包推送到 GitHub 并发布到 Packagist。
在发布包之前,确保你的包满足以下条件:
创建新仓库
在 GitHub 上创建一个新的公开仓库,用于托管你的包代码。
初始化本地仓库(如果尚未初始化)
cd ~/workspace/dev/my-utils-package
git init
git add .
git commit -m "Initial commit"
关联远程仓库
git remote add origin https://github.com/your-name/my-utils-package.git
git push -u origin main
确保你的 composer.json 文件包含以下必要信息:
{
"name": "your-name/my-utils-package",
"description": "A useful package that provides utility functions",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Your Name",
"email": "your.email@example.com"
}
],
"require": {
"php": ">=7.4"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"autoload": {
"psr-4": {
"YourNameMyUtilsPackage": "src/"
}
},
"autoload-dev": {
"psr-4": {
"YourNameMyUtilsPackageTests": "tests/"
}
},
"minimum-stability": "stable"
}
创建 Packagist 账号
访问 Packagist 官网 并注册一个账号(可以使用 GitHub 账号登录)。
提交包
登录后,点击顶部导航栏的 "Submit" 按钮。
输入 GitHub 仓库 URL
在提交表单中,输入你的 GitHub 仓库 URL,例如:
https://github.com/your-name/my-utils-package
检查并确认
Packagist 会自动检查你的 composer.json 文件。如果一切正常,你的包将被添加到 Packagist。
为了让 Packagist 自动更新你的包版本信息,你需要设置 GitHub Webhook:
获取 Packagist API Token
在 Packagist 网站的个人设置页面,找到你的 API Token。
在 GitHub 仓库中设置 Webhook
https://packagist.org/api/github?username=your-packagist-username&apiToken=your-api-token
application/json
使用 GitHub Actions 自动化
或者,你可以创建一个 GitHub Actions 工作流来自动更新 Packagist:
# .github/workflows/packagist.yml
name: Update Packagist
on:
push:
tags:
- *
jobs:
packagist:
runs-on: ubuntu-latest
steps:
- name: Update Packagist
run: |
curl -XPOST -H content-type:application/json
"https://packagist.org/api/update-package?username=${{ secrets.PACKAGIST_USERNAME }}&apiToken=${{ secrets.PACKAGIST_API_TOKEN }}"
-d {"repository":{"url":"https://github.com/your-name/my-utils-package"}}
下面是一个完整的工作流程,从本地开发到最终发布:
本地开发
使用符号链接方案进行本地开发和测试:
# 在本地修改代码
cd ~/workspace/dev/my-utils-package
# 编辑代码...
# 运行测试
composer test
# 提交更改
git add .
git commit -m "实现新功能"
版本管理
使用语义化版本号创建新版本:
# 创建新版本标签
git tag v1.0.0
# 推送标签
git push && git push --tags
自动更新
一旦推送了新的标签,Packagist 会自动更新你的包版本(如果你已设置 Webhook 或 GitHub Actions)。
在项目中使用
目前,任何人都可以通过 Composer 安装你的包:
composer require your-name/my-utils-package
持续更新
继续使用本地开发方案进行开发,并定期发布新版本。
通过这种完整的工作流程,你可以实现从本地开发、Git 独立管理到线上部署的无缝过渡,为全球 PHP 开发者提供高质量的包。
虽然本指南主要关注开源包的开发和发布流程,但在企业环境中,私有仓库一般是必不可少的。以下是关于私有仓库的简要介绍,详细的设置和管理流程不在本指南的主要范围内,由于这些一般涉及到特定的企业需求、安全策略和基础设施配置。
Private Packagist 是 Composer 官方团队提供的私有包托管服务,适用于需要在组织内部共享私有代码的场景。
主要特点:
基本使用流程:
{
"repositories": [
{
"type": "composer",
"url": "https://repo.packagist.com/your-company/"
}
],
"require": {
"your-company/private-package": "^1.0"
}
}
对于不想使用第三方服务的组织,可以直接在 Composer 中配置私有 Git 仓库。
主要方式:
{
"repositories": [
{
"type": "vcs",
"url": "git@your-git-server:your-company/private-package.git"
}
],
"require": {
"your-company/private-package": "^1.0"
}
}
注意事项:
私有仓库的详细设置和最佳实践超出了本指南的范围,由于它们一般需要根据特定组织的需求和基础设施进行定制。如果你需要在企业环境中实施私有包管理,提议参考 Composer 官方文档 或咨询专业的 DevOps 团队。
成功发布包只是开始,长期维护和协作对于包的生命周期至关重大。本节介绍如何建立有效的维护流程和协作机制。
为了鼓励社区贡献并保持代码质量,提议创建以下文档:
CONTRIBUTING.md:详细说明如何贡献代码,包括:
示例内容:
# 贡献指南
感谢您思考为本项目做出贡献!
## 开发环境设置
1. Fork 本仓库
2. 克隆您的 Fork: `git clone https://github.com/YOUR-USERNAME/my-utils-package.git`
3. 安装依赖: `composer install`
## 代码风格
本项目遵循 PSR-12 编码规范。请在提交前运行:
composer cs-check
## 测试
添加或修改功能时,请确保添加相应的测试:
composer test
## Pull Request 流程
1. 确保本地测试通过
2. 推送到您的 Fork
3. 创建 Pull Request 到主仓库的 `develop` 分支
4. 在 PR 描述中详细说明您的更改
CODE_OF_CONDUCT.md:设定社区行为准则,创建友善的协作环境。
ISSUE_TEMPLATE:创建 GitHub Issue 模板,协助用户提供必要信息:
建立清晰的版本更新策略,协助用户理解和适应变更:
语义化版本控制:严格遵循语义化版本规范
更新日志:维护详细的 CHANGELOG.md 文件
废弃策略:
LTS 支持(适用于大型包):
包的安全性对于用户至关重大,尤其是在生产环境中使用的包:
安全漏洞处理流程:
依赖管理:
代码安全实践:
安全扫描集成:
通过建立这些维护和协作机制,你的包将能够吸引更多贡献者,并在长期内保持高质量和安全性。这不仅有利于用户,也有助于建立你在 PHP 社区中的专业声誉。
Q: 如何确保团队中每个人都能正确设置本地开发环境?
A: 创建一个项目初始化脚本,自动设置工作目录结构和符号链接。
Q: 在主项目中修改包代码和在原始包目录修改有什么区别?
A: 技术上没有区别,由于符号链接使它们指向同一文件。选择哪种方式取决于个人工作习惯和当前的工作上下文。在主项目中修改更方便测试,而在原始包目录修改更方便进行 Git 操作。
Q: 如果我在主项目的 vendor 目录中修改了包代码,如何提交这些更改?
A: 需要切换到原始包目录(~/dev/my-utils-package)进行 Git 提交,由于 vendor 目录中的文件只是符号链接到原始文件。
Q: 这种方法会导致 IDE 或编辑器混淆吗?
A: 大多数现代 IDE(如 PHPStorm、VS Code)都能正确处理符号链接,允许你在任一位置编辑文件,并提供正确的代码补全和导航功能。
Q: 符号链接方案在不同操作系统上有兼容性问题吗?
A: Windows 系统需要管理员权限创建符号链接,而 Linux/Mac 则没有这个限制。可以在团队中提供针对不同操作系统的设置脚本。
Q: 使用通配符 path 存储库会影响 Composer 性能吗?
A: 一般不会有明显影响,但如果 packages 目录下有大量包,可能会略微增加 Composer 扫描时间。
Q: 如果我的团队成员没有设置符号链接,会发生什么?
A: Composer 会从远程仓库拉取包,而不使用本地版本。这不会影响功能,但无法实现本地开发。
Q: 如何处理主项目和依赖包之间的 Git 提交冲突?
A: 使用 path 存储库方式,主项目和依赖包的 Git 历史完全独立,不会产生冲突。只需确保不要将本地开发配置提交到主项目的仓库中。
Q: 在持续集成环境中如何处理这种开发设置?
A: CI 环境应使用正式发布的包版本,而不是本地 path 配置。可以使用环境变量或不同的 composer 配置文件区分开发环境和 CI 环境。
Q: 使用 path 存储库后,composer update 会覆盖我的本地修改吗?
A: 不会。使用 symlink 选项时,vendor 中只有符号链接,实际文件仍在你的开发目录中。
Q: 如何在团队中统一路径配置?
A: 使用相对路径,并约定统一的工作目录结构;或使用 composer.local.json 配置本地环境。
Q: 我可以同时开发多个相互依赖的包吗?
A: 可以,为每个包添加 path 存储库配置即可。
Q: 如何切换回使用远程包?
A: 删除 composer.json 中的 repositories 配置,然后运行 composer update。
Q: 在 Windows 系统上使用符号链接有问题,如何解决?
A: 确保以管理员权限运行命令行,或将 "options": {"symlink": true} 改为 "options": {"symlink": false}(会复制文件而非创建链接)。
Q: 如何避免意外将本地开发配置提交到生产环境?
A: 使用 composer.local.json 存储本地配置,并在 .gitignore 中排除它。
Q: 如何确保我的包符合 Packagist 的质量标准?
A: 确保你的包有完整的文档、测试用例、遵循 PSR 标准,并使用语义化版本号。使用 PHP-CS-Fixer 和 PHPStan 等工具来保证代码质量。
Q: 我的包在 Packagist 上发布后,如何查看下载统计?
A: 在 Packagist 网站上,你可以在包的详情页面查看下载统计信息,包括总下载量、月下载量和日下载量。
Q: 如何处理包的依赖关系?
A: 在 composer.json 的 "require" 部分明确声明你的包依赖的其他包和版本要求。避免不必要的依赖,以减少潜在的兼容性问题。
Q: 使用符号链接结合通配符配置方案时,如何处理多个开发者使用不同的本地目录结构?
A: 每个开发者可以将包放在自己喜爱的位置,然后创建符号链接到项目的 packages 目录。由于 packages 目录(除了 .gitkeep)被 .gitignore 排除,不同开发者的本地设置不会相互干扰。
Q: 如何在多个项目中同时开发同一个包?
A: 将包克隆到一个独立位置,然后在每个需要使用该包的项目中创建符号链接到各自的 packages 目录。这样修改包代码时,所有项目都能立即看到变更。
Q: 通配符配置方案如何处理不同分支的开发?
A: 你可以在原始包目录中切换分支,由于使用的是符号链接,主项目会立即使用当前检出的分支代码。要在不同项目中使用不同分支,需要为每个分支创建单独的克隆和符号链接。
Q: 当我需要在生产环境中使用本地修改的包但尚未发布新版本时,有什么最佳实践?
A: 可以临时使用 Git 分支作为版本约束,如 "your-name/my-utils-package": "dev-feature-branch as 1.0.x-dev",这样可以使用特定分支的代码,同时保持语义化版本的兼容性。
Q: 如何在使用符号链接方案的同时保持包的自动加载性能?
A: 定期运行 composer dump-autoload -o 优化自动加载器。在开发环境中,可以使用 --no-scripts 选项加速 composer 更新,如 composer update --no-scripts。
Q: 如何管理包的测试,特别是需要依赖主项目环境的测试?
A: 为包创建独立的测试套件,使用 PHPUnit 的 bootstrap 文件模拟必要的环境。对于需要主项目环境的集成测试,可以在主项目中创建专门的测试用例调用包的功能。
Q: 在使用符号链接结合通配符配置方案时,如何处理包的版本约束?
A: 在开发环境中,使用 "your-name/my-utils-package": "@dev" 或 "dev-main" 来使用最新的开发版本。在生产环境中,改为使用具体的版本约束,如 "^1.0.0"。
Q: 如何确保团队中所有成员都了解并正确使用这种开发方式?
A: 创建详细的开发文档,包括环境设置步骤、Git 工作流程和常见问题解答。为新团队成员提供培训,并思考创建自动化脚本简化设置过程。
Q: 使用符号链接方案时,如何避免意外修改生产代码?
A: 建立明确的开发流程,始终在功能分支上进行开发,并通过 Pull Request 和代码审查流程合并到主分支。使用 Git hooks 防止直接推送到主分支。
Q: 如何处理包中的敏感配置信息?
A: 永远不要在包代码中硬编码敏感信息。使用环境变量或配置注入的方式处理敏感配置,并在文档中明确说明配置要求。
Q: 在大型团队中,如何协调多人同时开发同一个包?
A: 使用功能分支工作流,每个开发者在自己的分支上工作。定期合并主分支的更改以保持同步,并使用 Pull Request 进行代码审查和合并。