本文目录导读:

在PHP项目中抽取公共方法,主要遵循 DRY(Don't Repeat Yourself) 原则,以下是具体的操作步骤和最佳实践:
识别可抽取的重复代码
首先找到重复的模式,
// 重复的数据库查询
$user1 = $db->query("SELECT * FROM users WHERE id = 1");
// 处理 $user1...
$user2 = $db->query("SELECT * FROM users WHERE id = 2");
// 处理 $user2...
// 重复的日期格式化
$date1 = date('Y-m-d H:i:s', strtotime($order1['created_at']));
$date2 = date('Y-m-d H:i:s', strtotime($order2['created_at']));
基础抽法方法
1 简单函数抽取
// 抽取前
$price1 = $product1['price'] * 1.2;
$discount1 = $price1 > 100 ? $price1 * 0.9 : $price1;
$price2 = $product2['price'] * 1.2;
$discount2 = $price2 > 100 ? $price2 * 0.9 : $price2;
// 抽取后
function calculateDiscountedPrice($originalPrice): float
{
$price = $originalPrice * 1.2;
return $price > 100 ? $price * 0.9 : $price;
}
$discount1 = calculateDiscountedPrice($product1['price']);
$discount2 = calculateDiscountedPrice($product2['price']);
2 带有默认参数的函数
/**
* 格式化用户显示名
*/
function formatUserName(string $firstName, ?string $lastName = ''): string
{
return trim($firstName . ' ' . $lastName);
}
// 使用
echo formatUserName('John', 'Doe'); // "John Doe"
echo formatUserName('张三'); // "张三"
面向对象抽法
1 使用静态方法(工具类)
class StringHelper
{
public static function truncate(string $text, int $length = 100, string $suffix = '...'): string
{
if (mb_strlen($text) <= $length) {
return $text;
}
return mb_substr($text, 0, $length) . $suffix;
}
public static function slugify(string $text): string
{
$text = preg_replace('/[^\p{L}\p{N}]+/u', '-', $text);
$text = trim($text, '-');
return mb_strtolower($text);
}
}
// 使用
$articleExcerpt = StringHelper::truncate($article['content'], 150);
$cleanUrl = StringHelper::slugify('My Article Title');
2 使用继承(父类提供公共方法)
abstract class BaseController
{
protected function jsonResponse($data, int $statusCode = 200): void
{
header('Content-Type: application/json');
http_response_code($statusCode);
echo json_encode($data);
exit;
}
protected function validateRequiredFields(array $data, array $required): array
{
$missing = [];
foreach ($required as $field) {
if (!isset($data[$field]) || empty($data[$field])) {
$missing[] = $field;
}
}
return $missing;
}
}
class UserController extends BaseController
{
public function create(): void
{
$input = json_decode(file_get_contents('php://input'), true);
$missing = $this->validateRequiredFields($input, ['name', 'email']);
if (!empty($missing)) {
$this->jsonResponse(['error' => '缺少字段', 'fields' => $missing], 400);
return;
}
// ... 创建用户逻辑
}
}
3 使用Trait(横向复用)
trait Loggable
{
protected function log(string $message, string $level = 'info'): void
{
$logFile = __DIR__ . '/../logs/app.log';
$logEntry = sprintf(
"[%s] [%s] %s\n",
date('Y-m-d H:i:s'),
strtoupper($level),
$message
);
file_put_contents($logFile, $logEntry, FILE_APPEND);
}
}
trait Cacheable
{
private array $cache = [];
protected function remember(string $key, callable $callback, int $ttl = 3600)
{
if (isset($this->cache[$key])) {
return $this->cache[$key];
}
$result = $callback();
$this->cache[$key] = $result;
return $result;
}
}
class DataProcessor
{
use Loggable, Cacheable;
public function processExpensiveData(int $id): array
{
return $this->remember("data_{$id}", function () use ($id) {
$this->log("Processing data for ID: {$id}");
// 处理数据的逻辑...
return ['id' => $id, 'processed' => true];
});
}
}
依赖注入与服务类
1 定义服务类
class EmailService
{
private string $smtpHost;
private string $apiKey;
public function __construct(string $smtpHost, string $apiKey)
{
$this->smtpHost = $smtpHost;
$this->apiKey = $apiKey;
}
public function send(string $to, string $subject, string $body): bool
{
// 发送邮件的逻辑
return true;
}
public function sendWelcomeEmail(string $email, string $name): bool
{
$subject = 'Welcome to our platform!';
$body = "Hello {$name},\n\nWelcome!";
return $this->send($email, $subject, $body);
}
}
2 使用依赖注入
class UserService
{
private EmailService $emailService;
private Database $db;
public function __construct(EmailService $emailService, Database $db)
{
$this->emailService = $emailService;
$this->db = $db;
}
public function registerUser(array $userData): User
{
// 创建用户
$user = $this->db->insert('users', $userData);
// 发送欢迎邮件
$this->emailService->sendWelcomeEmail($user['email'], $user['name']);
return $user;
}
}
高级模式:策略模式
interface PaymentStrategy
{
public function pay(float $amount): bool;
}
class CreditCardPayment implements PaymentStrategy
{
public function pay(float $amount): bool
{
echo "Processing credit card payment of {$amount}\n";
return true;
}
}
class PayPalPayment implements PaymentStrategy
{
public function pay(float $amount): bool
{
echo "Processing PayPal payment of {$amount}\n";
return true;
}
}
class PaymentHandler
{
private PaymentStrategy $strategy;
public function __construct(PaymentStrategy $strategy)
{
$this->strategy = $strategy;
}
public function processPayment(float $amount): bool
{
// 通用的验证逻辑
if ($amount <= 0) {
throw new InvalidArgumentException('Amount must be positive');
}
// 使用策略执行支付
return $this->strategy->pay($amount);
}
}
抽取公共方法的最佳实践
1 命名规范
// ✅ 好的命名
function formatCurrency(float $amount): string {}
function validateEmail(string $email): bool {}
function generateUniqueId(): string {}
// ❌ 差的命名
function doStuff(): mixed {}
function processStuff(array $data): mixed {}
function h($v): string {} // 不明确的缩写
2 参数设计原则
// ✅ 好的设计:默认值 + 类型提示
function sendNotification(
string $recipient,
string $message,
string $type = 'email',
bool $urgent = false
): bool {
// ...
}
// ❌ 避免过多的参数
function createOrder(
$userId, $productId, $quantity, $address,
$paymentMethod, $couponCode = null,
$notes = null, $giftWrapping = false
) {
// 使用参数对象代替
}
3 使用参数对象
class OrderRequest
{
public function __construct(
public readonly int $userId,
public readonly int $productId,
public readonly int $quantity,
public readonly string $address,
public readonly string $paymentMethod,
public readonly ?string $couponCode = null,
public readonly bool $giftWrapping = false
) {}
}
// 使用
function createOrder(OrderRequest $request): Order
{
// 实现逻辑
}
重构示例:完整流程
重构前(重复代码)
// UserController.php
class UserController
{
public function createUser()
{
$input = json_decode(file_get_contents('php://input'), true);
// 验证
$errors = [];
if (!isset($input['name']) || strlen($input['name']) < 2) {
$errors[] = 'Name is too short';
}
if (!isset($input['email']) || !filter_var($input['email'], FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Invalid email';
}
if (!isset($input['password']) || strlen($input['password']) < 6) {
$errors[] = 'Password must be at least 6 characters';
}
if (!empty($errors)) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'errors' => $errors]);
exit;
}
// 保存到数据库
$this->db->query("INSERT INTO users (name, email, password) VALUES (?, ?, ?)",
[$input['name'], $input['email'], password_hash($input['password'], PASSWORD_DEFAULT)]);
// 返回响应
header('Content-Type: application/json');
echo json_encode(['success' => true, 'message' => 'User created']);
}
public function updateUser($id)
{
$input = json_decode(file_get_contents('php://input'), true);
// 验证(重复!)
$errors = [];
if (isset($input['name']) && strlen($input['name']) < 2) {
$errors[] = 'Name is too short';
}
if (isset($input['email']) && !filter_var($input['email'], FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Invalid email';
}
if (isset($input['password']) && strlen($input['password']) < 6) {
$errors[] = 'Password must be at least 6 characters';
}
if (!empty($errors)) {
$this->jsonResponse(['success' => false, 'errors' => $errors], 400);
exit;
}
// 更新数据库
$this->db->query("UPDATE users SET name = ?, email = ? WHERE id = ?",
[$input['name'], $input['email'], $id]);
// 返回响应(重复)
$this->jsonResponse(['success' => true, 'message' => 'User updated']);
}
private function jsonResponse($data, $statusCode = 200)
{
header('Content-Type: application/json');
http_response_code($statusCode);
echo json_encode($data);
}
}
重构后(抽取公共方法)
// Validator.php
class Validator
{
public static function validate(array $data, array $rules): array
{
$errors = [];
foreach ($rules as $field => [$rule, $message]) {
$value = $data[$field] ?? null;
switch ($rule) {
case 'required':
if (empty($value)) {
$errors[$field][] = $message ?? "{$field} is required";
}
break;
case 'min_length':
if (isset($value) && strlen($value) < $message) {
$errors[$field][] = "{$field} must be at least {$message} characters";
}
break;
case 'email':
if (isset($value) && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
$errors[$field][] = "{$field} is not a valid email";
}
break;
}
}
return $errors;
}
}
// UserService.php
class UserService
{
private Database $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function createUser(array $data): User
{
$this->db->query(
"INSERT INTO users (name, email, password) VALUES (?, ?, ?)",
[$data['name'], $data['email'], password_hash($data['password'], PASSWORD_DEFAULT)]
);
return new User($this->db->lastInsertId(), $data);
}
public function updateUser(int $id, array $data): bool
{
$fields = [];
$values = [];
foreach (['name', 'email', 'password'] as $field) {
if (isset($data[$field])) {
$fields[] = "{$field} = ?";
$values[] = $field === 'password' ? password_hash($data[$field], PASSWORD_DEFAULT) : $data[$field];
}
}
if (empty($fields)) {
return false;
}
$values[] = $id;
$sql = "UPDATE users SET " . implode(', ', $fields) . " WHERE id = ?";
return $this->db->query($sql, $values);
}
}
// UserController.php(重构后)
class UserController extends BaseController
{
private UserService $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function createUser(): void
{
$input = $this->getJsonInput();
$rules = [
'name' => ['required', 'min_length' => 2],
'email' => ['required', 'email'],
'password' => ['required', 'min_length' => 6],
];
$errors = Validator::validate($input, $rules);
if (!empty($errors)) {
$this->jsonResponse(['success' => false, 'errors' => $errors], 400);
return;
}
try {
$user = $this->userService->createUser($input);
$this->jsonResponse(['success' => true, 'user' => $user->toArray()], 201);
} catch (\Exception $e) {
$this->jsonResponse(['success' => false, 'message' => $e->getMessage()], 500);
}
}
public function updateUser(int $id): void
{
$input = $this->getJsonInput();
$rules = [
'name' => ['min_length' => 2],
'email' => ['email'],
'password' => ['min_length' => 6],
];
$errors = Validator::validate($input, $rules);
if (!empty($errors)) {
$this->jsonResponse(['success' => false, 'errors' => $errors], 400);
return;
}
try {
$this->userService->updateUser($id, $input);
$this->jsonResponse(['success' => true, 'message' => 'User updated']);
} catch (\Exception $e) {
$this->jsonResponse(['success' => false, 'message' => $e->getMessage()], 500);
}
}
}
抽取公共方法的关键步骤:
- 识别重复:找到代码中相同或类似的逻辑块
- 分析抽象:确定哪些部分是变化的,哪些是固定的
- 选择方式:根据场景选择函数、静态方法、继承、Trait或服务类
- 参数设计:考虑可扩展性,使用默认值和类型提示
- 测试确保:重构后确保功能不变
不要过早抽象,三次原则”比较合理——在代码重复出现三次后才进行抽象。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播