2025 PHP 开发者必看得 25 个容易犯的常见错误 90% 的开发者都踩过
前言
PHP 发展到今天,新特性层出不穷,最佳实践也在不断更新。写出干净、高效、好维护的代码,对每个 PHP 开发者来说都很重要。
这篇文章总结了 PHP 开发中最容易踩的坑,以及对应的解决方案。
不管你是刚入门还是已经写了很多年 PHP,相信都能从中找到有用的东西。
原文链接-2025 PHP 开发者必看得 25 个容易犯的常见错误 90% 的开发者都踩过
常见错误和解决方案
用 == 比较,结果不符合预期
- // 错误做法
- 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);
- }
复制代码 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 安全指南 - 安全编程必读
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |