PHP Classes

File: docs/BUG_FIX_OUTPUT_BEFORE_HEADER.md

Recommend this page to a friend!
  Packages of Adrian M   upMVC   docs/BUG_FIX_OUTPUT_BEFORE_HEADER.md   Download  
File: docs/BUG_FIX_OUTPUT_BEFORE_HEADER.md
Role: Example script
Content type: text/markdown
Description: Example script
Class: upMVC
Pure PHP web development without other frameworks
Author: By
Last change: Update of docs/BUG_FIX_OUTPUT_BEFORE_HEADER.md
Date: 2 months ago
Size: 6,274 bytes
 

Contents

Class file image Download

Critical Bug #4: JavaScript Redirect Overriding PHP Header

The Final Boss Bug

Date: October 16, 2025 - 18:53

The Stubborn Symptom

After fixing 3 bugs, the redirect STILL didn't work! The log showed:

[18:53:05] DEBUG Auth Controller - Redirecting to intended: /upMVC/moda ?
[18:53:05] DEBUG Router - reqRoute: /                                   ? STILL WRONG!

Root Cause: Output Before Header

The Order of Operations Bug

// In login() method:
$_SESSION['logged'] = true;
$this->html->validateToken();  // ? Outputs HTML/JavaScript!
header("Location: /upMVC/moda");  // ? Too late! Headers already sent!

What validateToken() Does

public function validateToken()
{
?>
    <script>
        location.href = "<?php echo BASE_URL; ?>/";  // ? ALWAYS redirects to home!
    </script>
<?php
}

The Deadly Sequence

  1. PHP sets session: `$_SESSION['logged'] = true` ?
  2. PHP calls: `$this->html->validateToken()`
  3. Outputs HTML: `<script>location.href = "/upMVC/";</script>`
  4. Headers sent! (any output sends headers)
  5. PHP tries: `header("Location: /upMVC/moda")`
  6. PHP header ignored! (can't modify headers after output)
  7. Browser receives the HTML with JavaScript
  8. JavaScript executes: `location.href = "/upMVC/"`
  9. Redirects to home ?

PHP Headers and Output

The Golden Rule

You cannot send headers after ANY output!

// ? WRONG - Output before header
echo "Hello";
header("Location: /somewhere");  // Error or ignored!

// ? CORRECT - Header before output
header("Location: /somewhere");
exit;
echo "Hello";  // Never executes

Why This Happens

HTTP protocol requires:

HTTP/1.1 200 OK
Content-Type: text/html
Location: /somewhere
                        ? Blank line separates headers from body
<html>...</html>        ? Body/output starts here

Once PHP outputs anything (echo, HTML, whitespace), it must send headers first. After that, headers are locked.

What Counts as Output?

  • `echo` or `print`
  • Any HTML outside `<?php ?>`
  • Whitespace before `<?php`
  • Warning/error messages
  • Return from a function that echoes
  • Even a single space!

The Fix

Remove the validateToken() call since we're doing a proper PHP redirect:

// BEFORE (Bug)
$_SESSION['logged'] = true;
$this->html->validateToken();  // ? Outputs JavaScript redirect to home
header("Location: " . $redirectUrl);  // ? Ignored!
exit;

// AFTER (Fixed)
$_SESSION['logged'] = true;
// REMOVED validateToken() - was outputting JavaScript that overrode PHP header
header("Location: " . $redirectUrl);  // ? Works!
exit;

Why validateToken() Existed

It was probably meant for AJAX logins or as a fallback: - If headers fail ? JavaScript redirects - For compatibility with old code

But in modern code: - ? Use PHP header() redirect - ? Call exit immediately after - ? Don't output anything before redirect - ? Don't use JavaScript redirect as primary method

Testing the Fix

Before All Fixes

[18:22:28] Storing intended_url: /upMVC/moda
[18:22:34] Redirecting to intended: /upMVC/moda
[18:22:34] reqRoute: /  ? Wrong!

After All Fixes

[timestamp] Storing intended_url: /upMVC/moda
[timestamp] Redirecting to intended: /upMVC/moda
[timestamp] reqRoute: /moda  ? Correct! ?

All 4 Bugs - Complete Summary

Bug #1: Middleware Overwriting Session ?

Line: AuthMiddleware.php:64 Problem: Stored intended_url on every request, overwriting original Fix: Only store if not already set

Bug #2: Assignment Instead of Comparison ?

Line: Controller.php:68 Problem: if ($logged = true) always true Fix: Use if ($logged === true)

Bug #3: Missing exit After Redirect ?

Lines: Controller.php:75, 79, 140 Problem: Code continued after header(), sent multiple redirects Fix: Add exit after every header("Location: ...")

Bug #4: Output Before Header (THIS BUG) ?

Line: Controller.php:117 Problem: validateToken() output JavaScript before PHP header Fix: Remove validateToken() call before redirect

The Complete Flow (Now Fixed!)

User visits /moda (not logged in)
    ?
AuthMiddleware:
    - Stores: $_SESSION['intended_url'] = '/upMVC/moda'
    - Redirects to: /auth
    ?
User submits login form
    ?
Controller login():
    - Validates credentials ?
    - Sets: $_SESSION['logged'] = true ?
    - Gets: $url = $_SESSION['intended_url'] = '/upMVC/moda' ?
    - Sends: header("Location: /upMVC/moda") ?
    - Exits: exit ?
    ?
Browser receives: HTTP 302 redirect to /upMVC/moda ?
    ?
Browser requests: /upMVC/moda ?
    ?
User sees: /moda page ? SUCCESS!

PHP Best Practices Learned

1. Headers Before Output

// ? Always send headers first
header("Content-Type: application/json");
header("Location: /somewhere");
// Then output
echo json_encode($data);

2. Exit After Redirect

// ? Always exit after redirect
header("Location: /target");
exit;

3. No Output in Logic Code

// ? Bad - Mixed logic and output
function login() {
    // logic
    echo "<script>redirect</script>";  // Output in logic!
    header("Location: /");  // Won't work!
}

// ? Good - Separate concerns
function login() {
    // pure logic
    return '/target-url';
}
// Later, in view layer:
header("Location: " . login());
exit;

4. Check for Output

// Check if headers already sent
if (headers_sent($file, $line)) {
    die("Headers already sent in $file on line $line");
}
header("Location: /somewhere");
exit;

Files Changed

  • `d:\GitHub\upMVC\modules\auth\Controller.php` - Removed `validateToken()` call before redirect

Related Files

  • `d:\GitHub\upMVC\modules\auth\View.php` - Contains the `validateToken()` method (kept for backward compatibility) - Can be safely used ONLY when not doing PHP redirects

The Lesson

> Three rules for redirects in PHP: > 1. No output before header > 2. Always call exit after redirect header > 3. Check your View methods - they might output HTML/JavaScript!

This completes the saga of the auth redirect bug! ?