用 Composer 从零构建并发布 PHP 包:完整实战教程

Composer 是 PHP 生态中不可或缺的依赖管理工具,但很多开发者只停留在"使用别人的包"这一阶段。如果你有一段复用性极强的代码——无论是工具函数库、中间件,还是 SDK——将它封装成标准 Composer 包并发布到 Packagist,能让整个团队甚至全球开发者受益。本文将手把手带你完成从零创建包到成功发布的每一步,包括目录规范、自动加载配置、单元测试集成、版本语义化,以及发布时的常见踩坑点。
一、为什么要发布 Composer 包?
很多团队的代码库里都存在大量"内部 utils":重复造轮子、跨项目 copy-paste、版本不同步……这些问题的根源是缺乏统一的包管理机制。发布 Composer 包能带来:
版本可控:通过
composer.json的 version 字段或 Git tag 精确锁定版本依赖隔离:每个包有独立的依赖树,不污染主项目
CI 友好:可为独立包单独跑测试,质量更容易保证
开源沉淀:把内部积累的好代码贡献给社区,建立技术影响力
接下来我们以一个真实场景为例:封装一个 PHP 数组工具库 yourname/arr-helper,提供常用的多维数组操作方法。
二、标准包目录结构与 composer.json 配置
首先创建项目目录并初始化:
mkdir arr-helper && cd arr-helper composer init
composer init 会交互式引导你填写包名、描述、作者信息等。完成后,手动调整 composer.json,使其符合 Packagist 规范:
{
"name": "yourname/arr-helper",
"description": "A collection of useful PHP array utilities",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Your Name",
"email": "you@example.com"
}
],
"require": {
"php": "^8.1"
},
"require-dev": {
"phpunit/phpunit": "^11.0"
},
"autoload": {
"psr-4": {
"YourName\\ArrHelper\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"YourName\\ArrHelper\\Tests\\": "tests/"
}
}
}推荐的目录结构如下:
arr-helper/ ├── src/ │ └── Arr.php # 核心类 ├── tests/ │ └── ArrTest.php # 单元测试 ├── composer.json ├── .gitignore ├── README.md └── LICENSE
⚠️ 踩坑记录:autoload.psr-4 的命名空间末尾必须有反斜杠 \\,路径末尾必须有斜杠 /,否则自动加载会静默失败,调试起来极其痛苦。
三、编写核心类与实际功能
以下是 src/Arr.php 的实现,包含若干常用方法:
<?php
declare(strict_types=1);
namespace YourName\ArrHelper;
class Arr
{
/**
* 用"点号"路径访问多维数组
* 例: Arr::get($data, 'user.profile.name', 'default')
*/
public static function get(array $array, string $key, mixed $default = null): mixed
{
if (array_key_exists($key, $array)) {
return $array[$key];
}
foreach (explode('.', $key) as $segment) {
if (!is_array($array) || !array_key_exists($segment, $array)) {
return $default;
}
$array = $array[$segment];
}
return $array;
}
/**
* 将多维数组"扁平化"为一维,保留点号路径作为 key
*/
public static function flatten(array $array, string $prefix = ''): array
{
$result = [];
foreach ($array as $key => $value) {
$newKey = $prefix !== '' ? $prefix . '.' . $key : (string)$key;
if (is_array($value) && !empty($value)) {
$result = array_merge($result, self::flatten($value, $newKey));
} else {
$result[$newKey] = $value;
}
}
return $result;
}
/**
* 从数组中按条件过滤,支持回调
*/
public static function where(array $array, callable $callback): array
{
return array_values(array_filter($array, $callback));
}
/**
* 将数组按指定字段分组
*/
public static function groupBy(array $array, string $key): array
{
$result = [];
foreach ($array as $item) {
$groupKey = is_array($item) ? ($item[$key] ?? 'null') : 'null';
$result[$groupKey][] = $item;
}
return $result;
}
}以上方法都是真实项目中高频使用的数组操作,尤其是 Arr::get()(灵感来自 Laravel 的 Arr 门面),在处理接口返回的嵌套 JSON 时能大幅减少 isset() 判断。
四、编写 PHPUnit 单元测试
发布到 Packagist 的包没有测试是不负责任的。先安装 PHPUnit:
composer install
创建 tests/ArrTest.php:
<?php
declare(strict_types=1);
namespace YourName\ArrHelper\Tests;
use PHPUnit\Framework\TestCase;
use YourName\ArrHelper\Arr;
class ArrTest extends TestCase
{
public function testGetWithDotNotation(): void
{
$data = ['user' => ['name' => 'Alice', 'age' => 30]];
$this->assertSame('Alice', Arr::get($data, 'user.name'));
$this->assertSame('unknown', Arr::get($data, 'user.email', 'unknown'));
}
public function testFlatten(): void
{
$data = ['a' => ['b' => ['c' => 1]], 'd' => 2];
$flat = Arr::flatten($data);
$this->assertSame(['a.b.c' => 1, 'd' => 2], $flat);
}
public function testGroupBy(): void
{
$data = [
['type' => 'fruit', 'name' => 'apple'],
['type' => 'veggie', 'name' => 'carrot'],
['type' => 'fruit', 'name' => 'banana'],
];
$grouped = Arr::groupBy($data, 'type');
$this->assertCount(2, $grouped['fruit']);
$this->assertCount(1, $grouped['veggie']);
}
}运行测试:
./vendor/bin/phpunit tests/
如果看到 OK (3 tests, 4 assertions),说明代码质量过关,可以继续发布流程。
⚠️ 踩坑记录:PHPUnit 11.x 要求 PHP 8.2+,如果你的 PHP 版本是 8.1,需要指定 "phpunit/phpunit": "^10.0",否则 Composer 会报依赖冲突。
五、推送到 GitHub 并发布到 Packagist
在 GitHub 上创建新仓库(比如 github.com/yourname/arr-helper),然后推送代码:
git init git add . git commit -m "feat: initial release v1.0.0" git remote add origin git@github.com:yourname/arr-helper.git git push -u origin main
打上语义化版本 tag:
git tag v1.0.0 git push origin v1.0.0
然后前往 packagist.org 注册账号,点击 Submit,输入你的 GitHub 仓库地址。Packagist 会自动解析 composer.json 并注册包。
建议同时在 GitHub 仓库 Settings → Webhooks 中添加 Packagist 的 Webhook,这样每次 git push 都会自动同步:
Payload URL: https://packagist.org/api/github Content type: application/json Secret: (your Packagist API token)
发布成功后,任何人都可以通过以下命令安装你的包:
composer require yourname/arr-helper
六、版本语义化与维护建议
Packagist 生态遵循 SemVer(语义化版本)规范,即 主版本.次版本.补丁版本:
补丁版本(1.0.x):向下兼容的 Bug 修复
次版本(1.x.0):新增功能,但不破坏现有 API
主版本(x.0.0):破坏性变更,升级需用户手动迁移
实际维护中还需注意:
在
CHANGELOG.md中记录每个版本的变更,方便用户决策是否升级在
composer.json中添加"abandoned"字段,当包不再维护时及时告知用户为包添加 GitHub Actions 自动测试,确保每个 PR 都经过 CI 验证
避免在
1.x版本中随意删除 public 方法,这属于破坏性变更
一个典型的 GitHub Actions 配置(.github/workflows/tests.yml):
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php: ['8.1', '8.2', '8.3']
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
- run: composer install
- run: ./vendor/bin/phpunit遵循这套流程,你的 Composer 包就具备了专业开源项目的基本素养。从内部工具到社区贡献,只需规范化这一步。
发布评论
热门评论区: