软件搬运工
发布于 2026-05-28 / 1 阅读
0
0

开发属于你自己的 Composer 包:从零到发布,最佳实践全攻略

开发属于你自己的 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

[![CI](https://github.com/yourname/my-php-toolkit/actions/workflows/ci.yml/badge.svg)](...)
[![Latest Version](https://img.shields.io/packagist/v/yourname/my-php-toolkit)](...)
[![PHP Version](https://img.shields.io/packagist/php-v/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

  1. 访问 https://packagist.org 注册账号
  2. 点击 Submit Package
  3. 填入 GitHub 仓库地址
  4. 点击 CheckSubmit

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 是否已配置自动同步?

十三、总结

步骤动作耗时
1composer init 初始化5 分钟
2配置 PSR-4 autoload2 分钟
3编写核心代码视功能
4PHPUnit 单元测试30 分钟
5GitHub Actions CI10 分钟
6完善 README15 分钟
7发布 Packagist5 分钟

首次发布一个完整的 Composer 包,从零到上线不超过 2 小时。

停止复制粘贴,把你的工具类变成全球 PHP 开发者都能 composer require 的宝贝。


评论