Tutorials Logic, IN info@tutorialslogic.com

PHP Forms: POST, Validation, Escaping, and Error Feedback

PHP Form Boundaries

A PHP form handler accepts a defined request shape, normalizes without changing intent, validates domain rules, reports accessible field errors, encodes only at output, and persists only trusted values.

Production forms also authorize every option and resource, verify CSRF protection, constrain files and request complexity, prevent duplicate commands, redirect after success, and test malformed requests with zero unintended side effects.

GET and POST

Method Good fit Browser behavior
GET Search, filtering, pagination Values appear in the URL and can be bookmarked
POST State-changing submissions Values are sent in the request body

Validate Fields

Email and Name Validation

Email and Name Validation
<?php
$name = trim((string) ($_POST['name'] ?? ''));
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
$errors = [];

if ($name === '') {
    $errors['name'] = 'Enter your name.';
}
if ($email === false || $email === null) {
    $errors['email'] = 'Enter a valid email address.';
}

if ($errors === []) {
    echo 'Form is valid.';
}

Validation produces structured field errors instead of silently changing invalid data.

Redisplay Safely

When validation fails, preserve useful input and show errors beside their fields. Escape values when they are inserted into HTML attributes or text.

Escape an Input Value

Escape an Input Value
<?php
$name = (string) ($_POST['name'] ?? '');
$safeName = htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
?>
<input name="name" value="<?= $safeName ?>">

ENT_QUOTES protects both quote styles so a submitted value cannot break out of the value attribute.

Successful Submission

After saving valid data, redirect to a confirmation URL. The Post/Redirect/Get pattern prevents an accidental browser refresh from repeating the POST.

Any state-changing form also needs a server-verified CSRF token. The security lesson builds that token flow.

Redirect After Save

Redirect After Save
<?php
// Save validated data first.
header('Location: /profile/saved', true, 303);
exit;
  • Headers must be sent before response output.

Form Failure Signals

  • Undefined array key: read optional fields with ?? and validate them.
  • Headers already sent: output occurred before header() or session_start().
  • Invalid value disappears: preserve normalized input for the error response.
  • Duplicate record after refresh: redirect after a successful POST.

Request Boundary

A form handler begins by accepting only the intended HTTP method and media type. GET should retrieve a representation without changing server state; POST commonly submits a state-changing command. Method choice does not validate, encrypt, or authorize the values.

Read each expected field by name and reject unexpected shapes. Attackers can send arrays where a scalar was expected, omit browser-required fields, repeat keys, or bypass the HTML form entirely. Browser constraints improve interaction but never replace server validation.

`filter_input` reads the original value supplied by the server interface, before application changes to the matching superglobal. Use `filter_var` when validating a value that your code has already normalized. Always handle missing, invalid, and valid-falsy results distinctly.

Set limits before parsing: request bytes, field count, nesting depth, string length, and file size. Reject an unsupported content type or oversized request with a clear status rather than spending unbounded memory on it.

  • Accept only the route method and media type you support.
  • Treat every submitted shape as untrusted.
  • Distinguish original input from normalized values.
  • Enforce request and field complexity limits.

Normalization and Validation

Normalization creates one representation for comparison, such as trimming permitted surrounding whitespace or converting an approved date format. Validation decides whether that representation belongs to the domain. Do not silently repair a value into a different user intention.

Required, type, range, length, format, and cross-field rules are different checks. Validate in a useful order so one field receives clear feedback. A password confirmation depends on two fields, while a booking end date depends on both dates and business policy.

Validation filters can recognize values such as email, integer, boolean, and URL forms, but acceptance still needs domain rules. A syntactically valid email may be too long for the schema; a valid URL may point to a forbidden host; integer zero may be a valid value rather than failure.

Keep submitted values separate from trusted domain values. Construct the domain command or value object only after all required checks pass. This prevents partially validated arrays from reaching persistence and authorization code.

  • Normalize only transformations that preserve intent.
  • Validate shape, type, range, and cross-field rules.
  • Treat valid syntax as only one domain condition.
  • Create trusted objects after validation succeeds.

Field Feedback

Return errors keyed by stable field names and include a form-level error for failures that do not belong to one control. Preserve safe non-secret input so the learner can correct one field without retyping the form.

Render labels with explicit control associations, identify invalid controls accessibly, and connect each message through descriptive relationships. Move focus to a concise error summary when a failed submission reloads a long page, while keeping errors beside their fields.

Use `htmlspecialchars` with an explicit encoding and suitable quote handling when placing a value in HTML text or a quoted attribute. HTML escaping is not URL, JavaScript, CSS, SQL, CSV, or header escaping; each destination has its own safe construction method.

Never redisplay passwords, tokens, payment secrets, or hidden authorization values from submitted input. Hidden controls are user-controlled too. Derive identity and permission from the authenticated server session, not hidden fields.

  • Key errors to controls and include form-wide failures.
  • Preserve only safe correction values.
  • Connect labels and messages for assistive technology.
  • Encode values for their exact output context.

Control Shapes

Unchecked checkboxes usually send no key, while checked controls send their configured value. Radio groups submit one selected value. Multi-select controls and names ending in brackets can produce arrays. Define the expected shape for each control before reading it.

Never trust an option merely because it appeared in a select list. Validate submitted identifiers against the set allowed for the current user and current state. A stale or forged option can otherwise cross an authorization boundary.

Distinguish absent, empty, zero, and false. Null coalescing is convenient for optional text but can hide a required missing key. Parse booleans and integers with APIs that report invalid input rather than relying on a cast that transforms arbitrary text.

Dynamic repeated rows need stable identifiers and a maximum row count. Validate every row independently, reject duplicate identifiers when uniqueness matters, and map row-specific errors back to the same visible item.

  • Model each control as scalar, array, or absent.
  • Authorize every submitted option on the server.
  • Preserve meaningful distinctions among falsy values.
  • Bound and identify repeated form rows.

File Controls

A file form requires `multipart/form-data`; uploaded data appears in `$_FILES`, not as an ordinary POST string. Check the upload error code before using size, name, or temporary path because partial and missing uploads have different meanings.

Enforce server-side byte limits and inspect content with an appropriate detector. A browser-provided MIME type and filename are untrusted metadata. Generate a storage name and keep the original display name only after normalization and output encoding.

Move a verified upload with the upload-specific API into storage outside the executable public tree when possible. Restrict extensions, prevent path traversal, deny script execution, and apply image or document validation appropriate to the feature.

Uploads can consume disk, memory, and processing time. Set per-file and per-request quotas, clean abandoned temporary work, scan where the risk model requires it, and avoid synchronous expensive conversion without a bounded job policy.

  • Use multipart encoding and inspect upload status first.
  • Ignore client claims about content and paths.
  • Store verified files under generated names.
  • Limit bytes, processing time, and retained temporary data.

Submission Integrity

Protect every state-changing browser form with an unpredictable token bound to the appropriate session and purpose. Compare the submitted token safely, expire or rotate it according to the application policy, and reject the request before performing side effects.

CSRF protection does not authorize the action. After authenticating the session, verify that this user may change this exact resource in its current state. Never accept an owner identifier from the form as proof of ownership.

After a successful POST, redirect with an appropriate status to a GET representation. Post/Redirect/Get prevents a normal refresh from repeating the browser POST, but it does not guarantee business-level idempotency under double clicks, retries, or concurrent requests.

For operations such as orders or payments, use an idempotency or unique-command key enforced by persistence. Disable repeated UI submission for usability, then rely on the server constraint for correctness.

  • Verify a purpose-bound CSRF token before side effects.
  • Authorize the resource independently of the token.
  • Redirect after successful state changes.
  • Enforce idempotency where duplicate commands matter.

Persistence and Tests

Pass only validated domain values to persistence and bind SQL values through prepared statements. Database constraints remain the final guard for uniqueness, references, and concurrent changes. Translate constraint failures into a safe form-level or field-level result.

Wrap related writes in a transaction when they must succeed together. Roll back on exceptions, log a correlation identifier and diagnostic context without submitted secrets, and show the user a stable message that does not expose database details.

Test the initial GET, valid POST, every validation rule, absent fields, array-for-scalar attacks, oversized values, invalid encoding, stale options, CSRF failure, duplicate submission, persistence failure, and redirect destination. Assert that invalid requests cause no writes.

Render tests should verify escaped redisplay, selected and checked state, field associations, error summary links, and keyboard focus. Integration tests should use the real request parser and session middleware because hand-built arrays can miss boundary behavior.

  • Persist only validated values through bound parameters.
  • Keep related writes atomic when required.
  • Assert failure paths produce no side effects.
  • Test both request behavior and rendered accessibility.
Before you move on

Mastery Check

5 checks
  • Verify method, media type, shape, and size limits.
  • Separate normalization, validation, and output encoding.
  • Authorize submitted options and protect state changes from CSRF.
  • Handle uploads outside executable public storage.
  • Test invalid shapes, duplicate requests, persistence failures, and accessibility.

Form Boundary Check

0 of 2 checked

Q1. When should output escaping happen?

Q2. What does Post/Redirect/Get prevent?

Form Boundary

  • Trusting submitted fields

    Treat every field as untrusted even when the HTML restricts it. Enforce method, CSRF, type, length, authorization, and business rules on the server.

Try this next

Build Field Feedback

0 of 2 completed

  1. Validate a non-empty name, email address, and integer age of at least 13.
  2. Redisplay the name and email after an error using the correct HTML escaping.
Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.