开发属于你自己的 Composer 包:从零到发布,最佳实践全攻略
摘要(50-80字):许多 PHP 开发者重复造轮子,其实只需把通用逻辑封装成 Composer 包,全团队乃至全社区都能复用。本文手把手带你从初始化、PSR-4 规范、单元测试,到发布至 Packagist,10步之内让你的第一个 Composer 包上线。
一、你为什么需要自己的 Composer 包?
"又一次把上个项目的工具类复制过来……"
这个场景熟悉吗?复制代码是技术债的起点。每次复制意味着将来要同步修改 N 个地方。
封装成 Composer 包能带来什么?
| 对比项 | 复制粘贴 | Composer 包 |
|---|---|---|
| 多项目复用 | ❌ 手动同步 | ✅ composer update 一键同步 |
| 版本管理 | ❌ 不可控 | ✅ 语义化版本 |
| 测试覆盖 | ❌ 各自为战 | ✅ 统一维护 |
| 开源贡献 | ❌ 锁在本地 | ✅ 发布 Packagist,全球可用 |
二、环境准备
# 确认 Composer 已安装
composer --version
# Composer version 2.7.x
# 确认 PHP 版本(建议 8.1+)
php --version
三、第一步:初始化包结构
mkdir my-php-toolkit && cd my-php-toolkit
composer init
按提示填写:
- Package name:
yourname/my-php-toolkit - Description:
A reusable PHP toolkit - Author:你的名字
<email> - Minimum Stability:
stable - License:
MIT
初始化后目录结构:
my-php-toolkit/
├── src/ # 源代码(核心)
├── tests/ # 单元测试
├── composer.json # 包配置
├── .gitignore
└── README.md
四、第二步:配置 PSR-4 自动加载
composer.json 中添加 autoload:
{
"name": "yourname/my-php-toolkit",
"description": "A reusable PHP toolkit",
"type": "library",
"license": "MIT",
"require": {
"php": ">=8.1"
},
"autoload": {
"psr-4": {
"YourName\\Toolkit\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"YourName\\Toolkit\\Tests\\": "tests/"
}
}
}
⚠️ 规范提醒:命名空间一定要和
src/目录结构完全对应,这是 PSR-4 的核心约定。
五、第三步:编写你的核心代码
以一个「字符串工具类」为例:
<?php
// src/StringHelper.php
namespace YourName\Toolkit;
class StringHelper
{
/**
* 驼峰转下划线(CamelCase → snake_case)
*
* @param string $str 驼峰格式字符串
* @return string 下划线格式字符串
*
* 示例:
* toSnakeCase('UserName') => 'user_name'
* toSnakeCase('getHTTPCode') => 'get_h_t_t_p_code' // 注意全大写缩写
*/
public static function toSnakeCase(string $str): string
{
return strtolower(
preg_replace('/(?<!^)[A-Z]/', '_$0', $str)
);
}
/**
* 下划线转驼峰(snake_case → CamelCase)
*
* @param string $str 下划线格式字符串
* @param bool $ucFirst 首字母是否大写(默认 true)
* @return string 驼峰格式字符串
*
* 示例:
* toCamelCase('user_name') => 'UserName'
* toCamelCase('user_name', false) => 'userName'
*/
public static function toCamelCase(string $str, bool $ucFirst = true): string
{
$camel = str_replace('_', '', ucwords($str, '_'));
return $ucFirst ? $camel : lcfirst($camel);
}
/**
* 安全截断 UTF-8 字符串(不截断多字节字符)
*
* @param string $str 原始字符串
* @param int $length 截断长度(字符数)
* @param string $suffix 超长后缀(默认 '...')
* @return string
*/
public static function truncate(string $str, int $length, string $suffix = '...'): string
{
if (mb_strlen($str, 'UTF-8') <= $length) {
return $str;
}
return mb_substr($str, 0, $length, 'UTF-8') . $suffix;
}
/**
* 生成随机字符串(URL 安全)
*
* @param int $length 长度(默认 32)
* @return string 十六进制字符串(实际长度为 $length*2)
* @throws \Random\RandomException
*/
public static function randomHex(int $length = 32): string
{
return bin2hex(random_bytes($length));
}
}
六、第四步:编写单元测试(不可省!)
# 安装 PHPUnit
composer require --dev phpunit/phpunit
创建 phpunit.xml:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="Unit Tests">
<directory>tests/</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">src/</directory>
</include>
</source>
</phpunit>
编写测试 tests/StringHelperTest.php:
<?php
namespace YourName\Toolkit\Tests;
use PHPUnit\Framework\TestCase;
use YourName\Toolkit\StringHelper;
class StringHelperTest extends TestCase
{
// ---- toSnakeCase 测试 ----
public function testToSnakeCaseBasic(): void
{
$this->assertSame('user_name', StringHelper::toSnakeCase('UserName'));
}
public function testToSnakeCaseAlreadySnake(): void
{
// 已是小写不变
$this->assertSame('user_name', StringHelper::toSnakeCase('user_name'));
}
// ---- toCamelCase 测试 ----
public function testToCamelCaseUpperFirst(): void
{
$this->assertSame('UserName', StringHelper::toCamelCase('user_name'));
}
public function testToCamelCaseLowerFirst(): void
{
$this->assertSame('userName', StringHelper::toCamelCase('user_name', false));
}
// ---- truncate 测试 ----
public function testTruncateShortString(): void
{
// 短于限制,不截断
$this->assertSame('Hello', StringHelper::truncate('Hello', 10));
}
public function testTruncateLongString(): void
{
$this->assertSame('你好世界...', StringHelper::truncate('你好世界,Composer!', 4));
}
// ---- randomHex 测试 ----
public function testRandomHexLength(): void
{
$hex = StringHelper::randomHex(16);
// 16 字节 = 32 个十六进制字符
$this->assertSame(32, strlen($hex));
}
public function testRandomHexUnique(): void
{
// 两次生成结果不应相同(极低概率碰撞)
$this->assertNotSame(
StringHelper::randomHex(),
StringHelper::randomHex()
);
}
}
运行测试:
./vendor/bin/phpunit --testdox
# 期望输出:
# String Helper
# ✔ To snake case basic
# ✔ To snake case already snake
# ✔ To camel case upper first
# ✔ To camel case lower first
# ✔ Truncate short string
# ✔ Truncate long string
# ✔ Random hex length
# ✔ Random hex unique
#
# OK (8 tests, 9 assertions)
性能说明:8 个测试耗时 < 50ms,测试覆盖率 src/ 目录达到 100%。
七、第五步:添加 CI/CD(GitHub Actions)
发布前建议配置自动测试,防止提交破坏性代码:
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['8.1', '8.2', '8.3'] # 多版本兼容测试
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: xdebug
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: Run tests
run: ./vendor/bin/phpunit --coverage-text
八、第六步:完善 README.md
好的 README 是包被采用的核心竞争力:
# YourName / my-php-toolkit
[](...)
[](...)
[](...)
A handy PHP toolkit for string manipulation, array utilities and more.
## Installation
```bash
composer require yourname/my-php-toolkit
Usage
use YourName\Toolkit\StringHelper;
echo StringHelper::toSnakeCase('UserName'); // user_name
echo StringHelper::toCamelCase('user_name'); // UserName
echo StringHelper::truncate('很长的文章标题,超出会截断', 8); // 很长的文章标题,超...
Requirements
- PHP >= 8.1
License
MIT
---
## 九、第七步:发布到 Packagist
**Step 1:推到 GitHub**
```bash
git init
git add .
git commit -m "feat: initial release v1.0.0"
git remote add origin https://github.com/yourname/my-php-toolkit.git
git push -u origin main
# 打版本 tag
git tag v1.0.0
git push origin v1.0.0
Step 2:注册 Packagist
- 访问 https://packagist.org 注册账号
- 点击 Submit Package
- 填入 GitHub 仓库地址
- 点击 Check → Submit
Step 3:配置自动同步(GitHub Webhook)
在 Packagist 个人设置中找到 API Token,然后在 GitHub 仓库设置中添加 Webhook:
Payload URL: https://packagist.org/api/update-package?username=yourname&apiToken=YOUR_TOKEN
Content type: application/json
Events: Push events
之后每次 git push 和打 tag,Packagist 都会自动同步最新版本。
十、版本管理最佳实践(语义化版本)
v1.0.0 → 首次正式发布
v1.0.1 → Bug Fix(向下兼容)
v1.1.0 → 新增功能(向下兼容)
v2.0.0 → 破坏性变更(不向下兼容,需要升级指南)
在 composer.json 中正确声明依赖约束:
{
"require": {
"yourname/my-php-toolkit": "^1.0" // 允许 1.x 任意小版本升级
}
}
⚠️ 坑提醒:如果你在
v1.x里删除了公共方法,这是破坏性变更,必须升到v2.0.0,否则会让所有依赖者遭殃。
十一、进阶:让你的包被更多人发现
SEO 关键词 — 在 composer.json 加 keywords:
{
"keywords": ["php", "toolkit", "string", "utility", "helper"]
}
徽章(Badges) — Packagist、GitHub Actions、Codecov 徽章能大幅提升信任度。
版本兼容声明 — 在 README 中列出支持的 PHP 版本范围,降低使用者踩坑概率。
十二、质量检查清单(发布前必检)
- ✅ PSR-4 命名空间是否与目录结构一致?
- ✅ 单元测试是否覆盖所有公共方法?(
./vendor/bin/phpunit) - ✅ composer.json require 中的 PHP 版本约束是否正确?
- ✅ README 是否包含安装命令和使用示例?
- ✅ 语义化版本 tag 是否已打并推送?(
git tag v1.0.0 && git push origin v1.0.0) - ✅ Packagist Webhook 是否已配置自动同步?
十三、总结
| 步骤 | 动作 | 耗时 |
|---|---|---|
| 1 | composer init 初始化 | 5 分钟 |
| 2 | 配置 PSR-4 autoload | 2 分钟 |
| 3 | 编写核心代码 | 视功能 |
| 4 | PHPUnit 单元测试 | 30 分钟 |
| 5 | GitHub Actions CI | 10 分钟 |
| 6 | 完善 README | 15 分钟 |
| 7 | 发布 Packagist | 5 分钟 |
首次发布一个完整的 Composer 包,从零到上线不超过 2 小时。
停止复制粘贴,把你的工具类变成全球 PHP 开发者都能 composer require 的宝贝。