PHP项目异常与finally

wen PHP项目 7

本文目录导读:

PHP项目异常与finally

  1. 异常(Exception)
  2. finally 块
  3. finally 与 return 的交互
  4. 异常与 finally 的典型场景
  5. PHP 8+ 新特性
  6. 常见陷阱与最佳实践

在 PHP 项目中,异常(Exception)finally 是错误处理的关键机制,以下是它们的核心概念、用法及最佳实践。


异常(Exception)

基本结构

try {
    // 可能抛出异常的代码
    if ($somethingWrong) {
        throw new Exception("Something went wrong");
    }
} catch (Exception $e) {
    // 处理异常
    echo "Caught: " . $e->getMessage();
}

异常类层级

  • PHP 内置异常:ExceptionRuntimeExceptionInvalidArgumentException
  • 自定义异常类(推荐继承 Exception
class ValidationException extends Exception {}
class DatabaseException extends Exception {}

多 catch 捕获

try {
    // ...
} catch (ValidationException $e) {
    // 处理验证错误
} catch (DatabaseException $e) {
    // 处理数据库错误
} catch (Exception $e) {
    // 兜底处理
}

finally 块

作用

无论是否抛出异常,finally 块中的代码必定执行,常用于:

  • 关闭数据库连接
  • 释放文件句柄
  • 恢复全局状态
  • 记录日志

语法

try {
    // 可能异常的代码
} catch (Exception $e) {
    // 异常处理
} finally {
    // 总是执行,用于清理资源
    $db->close();
}

示例:文件操作

function readFile(string $path): string {
    $handle = fopen($path, 'r');
    if (!$handle) {
        throw new RuntimeException("Cannot open file");
    }
    try {
        $content = fread($handle, filesize($path));
        return $content;
    } finally {
        fclose($handle); // 确保文件关闭
    }
}

finally 与 return 的交互

关键规则

  • finally 中的 return 会覆盖 try/catch 中的 return
  • finally 中无 return,则返回 try/catch 中的值
function test(): string {
    try {
        return "from try";
    } finally {
        return "from finally"; // 最终返回这个
    }
}
echo test(); // 输出: from finally

最佳实践

  • 不要在 finally 中使用 return(除非有明确意图)
  • 避免在 finally 中抛出异常(会覆盖原始异常)

异常与 finally 的典型场景

数据库事务

$db->beginTransaction();
try {
    $db->query("UPDATE users SET ...");
    $db->query("INSERT INTO logs ...");
    $db->commit();
} catch (Exception $e) {
    $db->rollBack();
    throw $e; // 重新抛出
} finally {
    $db->close(); // 确保连接释放
}

API 响应封装

function apiCall(string $url): array {
    $ch = curl_init($url);
    try {
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            throw new CurlException(curl_error($ch));
        }
        return json_decode($response, true);
    } finally {
        curl_close($ch); // 总是关闭 curl 句柄
    }
}

PHP 8+ 新特性

匹配表达式(Match Expression)

  • 不直接处理异常,但可用于条件判断后抛异常

构造器属性提升 + 异常

class UserController {
    public function __construct(
        private UserRepository $repo
    ) {}
    public function show(int $id): User {
        $user = $this->repo->find($id);
        if (!$user) {
            throw new NotFoundException("User not found");
        }
        return $user;
    }
}

常见陷阱与最佳实践

陷阱 说明 解决方案
吞掉异常(catch 空) catch (Exception $e) {} 隐藏错误 至少记录日志或重新抛出
finally 中抛出异常 会覆盖原始异常 用 try-catch 包裹 finally 代码
过度使用异常做流程控制 性能差且难以维护 只用于真正的错误情况
未捕获的异常导致 500 生产环境应设置全局异常处理器 使用 set_exception_handler()

全局异常处理器示例

set_exception_handler(function (Throwable $e) {
    // 生产环境记录日志,返回友好错误页面
    error_log($e->getMessage());
    http_response_code(500);
    echo json_encode(['error' => 'Internal Server Error']);
});

  • 异常:用于处理运行时错误,通过 try-catch 捕获
  • finally:无论是否异常,保证资源清理代码执行
  • 组合使用:数据库事务、文件操作、API 请求等需要资源管理的场景
  • 避免陷阱:不在 finally 中 return/throw,不吞掉异常

合理使用异常和 finally,能让 PHP 项目更加健壮、可维护。

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