Variable Overwriting Bug: When $data Means Two Different Things

📖 2 minutes read

Reusing variable names seems harmless until you’re deep in a debugging session wondering why your logs don’t match reality. Here’s a common trap and how to avoid it.

The Bug

You’re calling a payment gateway API. You build the request payload, send it, get a response. Standard stuff:

public function processPayment($amount, $currency)
{
    $data = [
        'amount' => $amount,
        'currency' => $currency,
        'merchant_id' => config('payment.merchant_id'),
    ];

    Log::info('Sending payment request', ['data' => $data]);

    $response = Http::post('https://api.paymentgateway.com/charge', $data);
    
    $data = $response->json();
    
    if ($data['status'] === 'success') {
        Log::info('Payment succeeded', ['data' => $data]);
        return $data['transaction_id'];
    }
    
    Log::error('Payment failed', ['data' => $data]);
    throw new PaymentException($data['message']);
}

Looks fine. But when you check logs after an error, you see:

Sending payment request: {"status":"failed","message":"Invalid merchant"}
Payment failed: {"status":"failed","message":"Invalid merchant"}

Wait, what? The “sending” log shows the response, not the request.

What Happened

You logged $data before the API call, but $data got overwritten by the response on line 11. Laravel’s logger is lazy—it doesn’t serialize variables immediately. When the log actually writes, $data now holds the response, not the request.

Result: both log entries show the same value (the response), making debugging a nightmare.

The Fix: Use Different Variable Names

Don’t reuse $data for two conceptually different things:

public function processPayment($amount, $currency)
{
    $payload = [
        'amount' => $amount,
        'currency' => $currency,
        'merchant_id' => config('payment.merchant_id'),
    ];

    Log::info('Sending payment request', ['payload' => $payload]);

    $response = Http::post('https://api.paymentgateway.com/charge', $payload);
    
    $responseData = $response->json();
    
    if ($responseData['status'] === 'success') {
        Log::info('Payment succeeded', ['response' => $responseData]);
        return $responseData['transaction_id'];
    }
    
    Log::error('Payment failed', ['response' => $responseData]);
    throw new PaymentException($responseData['message']);
}

Now your logs are correct:

Sending payment request: {"amount":1000,"currency":"USD","merchant_id":"123"}
Payment failed: {"status":"failed","message":"Invalid merchant"}

Why This Matters

  • Logs are your debugging lifeline. If they lie, you’re lost.
  • $data, $result, $response — these names are magnets for reuse.
  • Lazy logging (common in Laravel, Monolog, etc.) means variable references get resolved later, not when you call Log::info().

Rule of Thumb

If you’re logging a variable, don’t overwrite it afterward. Give each conceptual “thing” its own name: $request, $response, $payload, $result.

Debugging is hard enough. Don’t let your variable names lie to you.

Daryle De Silva

VP of Technology

11+ years building and scaling web applications. Writing about what I learn in the trenches.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *