PHP 如何编写可维护代码

wen PHP项目 5

PHP 可维护代码编写指南

遵循编码标准

<?php
// ❌ 不规范的代码
function getdata($id){return DB::table('users')->where('id',$id)->first();}
// ✅ 遵循 PSR-12 标准
function getData(int $id): ?User
{
    return User::find($id);
}

建议采用的标准:

PHP 如何编写可维护代码

  • PSR-12(代码风格)
  • PSR-4(自动加载)
  • PSR-1(基础编码标准)

使用类型提示

<?php
// ❌ 无类型提示
function calculate($a, $b) {
    return $a * $b;
}
// ✅ 强类型提示
function calculate(float $a, float $b): float
{
    return $a * $b;
}
// 标量类型声明
declare(strict_types=1);

单一职责原则

<?php
// ❌ 违反单一职责
class UserService {
    public function register($data) {
        // 验证
        // 创建用户
        // 发送邮件
        // 记录日志
        // 生成报告
    }
}
// ✅ 职责分离
class UserRegistrationService {
    public function register(UserData $data): User
    {
        // 只负责用户注册
    }
}
class EmailService {
    public function sendWelcomeEmail(User $user): void
    {
        // 只负责发送邮件
    }
}

使用依赖注入

<?php
// ❌ 硬编码依赖
class OrderService {
    private $db;
    public function __construct() {
        $this->db = new Database('localhost', 'user', 'pass');
    }
}
// ✅ 依赖注入
class OrderService {
    private $db;
    private $logger;
    public function __construct(DatabaseInterface $db, LoggerInterface $logger) {
        $this->db = $db;
        $this->logger = $logger;
    }
}

善用设计模式

<?php
// 工厂模式
interface PaymentGateway {
    public function pay(float $amount): bool;
}
class StripeGateway implements PaymentGateway {
    public function pay(float $amount): bool
    {
        // Stripe 实现
    }
}
class PayPalGateway implements PaymentGateway {
    public function pay(float $amount): bool
    {
        // PayPal 实现
    }
}
class PaymentFactory {
    public static function create(string $type): PaymentGateway
    {
        switch ($type) {
            case 'stripe':
                return new StripeGateway();
            case 'paypal':
                return new PayPalGateway();
            default:
                throw new InvalidArgumentException("Unsupported payment method");
        }
    }
}

编写文档和注释

<?php
/**
 * 计算订单总价
 *
 * @param array $items 订单商品列表
 * @param float $taxRate 税率
 * @param float $discount 折扣金额
 * 
 * @return float 最终价格
 * @throws InvalidArgumentException 当商品列表为空时
 */
function calculateOrderTotal(array $items, float $taxRate = 0.1, float $discount = 0): float
{
    if (empty($items)) {
        throw new InvalidArgumentException('Order items cannot be empty');
    }
    $subtotal = array_sum(array_column($items, 'price'));
    $total = $subtotal * (1 + $taxRate) - $discount;
    return max(0, $total);
}

使用命名空间和自动加载

<?php
// composer.json 配置
{
    "autoload": {
        "psr-4": {
            "App\\": "src/",
            "App\\Tests\\": "tests/"
        }
    }
}
// 使用命名空间
namespace App\Services;
use App\Repositories\UserRepository;
class UserService {
    // ...
}

错误处理和日志记录

<?php
// ✅ 正确的错误处理
try {
    $user = $this->userRepository->find($id);
    if (!$user) {
        throw new UserNotFoundException("User not found");
    }
} catch (UserNotFoundException $e) {
    $this->logger->error('User not found', ['id' => $id]);
    throw new BusinessException('Invalid user');
} catch (Exception $e) {
    $this->logger->critical('Unexpected error', [
        'exception' => $e,
        'user_id' => $id
    ]);
    throw $e;
}

编写测试

<?php
use PHPUnit\Framework\TestCase;
class UserServiceTest extends TestCase {
    public function testCreateValidUser(): void
    {
        $userData = new UserData('John', 'Doe', 'john@example.com');
        $userService = new UserService();
        $user = $userService->create($userData);
        $this->assertInstanceOf(User::class, $user);
        $this->assertEquals('John', $user->getFirstName());
        $this->assertEquals('john@example.com', $user->getEmail());
    }
}

代码审查清单

可维护性检查项:

  • [ ] 变量命名清晰有意义
  • [ ] 函数和方法简短(不超过20行)
  • [ ] 类遵循单一职责
  • [ ] 无重复代码(DRY原则)
  • [ ] 使用PHP内置函数而非自定义
  • [ ] 正确处理异常和错误
  • [ ] 有清晰的注释和文档
  • [ ] 遵循项目编码标准
  • [ ] 使用类型提示和返回类型
  • [ ] 无魔法数字和字符串

工具推荐

# 代码质量工具
composer require --dev phpstan/phpstan
composer require --dev squizlabs/php_codesniffer
composer require --dev phpunit/phpunit
composer require --dev friendsofphp/php-cs-fixer
# 运行检查
vendor/bin/phpstan analyse src
vendor/bin/phpcs --standard=PSR12 src/
vendor/bin/php-cs-fixer fix src/

实际示例:一个完整的可维护服务

<?php
declare(strict_types=1);
namespace App\Services;
use App\Repositories\Interfaces\UserRepositoryInterface;
use App\Exceptions\UserValidationException;
use Psr\Log\LoggerInterface;
final class UserService
{
    public function __construct(
        private readonly UserRepositoryInterface $repository,
        private readonly LoggerInterface $logger,
        private readonly array $config
    ) {}
    /**
     * 创建新用户
     *
     * @param array $userData 用户数据
     * @return User 创建的用户对象
     * @throws UserValidationException 验证失败时抛出
     */
    public function createUser(array $userData): User
    {
        try {
            // 验证数据
            $this->validateUserData($userData);
            // 检查邮箱是否已存在
            if ($this->repository->findByEmail($userData['email'])) {
                throw new UserValidationException('Email already exists');
            }
            // 创建用户
            $user = $this->repository->create([
                'name' => $userData['name'],
                'email' => $userData['email'],
                'password' => $this->hashPassword($userData['password'])
            ]);
            // 记录成功
            $this->logger->info('User created successfully', [
                'user_id' => $user->getId(),
                'email' => $user->getEmail()
            ]);
            return $user;
        } catch (UserValidationException $e) {
            $this->logger->warning('User creation failed', [
                'error' => $e->getMessage(),
                'data' => $userData
            ]);
            throw $e;
        }
    }
    private function validateUserData(array $data): void
    {
        // 验证逻辑
    }
    private function hashPassword(string $password): string
    {
        return password_hash($password, PASSWORD_DEFAULT);
    }
}

最佳实践总结

  1. 命名要清晰 - 使用有意义的名称
  2. 函数要小 - 单一职责,便于测试
  3. 避免静态方法 - 优先使用依赖注入
  4. 使用异常而非错误码 - 更好的错误处理
  5. 缓存查询结果 - 提高性能
  6. 使用接口 - 解耦实现
  7. 避免全局状态 - 减少副作用
  8. 持续重构 - 保持代码简洁

通过这些实践,你可以编写出易于维护、测试和扩展的PHP代码,可维护性不是一次性的目标,而是持续的改进过程。

抱歉,评论功能暂时关闭!