2025 PHP 开发者必看得 25 个容易犯的常见错误 90% 的开发者都踩过
前言
PHP 发展到今天,新特性层出不穷,最佳实践也在不断更新。写出干净、高效、好维护的代码,对每个 PHP 开发者来说都很重要。
这篇文章总结了 PHP 开发中最容易踩的坑,以及对应的解决方案。
不管你是刚入门还是已经写了很多年 PHP,相信都能从中找到有用的东西。
原文链接-2025 PHP 开发者必看得 25 个容易犯的常见错误 90% 的开发者都踩过
常见错误和解决方案
用 == 比较,结果不符合预期
// 错误做法
function isUserActive($status) {if ($status == true) { // '1', 1, true, 'true' 都会被判断为 truereturn 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);
}
SQL 语句直接拼接参数,有注入风险
// 错误做法
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);}
}
没用 PHP 8 的构造器属性提升
// 错误做法
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,) {}
}
不用 PHP 8 的空安全操作符 ?->
// 错误做法
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);
}
日期处理用时间戳,不用 DateTime
// 错误做法
function isUserActive($lastLoginTimestamp) {return (time() - $lastLoginTimestamp) < (30 * 24 * 60 * 60);
}// 正确做法
function isUserActive(DateTime $lastLogin): bool {$thirtyDaysAgo = new DateTime('-30 days');return $lastLogin > $thirtyDaysAgo;
}
还在用 switch,不用 PHP 8 的 match
// 错误做法
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 => '未知状态',};
}
不用 PHP 8 的命名参数
// 错误做法
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);
}
在类里直接 new 对象,不用依赖注入
// 错误做法
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;
}
数组初始化为 null,后面可能出错
// 错误做法
$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) {}
}
用 MD5 等弱算法存储密码
// 错误做法
function setPassword($password) {$this->password = md5($password); // 绝不要用 MD5 处理密码
}// 正确做法
function setPassword(string $password): void {$this->password = password_hash($password,PASSWORD_DEFAULT,['cost' => 12]);
}
总结
写好 PHP 代码,关键在于细节和对新特性的掌握。避开这些常见的坑,你的代码会更好维护、更安全、性能也更好。
几个要点:
- 用严格类型,给函数加上类型声明
- PHP 8 的新特性很好用,别浪费了
- 安全问题不能马虎,SQL 注入、密码存储都要注意
- 性能优化要有针对性,别过早优化
- 代码要写得清晰,别人能看懂
- SOLID 原则还是很有用的
- 错误处理要到位,别让程序莫名其妙挂掉
- 日志要记得详细点,出问题好排查
- 代码要能测试,不然改起来心里没底
- PHP 更新很快,要跟上节奏
推荐资源
- PHP 官方文档 - 最权威的参考
- PHP 标准建议 (PSR) - 社区规范
- PHP The Right Way - 最佳实践合集
- 《现代 PHP》等书籍
- PHP 安全指南 - 安全编程必读