本文目录导读:

- 基础规范:使用
__construct命名 - 使用构造器属性提升(PHP 8.0+)
- 遵守单一职责原则
- 类型声明与参数验证
- 使用命名参数(PHP 8.0+)优化调用方代码
- 依赖注入与控制反转
- 不可变对象:使用只读属性(PHP 8.1+)
- 复杂构造逻辑:静态工厂方法模式
- 异常处理原则
- 完整示例:结合所有规范
在 PHP 项目中,构造方法(Constructor)的规范编写对于代码的可读性、可维护性和扩展性至关重要,以下是基于现代 PHP(7.4+ / 8.x)的最佳实践指南:
基础规范:使用 __construct 命名
始终使用魔术方法 __construct(),不要使用与类同名的旧式构造函数(已弃用)。
// ✅ 正确
class UserService {
public function __construct(private Logger $logger) {}
}
// ❌ 错误(PHP 8.0+ 弃用,8.2+ 移除)
class UserService {
public function UserService($logger) {}
}
使用构造器属性提升(PHP 8.0+)
这是 PHP 8.0 引入的最大亮点之一,能大幅简化代码:
// ✅ 传统方式
class UserController {
private UserRepository $repository;
private Logger $logger;
public function __construct(UserRepository $repository, Logger $logger) {
$this->repository = $repository;
$this->logger = $logger;
}
}
// ✅ 现代方式:构造器属性提升
class UserController {
public function __construct(
private UserRepository $repository,
private Logger $logger,
private ?string $config = null // 可选的 null 类型
) {}
}
遵守单一职责原则
构造方法的职责只有一件事:初始化对象到有效状态。不要在构造方法中:
- 执行复杂的业务逻辑
- 访问数据库或外部 API
- 发送网络请求
// ✅ 正确:只做依赖注入和简单初始化
class OrderProcessor {
public function __construct(
private PaymentGateway $gateway,
private Mailer $mailer,
private float $taxRate = 0.1
) {
$this->validateTaxRate();
}
private function validateTaxRate(): void {
assert($this->taxRate >= 0 && $this->taxRate <= 1, '无效税率');
}
}
// ❌ 错误:在构造方法中执行业务逻辑
class OrderProcessor {
public function __construct(array $orderData) {
$this->connectToDatabase(); // 不要在构造中连接数据库
$this->processOrder($orderData); // 构造不应该处理订单
}
}
类型声明与参数验证
使用强类型声明,并验证关键参数:
class FileManager {
public function __construct(
private string $basePath,
private array $allowedExtensions = ['jpg', 'png']
) {
// 验证必需条件
if (!is_dir($basePath)) {
throw new \InvalidArgumentException("目录不存在: $basePath");
}
$this->validateExtensions();
}
private function validateExtensions(): void {
$invalid = array_filter($this->allowedExtensions, fn($ext) => !preg_match('/^[a-z]+$/', $ext));
if (!empty($invalid)) {
throw new \InvalidArgumentException('无效的文件扩展名: ' . implode(', ', $invalid));
}
}
}
使用命名参数(PHP 8.0+)优化调用方代码
当有多个可选参数时,使用命名参数让调用更清晰:
class Database {
public function __construct(
private string $host = 'localhost',
private int $port = 3306,
private string $username = 'root',
private string $password = '',
private string $database = ''
) {}
}
// 只指定需要的参数,顺序无关
$db = new Database(
host: 'prod.example.com',
database: 'orders',
username: 'admin'
);
依赖注入与控制反转
避免在构造方法中直接 new 对象,使用依赖注入(DI):
// ✅ 使用 DI 容器
class ReportController {
public function __construct(
private ReportService $service, // 由 DI 容器注入
private Request $request
) {}
}
// ❌ 避免:自行实例化 (导致紧耦合)
class ReportController {
private ReportService $service;
public function __construct() {
$this->service = new ReportService(); // 紧耦合,难测试
}
}
不可变对象:使用只读属性(PHP 8.1+)
对于值对象或配置对象,使用 readonly 确保构造后不可变:
readonly class Config {
public function __construct(
public string $apiUrl,
public string $apiKey,
public int $timeout = 30
) {}
}
$config = new Config(apiUrl: 'https://api.example.com', apiKey: 'secret');
// $config->apiUrl = 'new'; // 报错!不可修改
复杂构造逻辑:静态工厂方法模式
如果构造逻辑很复杂或需要多个构造方式,使用静态工厂方法:
class DateTimeRange {
private function __construct(
private \DateTimeImmutable $start,
private \DateTimeImmutable $end
) {
if ($start > $end) {
throw new \InvalidArgumentException('开始时间不能晚于结束时间');
}
}
// 使用命名构造方法更清晰
public static function fromStrings(string $start, string $end): self {
return new self(
new \DateTimeImmutable($start),
new \DateTimeImmutable($end)
);
}
public static function forCurrentWeek(): self {
$now = new \DateTimeImmutable();
return new self(
$now->modify('monday this week'),
$now->modify('sunday this week')
);
}
}
// 使用
$range = DateTimeRange::forCurrentWeek();
异常处理原则
构造方法中的异常应该只表示初始化失败,应抛出 \InvalidArgumentException 或自定义异常:
class ProductFactory {
public function __construct(
private array $productConfig
) {
// 验证结构
if (!isset($productConfig['type'])) {
throw new \InvalidArgumentException('Product config requires a "type" key');
}
}
}
完整示例:结合所有规范
<?php
declare(strict_types=1);
namespace App\Service;
use App\Repository\UserRepository;
use App\Logger\LoggerInterface;
use App\Exception\InvalidConfigException;
readonly class UserService
{
public function __construct(
private UserRepository $repository,
private LoggerInterface $logger,
private string $defaultRole = 'guest',
private int $maxLoginAttempts = 5,
) {
$this->validateDefaultRole();
$this->validateMaxLoginAttempts();
}
private function validateDefaultRole(): void
{
$allowedRoles = ['admin', 'editor', 'guest'];
if (!in_array($this->defaultRole, $allowedRoles, true)) {
throw new InvalidConfigException(
sprintf('Invalid role "%s"', $this->defaultRole)
);
}
}
private function validateMaxLoginAttempts(): void
{
if ($this->maxLoginAttempts < 1 || $this->maxLoginAttempts > 100) {
throw new InvalidConfigException(
'maxLoginAttempts must be between 1 and 100'
);
}
}
}
// 使用
$service = new UserService(
repository: new UserRepository(),
logger: new FileLogger(),
defaultRole: 'editor'
);
| 规范 | 说明 |
|---|---|
| 统一方法名 | 始终使用 __construct() |
| 属性提升 | PHP 8.0 以上用 private readonly 等直接声明 |
| 职责单一 | 只做初始化,不做业务逻辑 |
| 强类型 | 所有参数声明类型 |
| DI 原则 | 接受注入,避免自己 new |
| 只读属性 | 值对象用 readonly 类或属性 |
| 复杂构造 | 用静态工厂方法替代多构造参数 |
| 合理验证 | 验证可预见的无效参数 |
遵循这些规范能让你的 PHP 项目更易于维护、测试和扩展,同时充分利用现代 PHP 的语言特性。