Course outline

PHP / Basic: values and request foundations / Lesson 6

Escaping, headers, and response construction

Keep domain data separate from HTML and JSON representations. Build response decisions before output, and understand why an early byte can prevent later header changes.

About 4 min readContent reviewed
On this page

Lesson goals

  • Escape text for the intended output context.
  • Separate response metadata from body emission.
  • Explain header timing and serialization failures.

Escaping belongs where data becomes representation

Store validated domain text rather than preescaped HTML. At the rendering boundary, htmlspecialchars with an explicit UTF-8 policy and suitable flags protects ordinary HTML text and quoted attribute contexts. It does not make a value safe inside JavaScript, CSS, a URL protocol decision, or arbitrary raw markup. Prefer simple context boundaries and avoid composing executable code from strings. A title containing angle brackets can remain legitimate data and display literally. Escaping is not authorization or input validation; a safe-looking page can still contain a wrong price or expose records the current user should never receive.

php Explicit HTML text encoding
<?php
declare(strict_types=1);
$title = '<label title="x">&</label>';
echo htmlspecialchars($title, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"), PHP_EOL;
Expected output
&lt;label title=&quot;x&quot;&gt;&amp;&lt;/label&gt;

Quotes, brackets, and ampersand become entities. The output is a representation for this context, not replacement domain data.

Decide status and headers before sending a body

An HTTP response has a status, headers, and a body. PHP header-related functions must run before output is committed to the client in the relevant server interface. A stray echo, whitespace outside PHP tags, a byte-order mark, or displayed warning can send bytes early. Keep reusable PHP-only source files without a closing tag to reduce accidental trailing output. Do not use output buffering as a blanket excuse to mix calculation, headers, and templates in arbitrary order. Buffering can be a deliberate tool, but it does not remove the response lifecycle or make redirected code stop executing.

Represent a response before emitting it

A small array can model response metadata for a local test. The example creates a JSON body using explicit error handling, then records the content type and status separately. It prints the model to the CLI, so it verifies construction rather than actual HTTP header delivery. A real adapter would apply the status and headers once, emit the body once, and stop or return according to its execution contract. For a redirect, choose a validated destination and terminate the current path deliberately; sending Location alone does not prevent later sensitive work or output.

php A CLI response-construction model
<?php
declare(strict_types=1);
$body = json_encode(["title" => "PDO", "seats" => 2], JSON_THROW_ON_ERROR);
$response = ["status" => 200, "headers" => ["Content-Type" => "application/json; charset=UTF-8"], "body" => $body];
echo $response["status"], PHP_EOL;
echo $response["headers"]["Content-Type"], PHP_EOL;
echo $response["body"], PHP_EOL;
Expected output
200
application/json; charset=UTF-8
{"title":"PDO","seats":2}

No server header is sent. Construction and JSON output are deterministic and can be tested independently of a web interface.

JSON has shape and encoding obligations

A successful JSON encoding operation does not prove the payload matches the caller’s schema. Distinguish a list from an identifier map and define nullability, required fields, and error representations. JSON_THROW_ON_ERROR turns encoding problems into explicit exceptions; handle them at the response boundary without leaking a stack trace into a partial JSON document. Do not concatenate warning output with a JSON body and still advertise application/json. Prefer one complete response decision so failures can choose a safe error status and body before anything is emitted. Keep serialization separate from SQL parameter binding and HTML escaping.

Verify emitted bytes and browser behavior separately

Unit checks can compare an escaped string or response model exactly. An isolated HTTP check must separately inspect status, content type, cookies, redirects, and rendered output. A CLI pass cannot establish cookie delivery, and a browser displaying text does not prove the response had the intended status. When investigating headers already sent, preserve the first output location and remove the cause rather than suppressing all warnings. In production, error visibility and logging are configured responsibilities; never expose private diagnostics merely to obtain a convenient debugging page. Use synthetic cases and safe local evidence.

Common errors and fixes

HTML data is escaped twice

Why it happens
The application stores a rendered representation as source data.
How to fix it
Store validated text and escape once at each output boundary.
Prevent it next time
Test the same title in HTML and JSON separately.

A redirect continues executing writes

Why it happens
Location is mistaken for terminating control flow.
How to fix it
Return or terminate deliberately after the response decision.
Prevent it next time
Assert no later side effect occurs on the redirect path.

A JSON endpoint returns warning text

Why it happens
Errors are printed into the response stream.
How to fix it
Fix the warning and use safe boundary error handling.
Prevent it next time
Validate status, content type, and body parsing together.

Check your understanding

1. Does htmlspecialchars make a destination URL safe to redirect to?

Show worked solution

No. It encodes an HTML representation, not the destination policy. Parse and validate the URL scheme, origin, and allowed path rules separately. If displaying a link, also escape it for the quoted HTML attribute after the URL has passed its own policy.

2. What does the response-model example prove?

Show worked solution

It proves the local construction produces the documented status value, content-type string, and JSON body. It does not send HTTP or verify a browser’s interpretation. Add an isolated adapter test when real header delivery, redirects, or cookies become part of the feature.

Key takeaways

  • Keep original data separate from context-specific representations.
  • Construct a complete response before emitting bytes.

Official references