一、从错误报告开始:读懂PHP的报错信息

当你的PHP代码出问题时,第一步不是急着改代码,而是先看清楚错误提示。PHP的错误信息就像医生的诊断报告,能告诉你问题出在哪里。

比如这段代码会触发一个典型错误:

<?php
// 技术栈:PHP 8.1
$price = 100;
echo $price + $undefinedVariable; // 故意使用未定义变量

运行后会看到类似这样的错误:
Warning: Undefined variable $undefinedVariable

关键信息解读:

  1. Warning 表示错误级别(非致命错误)
  2. Undefined variable 告诉你问题类型
  3. $undefinedVariable 直接定位到问题变量

实用技巧

  • 在开发环境设置error_reporting(E_ALL)显示所有错误
  • 使用ini_set('display_errors', 1)确保错误能显示在页面上

二、var_dump与print_r:最基础的调试利器

这两个函数是PHP调试的"瑞士军刀",特别适合查看变量内容。

<?php
// 技术栈:PHP 8.1
$user = [
    'id' => 101,
    'name' => '张三',
    'orders' => [
        ['id' => 'A1001', 'amount' => 299],
        ['id' => 'A1002', 'amount' => 599]
    ]
];

// print_r输出更简洁
print_r($user); 

// var_dump显示更详细信息(包括类型和长度)
var_dump($user['orders']); 

对比选择

  • 需要快速查看数组/对象结构 → print_r
  • 需要知道变量类型和具体值 → var_dump
  • 调试JSON数据时,可以加上JSON_PRETTY_PRINT参数

三、Xdebug:专业开发者的调试神器

Xdebug是PHP的官方调试扩展,提供了:

  • 代码单步执行
  • 断点调试
  • 调用栈追踪

配置示例(php.ini):

[xdebug]
zend_extension=xdebug.so
xdebug.mode=debug
xdebug.client_port=9003
xdebug.start_with_request=yes

实战演示

  1. 在IDE(如PHPStorm)中设置断点
  2. 使用浏览器访问页面
  3. 观察变量值和执行流程
<?php
// 技术栈:PHP 8.1 + Xdebug
function calculateDiscount($total) {
    if ($total > 1000) {
        return $total * 0.9; // 打9折
    }
    return $total;
}

$cart = [
    'items' => [250, 399, 599],
    'user' => 'VIP001'
];

$subtotal = array_sum($cart['items']);
$finalPrice = calculateDiscount($subtotal); // 在此行设置断点

注意事项

  • 生产环境务必关闭Xdebug(影响性能)
  • 远程调试需要配置IDE和服务器网络连通

四、日志记录:永不过时的调试方法

当问题无法在开发环境复现时,日志就是你的"黑匣子记录仪"。

推荐日志方案

<?php
// 技术栈:PHP 8.1 + Monolog
require 'vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// 创建日志实例
$log = new Logger('APP');
$log->pushHandler(new StreamHandler('debug.log', Logger::DEBUG));

// 记录不同级别的信息
$log->info('用户登录', ['user_id' => 101]);
$log->error('数据库连接失败', [
    'error' => $e->getMessage(),
    'trace' => $e->getTraceAsString()
]);

// 在复杂逻辑中插入调试日志
function processOrder($orderId) {
    global $log;
    $log->debug('开始处理订单', ['order_id' => $orderId]);
    // ...业务逻辑
    $log->debug('订单处理完成', ['status' => 'success']);
}

日志最佳实践

  1. 区分日志级别(DEBUG/INFO/WARNING/ERROR)
  2. 记录关键上下文信息(用户ID、请求参数等)
  3. 定期归档和清理日志文件

五、异常处理:优雅地捕获错误

好的异常处理能让调试事半功倍。

<?php
// 技术栈:PHP 8.1
class PaymentException extends \Exception {
    // 自定义异常类
}

function makePayment($amount) {
    if ($amount <= 0) {
        throw new PaymentException("支付金额必须大于0");
    }
    
    try {
        // 模拟支付API调用
        if (rand(0, 10) > 7) {
            throw new Exception("网络超时");
        }
        return "支付成功";
    } catch (Exception $e) {
        // 记录原始异常
        error_log($e->getMessage());
        // 抛出业务异常
        throw new PaymentException("支付处理失败,请重试");
    }
}

// 调用示例
try {
    echo makePayment(100);
} catch (PaymentException $e) {
    echo "友好提示:" . $e->getMessage();
}

异常处理原则

  • 捕获具体异常类型,不要笼统捕获所有Exception
  • 底层异常转换为业务异常再抛出
  • 给最终用户友好的错误提示

六、性能调试:找出慢速代码

当页面加载很慢时,你需要这些工具:

  1. 基础计时
<?php
// 技术栈:PHP 8.1
$start = microtime(true);

// ...执行你的代码

$elapsed = microtime(true) - $start;
echo "执行耗时:{$elapsed}秒";
  1. Xdebug性能分析
[xdebug]
xdebug.mode=profile
xdebug.output_dir=/tmp

生成cachegrind文件后,用KCacheGrind等工具分析:

  • 哪些函数调用次数最多
  • 哪些函数耗时最长

七、数据库调试:SQL查询追踪

数据库问题是常见痛点,可以这样调试:

<?php
// 技术栈:PHP 8.1 + PDO
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
// 启用异常模式
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// 记录所有SQL查询
$pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, [
    'LoggedPDOStatement',
    [$pdo]
]);

class LoggedPDOStatement extends PDOStatement {
    private $pdo;
    
    public function __construct($pdo) {
        $this->pdo = $pdo;
    }
    
    public function execute($params = null) {
        // 记录SQL和参数
        error_log("SQL: " . $this->queryString);
        if ($params) {
            error_log("PARAMS: " . print_r($params, true));
        }
        
        try {
            return parent::execute($params);
        } catch (PDOException $e) {
            error_log("SQL ERROR: " . $e->getMessage());
            throw $e;
        }
    }
}

// 示例查询
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([101]);

更高级的方案

  • 使用ORM的调试模式(如Eloquent的getQueryLog)
  • MySQL的general log(生产环境慎用)

八、HTTP请求调试:查看请求响应

调试API接口时,这些信息很关键:

<?php
// 技术栈:PHP 8.1
function debugCurl($url) {
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HEADER => true, // 包含响应头
        CURLOPT_VERBOSE => true, // 输出详细日志
    ]);
    
    $response = curl_exec($ch);
    
    // 获取调试信息
    $info = curl_getinfo($ch);
    echo "HTTP状态码:" . $info['http_code'] . "\n";
    echo "总耗时:" . $info['total_time'] . "秒\n";
    
    // 分离头部和正文
    list($headers, $body) = explode("\r\n\r\n", $response, 2);
    echo "响应头:\n" . $headers . "\n";
    
    curl_close($ch);
    return $body;
}

// 调用示例
debugCurl('https://api.example.com/users');

常用工具替代方案

  • Postman的"Code generation"功能
  • Guzzle的调试中间件

九、调试技巧总结

  1. 问题定位三板斧

    • 看错误信息
    • 查日志记录
    • 缩小问题范围
  2. 不同场景选择工具

    • 快速查看变量 → var_dump
    • 复杂逻辑调试 → Xdebug
    • 生产环境问题 → 日志分析
  3. 预防胜于治疗

    • 编写单元测试
    • 使用类型声明(PHP 7.0+)
    • 启用严格模式(declare(strict_types=1))

记住,好的开发者不是不写bug,而是能快速找到并解决bug。掌握这些调试技巧,能让你在PHP开发中事半功倍。