前言
在 PHP 开发中,Composer 已成为现代 PHP 生态的基石。学会编写自己的 Composer 包,不仅是团队代码复用的利器,更是理解现代 PHP 框架设计思路的必经之路。本文以一个实际场景出发,带你完成从「创建包」到「ThinkPHP 项目中使用」的全流程。
一、Composer 是什么
Composer 是 PHP 的依赖管理工具,类似 Node.js 的 npm、Python 的 pip。它的核心能力:
- 声明依赖 :
composer.json声明项目需要哪些包 - 自动加载 :PSR-4 自动加载规范,一行
require即可使用 - 版本控制 :语义化版本(Semantic Versioning),
^1.0.0、~2.1.0
Composer 常用命令
bash
# 初始化项目,生成 composer.json
composer init
# 安装依赖
composer install
# 更新依赖
composer update
# 搜索包
composer search 包名
# 列出已安装的包
composer show
二、从零创建 Composer 包
2.1 项目结构
我们在thinkphp的vendor创建一个示例包 /easy-utils,包含常用工具函数集合:
easy-utils/
├── src/
│ └── EasyUtils/
│ ├── Str.php # 字符串工具
│ └── Arr.php # 数组工具
├── tests/
│ └── EasyUtils/
│ ├── StrTest.php
│ └── ArrTest.php
├── composer.json # 包描述文件
└── README.md
2.2 编写 composer.json
json
{
"name": "easy-utils",
"description": "PHP 常用工具函数集合",
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {
"EasyUtils\\": "src/EasyUtils/"
}
},
"autoload-dev": {
"psr-4": {
"EasyUtils\\Tests\\": "tests/EasyUtils/"
}
},
"require": {
"php": ">=7.4"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"minimum-stability": "dev",
"prefer-stable": true
}
关键字段说明:
| 字段 | 说明 |
|---|---|
name |
包名,格式为 vendor/package,全局唯一 |
autoload.psr-4 |
PSR-4 自动加载规范,命名空间前缀 => 目录 |
minimum-stability |
stable / dev,最低稳定版要求 |
require.php |
PHP 版本要求 |
2.3 编写工具类
src/EasyUtils/Str.php
php
<?php
namespace EasyUtils;
/**
* 字符串工具类
*/
class Str
{
/**
* 生成随机字符串
*
* @param int $length 长度
* @param string $chars 可用字符集
* @return string
*/
public static function random(int $length = 16, string $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'): string
{
$result = '';
$charsLen = strlen($chars) - 1;
for ($i = 0; $i < $length; $i++) {
$result .= $chars[mt_rand(0, $charsLen)];
}
return $result;
}
/**
* 隐藏手机号中间4位
*
* @param string $phone 手机号
* @return string 138****5678
*/
public static function maskPhone(string $phone): string
{
return preg_replace('/(\d{3})\d{4}(\d{4})/', '$1****$2', $phone);
}
/**
* 截取字符串(支持中文)
*
* @param string $string 原始字符串
* @param int $length 截取长度
* @param string $suffix 超过长度的后缀
* @return string
*/
public static function truncate(string $string, int $length = 100, string $suffix = '...'): string
{
if (mb_strlen($string, 'utf-8') <= $length) {
return $string;
}
return mb_substr($string, 0, $length, 'utf-8') . $suffix;
}
}
src/EasyUtils/Arr.php
php
<?php
namespace EasyUtils;
/**
* 数组工具类
*/
class Arr
{
/**
* 从数组中获取值,支持点号语法
*
* @param array $array
* @param string $key 例如 'user.profile.name'
* @param mixed $default 默认值
* @return mixed
*/
public static function get(array $array, string $key, $default = null)
{
$keys = explode('.', $key);
$value = $array;
foreach ($keys as $k) {
if (!is_array($value) || !array_key_exists($k, $value)) {
return $default;
}
$value = $value[$k];
}
return $value;
}
/**
* 将数组转为带缩进的字符串(用于调试)
*
* @param array $array
* @param int $indent 缩进空格数
* @return string
*/
public static function toString(array $array, int $indent = 0): string
{
$lines = [];
foreach ($array as $key => $value) {
$prefix = str_repeat(' ', $indent);
if (is_array($value)) {
$lines[] = "{$prefix}{$key}:";
$lines[] = self::toString($value, $indent + 4);
} else {
$lines[] = "{$prefix}{$key}: " . var_export($value, true);
}
}
return implode("\n", $lines);
}
}
调试
在vendor/composer/autoload_psr4.php中添加一行
php
RealToken\\' => array($vendorDir . '/RealToken/src')
在vendor/composer/autoload_static.php中
在 public static $prefixLengthsPsr4 下添加
php
'E' =>
array (
'EasyUtils\\' => 11,
),
在 public static $prefixDirsPsr4 下添加
```php
'EasyUtils\\' =>
array (
0 => __DIR__ . '/..' . '/RealToken/src',
),
2.4 发布到 Packagist(可选)
发布到 Packagist 后,全世界都可以通过 composer require 安装你的包:
bash
# 1. 在 GitHub 创建仓库并推送代码
git init
git add .
git commit -m "Initial commit"
git remote add origin git@github.com:dengjianping/easy-utils.git
git push -u origin master
# 2. 登录 https://packagist.org,用 GitHub 账号登录
# 3. 点击 "Submit Package",填入仓库地址
# 4. 验证通过后,即可使用
composer require dengjianping/easy-utils
如果包未审核通过,也可以通过私有仓库方式引用:
json
{
"repositories": [
{
"type": "vcs",
"url": "git@github.com:dengjianping/easy-utils.git"
}
],
"require": {
"dengjianping/easy-utils": "dev-master"
}
}
三、ThinkPHP 项目中使用 Composer 包
3.1 创建 ThinkPHP 项目
bash
# 使用 Composer 创建 ThinkPHP 项目
composer create-project topthink/think my-tp-app
# 进入项目目录
cd my-tp-app
# 目录结构
my-tp-app/
├── app/ # 应用目录
│ └── controller/ # 控制器
├── config/ # 配置文件
├── public/ # 入口文件
├── route/ # 路由定义
├── think # 命令行工具
├── vendor/ # Composer 依赖目录
└── composer.json # 依赖声明文件
3.2 安装我们的包
bash
# 方法一:通过 Packagist(发布后)
composer require easy-utils
# 方法二:手动指定仓库(未发布时)
# 编辑 composer.json,加入:
composer config repositories.easy-utils vcs git@github.com:dengjianping/easy-utils.git
composer require dengjianping/easy-utils:dev-master
3.3 在控制器中使用
app/controller/Demo.php
php
<?php
namespace app\controller;
use EasyUtils\Str;
use EasyUtils\Arr;
class Demo
{
/**
* 字符串工具示例
*/
public function strDemo()
{
// 生成随机字符串
$token = Str::random(32);
dump("随机Token: " . $token);
// 隐藏手机号
$phone = Str::maskPhone('13812345678');
dump("手机号脱敏: " . $phone);
// 截取字符串
$intro = Str::truncate('这是一段很长的文字介绍,用于演示字符串截取功能。', 10);
dump("截取结果: " . $intro);
}
/**
* 数组工具示例
*/
public function arrDemo()
{
$user = [
'name' => '张三',
'age' => 28,
'profile' => [
'city' => '北京',
'skill' => ['PHP', 'Go', 'Vue']
]
];
// 使用点号语法获取嵌套值
$city = Arr::get($user, 'profile.city');
$skill = Arr::get($user, 'profile.skill');
$avatar = Arr::get($user, 'profile.avatar', '/default.png'); // 默认值
dump("城市: " . $city);
dump("技能: " . implode(', ', $skill));
dump("头像(默认): " . $avatar);
// 转为可读字符串
$str = Arr::toString($user);
dump("数组内容:\n" . $str);
}
}
3.4 在服务中使用(进阶)
为了更好地与 ThinkPHP 融合,可以创建服务类将工具包接入框架:
app/service/EasyUtilsService.php
php
<?php
namespace app\service;
use EasyUtils\Str;
use EasyUtils\Arr;
use think\Service;
/**
* EasyUtils 服务类
* 注册到框架服务中,与 ThinkPHP 生命周期集成
*/
class EasyUtilsService extends Service
{
public function register()
{
// 可以在这里添加框架级别的初始化逻辑
// 例如:配置文件加载、中间件注册等
}
public function boot()
{
// 应用启动时执行的逻辑
}
}
app/provider.php --- 注册服务提供器:
php
<?php
use app\service\EasyUtilsService;
return [
'EasyUtils' => EasyUtilsService::class,
];
四、在 ThinkPHP 中直接开发自定义包
如果你的工具类仅用于当前项目,不需要独立发布,可以直接在应用目录下开发:
4.1 目录结构
my-tp-app/
├── app/
│ └── common/ # 公共函数库
│ └── util/
│ ├── Str.php
│ └── Arr.php
└── composer.json
4.2 修改 composer.json
json
{
"name": "my-tp-app",
"type": "project",
"autoload": {
"psr-4": {
"app\\\": "app/",
"app\\common\\util\\": "app/common/util/"
}
}
}
更新自动加载:
bash
composer dump-autoload
4.3 完整项目示例:用户管理
app/model/User.php
php
<?php
namespace app\model;
use think\Model;
use app\common\util\Str as StrUtil;
use app\common\util\Arr as ArrUtil;
class User extends Model
{
protected $name = 'user';
/**
* 创建用户时自动处理
*/
public static function createUser(array $data): User
{
// 生成随机盐值
$salt = StrUtil::random(6);
$data['salt'] = $salt;
// 密码加盐哈希
$data['password'] = md5($data['password'] . $salt);
// 脱敏手机号
$data['phone'] = StrUtil::maskPhone($data['phone']);
return self::create($data);
}
/**
* 获取用户信息(带默认值安全访问)
*/
public static function getUserInfo(int $id): array
{
$user = self::find($id);
if (!$user) {
return [];
}
return [
'name' => ArrUtil::get($user->toArray(), 'name', '匿名用户'),
'phone' => ArrUtil::get($user->toArray(), 'phone', ''),
'city' => ArrUtil::get($user->toArray(), 'city', '未知'),
];
}
}
五、常见问题与最佳实践
5.1 常见问题
Q:composer install 和 composer update 的区别?
composer install:安装composer.lock中记录的精确版本(锁定版本)composer update:更新到composer.json允许的最新版本
生产环境建议使用 composer install,保证部署版本与开发一致。
Q:更新单个包?
bash
composer update dengjianping/easy-utils
Q:清理 Composer 缓存?
bash
composer clearcache
5.2 最佳实践
| 实践 | 说明 |
|---|---|
| 语义化版本 | 主版本号不兼容变更,次版本号新增功能,修订号 Bug 修复 |
| PSR-4 规范 | 命名空间与目录结构对应,便于自动加载 |
| 最小依赖 | 只引入真正需要的包,避免依赖膨胀 |
| 写单元测试 | 至少覆盖核心函数,便于回归验证 |
| 代码签名 | 发布时使用 composer sign 对包签名,保证完整性 |
| 使用 .gitignore | 忽略 vendor/ 目录(通过 Composer 安装恢复) |
5.3 composer.json 最佳模板
json
{
"name": "vendor/package",
"description": "简短描述包的功能",
"type": "project",
"license": "MIT",
"authors": [
{
"name": "作者名",
"email": "author@example.com"
}
],
"autoload": {
"psr-4": {
"Vendor\\Package\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Vendor\\Package\\Tests\\": "tests/"
}
},
"require": {
"php": ">=7.4"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"scripts": {
"test": "phpunit",
"post-autoload-dump": [
"@php think clear"
]
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"preferred-install": "dist"
}
}
六、总结
本文从实际需求出发,完成了以下内容:
- 理解 Composer --- PHP 依赖管理的核心工具
- 创建 Composer 包 ---
composer.json配置 + PSR-4 自动加载 + 工具类编写 - 发布到 Packagist --- 让全世界的项目都能使用你的包
- 在 ThinkPHP 中集成 --- 通过
composer require引入并实际使用 - 进阶整合 --- 服务类注册、模型集成、最佳实践
核心逻辑是:包提供能力,框架整合能力。掌握了这套模式,你既可以复用社区优质包,也可以将自己的积累封装为可复用的资产。
参考资料:Composer 官方文档 | Packagist | ThinkPHP6 文档