PHP 发展到今天,新特性层出不穷,最佳实践也在不断更新。写出干净、高效、好维护的代码,对每个 PHP 开发者来说都很重要。
这篇文章总结了 PHP 开发中最容易踩的坑,以及对应的解决方案。
不管你是刚入门还是已经写了很多年 PHP,相信都能从中找到有用的东西。
// 错误做法
function isUserActive($status) {
if ($status == true) { // '1', 1, true, 'true' 都会被判断为 true
return true;
}
return false;
}
// 正确做法
function isUserActive(bool $status): bool {
return $status === true;
}
// 错误做法
function getUserName($user) {
return $user['name']; // name 不存在就报错了
}
// 正确做法
function getUserName(array $user): string {
return $user['name'] ?? 'Unknown'; // 用 ?? 操作符兜底
}
// 错误做法
function buildReport(array $items): string {
$report = '';
foreach ($items as $item) {
$report = $report . $item->description; // 每次都创建新字符串
}
return $report;
}
// 正确做法
function buildReport(array $items): string {
$reports = [];
foreach ($items as $item) {
$reports[] = $item->description;
}
return implode('', $reports);
}
// 错误做法
function findUser($db, $userId) {
return $db->query("SELECT * FROM users WHERE id = " . $userId); // 这样写有 SQL 注入风险
}
// 正确做法
function findUser(PDO $db, int $userId): ?array {
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$userId]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// 错误做法
function readConfig($filename) {
$content = file_get_contents($filename); // 文件不存在或读取失败怎么办?
return json_decode($content);
}
// 正确做法
function readConfig(string $filename): array {
if (!file_exists($filename)) {
throw new RuntimeException("文件不存在: $filename");
}
$content = file_get_contents($filename);
if ($content === false) {
throw new RuntimeException("读取文件失败: $filename");
}
$data = json_decode($content, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new RuntimeException("JSON 格式有问题: " . json_last_error_msg());
}
return $data;
}
// 错误做法
function calculateTotal($price, $quantity) {
return $price * $quantity;
}
// 正确做法
function calculateTotal(float $price, int $quantity): float {
return $price * $quantity;
}
// 错误做法
function getImageSize($filename) {
return @getimagesize($filename); // 静默抑制错误
}
// 正确做法
function getImageSize(string $filename): ?array {
if (!file_exists($filename)) {
return null;
}
$size = getimagesize($filename);
return $size !== false ? $size : null;
}
// 错误做法 - 污染全局命名空间
class User {}
class Order {}
// 正确做法
namespace App\Models;
class User {}
namespace App\Orders;
class Order {}
// 错误做法
function processOrder($order) {
try {
// 处理订单
} catch (Exception $e) {
error_log($e->getMessage()); // 通用的异常捕获
}
}
// 正确做法
function processOrder(Order $order): void {
try {
// 处理订单
} catch (DatabaseException $e) {
// 处理数据库相关错误
throw new OrderProcessingException("处理订单时数据库错误", 0, $e);
} catch (ValidationException $e) {
// 处理验证相关错误
throw new OrderProcessingException("订单验证失败", 0, $e);
}
}
// 错误做法
class Customer {
private string $name;
private string $email;
public function __construct(string $name, string $email) {
$this->name = $name;
$this->email = $email;
}
}
// 正确做法
class Customer {
public function __construct(
private string $name,
private string $email,
) {}
}
// 错误做法
function getCountryName($user) {
if ($user !== null &&
$user->getAddress() !== null &&
$user->getAddress()->getCountry() !== null) {
return $user->getAddress()->getCountry()->getName();
}
return null;
}
// 正确做法
function getCountryName(?User $user): ?string {
return $user?->getAddress()?->getCountry()?->getName();
}
// 错误做法
function findUser(array $users, string $email): ?array {
foreach ($users as $user) {
if ($user['email'] === $email) {
return $user;
}
}
return null;
}
// 正确做法
function findUser(array $users, string $email): ?array {
return array_filter(
$users,
fn($user) => $user['email'] === $email
)[0] ?? null;
}
// 错误做法
function processCoordinates($point) {
$x = $point[0];
$y = $point[1];
return sqrt($x * $x + $y * $y);
}
// 正确做法
function processCoordinates(array $point): float {
[$x, $y] = $point;
return sqrt($x * $x + $y * $y);
}
// 错误做法
function isUserActive($lastLoginTimestamp) {
return (time() - $lastLoginTimestamp) < (30 * 24 * 60 * 60);
}
// 正确做法
function isUserActive(DateTime $lastLogin): bool {
$thirtyDaysAgo = new DateTime('-30 days');
return $lastLogin > $thirtyDaysAgo;
}
// 错误做法
function getStatusMessage($status) {
switch ($status) {
case 'pending':
return '订单待处理';
case 'processing':
return '订单处理中';
case 'completed':
return '订单已完成';
default:
return '未知状态';
}
}
// 正确做法
function getStatusMessage(string $status): string {
return match($status) {
'pending' => '订单待处理',
'processing' => '订单处理中',
'completed' => '订单已完成',
default => '未知状态',
};
}
// 错误做法
function createUser($name, $email, $age = null, $country = null) {
// 必须记住参数顺序
return new User($name, $email, null, 'US');
}
// 正确做法
function createUser(
string $name,
string $email,
?int $age = null,
?string $country = null
): User {
return new User(
name: $name,
email: $email,
age: $age,
country: $country ?? 'US'
);
}
// 错误做法
function validateEmail($email) {
return strpos($email, '@') !== false;
}
// 正确做法
function validateEmail(string $email): bool {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
// 错误做法
function processLargeFile($filename) {
$content = file_get_contents($filename); // 将整个文件加载到内存
$lines = explode("\n", $content);
foreach ($lines as $line) {
// 处理每行
}
}
// 正确做法
function processLargeFile(string $filename): void {
$handle = fopen($filename, 'r');
while (($line = fgets($handle)) !== false) {
// 处理每行
}
fclose($handle);
}
// 错误做法
class UserService {
private $db;
public function __construct() {
$this->db = new Database(); // 硬编码依赖
}
}
// 正确做法
class UserService {
public function __construct(
private readonly DatabaseInterface $db
) {}
}
// 错误做法
class CustomException extends Exception {
public function __construct($message) {
parent::__construct($message);
}
}
// 正确做法
class CustomException extends Exception {
public const INVALID_INPUT = 1001;
public const DATABASE_ERROR = 1002;
public const API_ERROR = 1003;
public function __construct(
string $message,
int $code = self::INVALID_INPUT,
?Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
}
}
// 错误做法
function processUser($user) {
if ($user !== null) {
if ($user->isActive()) {
if ($user->hasPermission('admin')) {
// 执行操作
return true;
}
}
}
return false;
}
// 正确做法
function processUser(?User $user): bool {
if ($user === null) {
return false;
}
if (!$user->isActive()) {
return false;
}
if (!$user->hasPermission('admin')) {
return false;
}
// 执行操作
return true;
}
// 错误做法
$items = null;
if (someCondition()) {
$items = [];
}
// 正确做法
$items = [];
if (someCondition()) {
// 填充数组
}
// 错误做法
function processPayment($amount) {
try {
// 处理支付
} catch (Exception $e) {
error_log('支付失败');
}
}
// 正确做法
function processPayment(float $amount): void {
try {
// 处理支付
} catch (PaymentException $e) {
$context = [
'amount' => $amount,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'timestamp' => date('Y-m-d H:i:s')
];
$this->logger->error('支付处理失败', $context);
throw $e;
}
}
// 错误做法
class UserController {
private UserService $userService;
private Logger $logger;
public function __construct() {
$this->userService = new UserService(new Database());
$this->logger = new Logger();
}
}
// 正确做法
class Container {
private array $services = [];
private array $factories = [];
public function register(string $id, callable $factory): void {
$this->factories[$id] = $factory;
}
public function get(string $id): object {
if (!isset($this->services[$id])) {
if (!isset($this->factories[$id])) {
throw new ServiceNotFoundException($id);
}
$this->services[$id] = $this->factories[$id]();
}
return $this->services[$id];
}
}
class UserController {
public function __construct(
private readonly UserService $userService,
private readonly LoggerInterface $logger
) {}
}
// 错误做法
function setPassword($password) {
$this->password = md5($password); // 绝不要用 MD5 处理密码
}
// 正确做法
function setPassword(string $password): void {
$this->password = password_hash(
$password,
PASSWORD_DEFAULT,
['cost' => 12]
);
}
写好 PHP 代码,关键在于细节和对新特性的掌握。避开这些常见的坑,你的代码会更好维护、更安全、性能也更好。
几个要点: