LEARN TO CODE | PHP | 02 PAGES AND FORMS
02 Pages and forms: challenges
Build and test a small request-driven PHP website with reusable layout, clear form feedback and remembered choices.
START HERE | INDEPENDENT PRACTICE
Use the complete files from lessons 02.1–02.4. Keep the same filenames and folders, start the PHP server in their root folder and use one consistent hostname.
Attempt tasks before opening the hints. Record the submitted data, response and expected state change, or lack of change. Tick each task only after checking; selections last while the page remains open.
CHALLENGE 01 | SHARE THE LAYOUT
Create Home and About using one shared header, footer and helper. Change one footer sentence and verify both pages.
Need a hint?
Set pageTitle before requiring the header.
Reveal a possible solution or check
Use the five-file example from 02.1. Require helpers once, prepare the title, require the header, provide page content and require the footer. Both pages should produce one complete HTML document.
CHALLENGE 02 | FOLLOW THE NAME
A form input has id="search" but no name. PHP expects $_GET['q']. Correct the connection.
Need a hint?
The submitted key comes from name.
Reveal a possible solution or check
Use name="q" on the input and a GET form targeting search.php. The id can remain search if its label uses for="search". The id and submitted key have different responsibilities.
CHALLENGE 03 | SEARCH AND BOOKMARK
Run search.php and search for robot. Copy its URL into another tab and explain why the same search appears.
Need a hint?
GET values travel in the query string.
Reveal a possible solution or check
The URL includes q=robot. The new request supplies the same query value, and the page generates the result again from its fixed dataset. No server-side session is required for that search.
CHALLENGE 04 | POST A GREETING
Submit a name containing an apostrophe. Then try a missing name and an array-shaped field.
Need a hint?
Check the request method and value shape before using it.
Reveal a possible solution or check
The normal apostrophe displays correctly. Missing, blank or non-string input produces Enter a name rather than a string-function crash. The greeting is a response calculation, not a saved registration.
CHALLENGE 05 | RANGE BOUNDARIES
Test year values 6, 7, 13, 14 and 10.5 in signup.php, with otherwise valid fields.
Need a hint?
Test both valid boundaries and just-outside cases.
Reveal a possible solution or check
7 and 13 are accepted. 6 and 14 fail the range; 10.5 fails whole-number validation. HTML browser controls may block some entries, so server-side tests should also send those values directly.
CHALLENGE 06 | A DROPDOWN IS NOT AUTHORITY
Send club=unknown directly to the preview form. Explain why the server must reject it.
Need a hint?
The browser is not the only way to construct a request.
Reveal a possible solution or check
The key is not in the server’s clubs map. Reject it even though the normal select never offers it. An allow-list validates choice; it is separate from authentication or permission.
CHALLENGE 07 | DISPLAY TEXT, NOT MARKUP
Use a name containing angle brackets and quotation marks. Make the preview and preserved input display the value as text.
Need a hint?
Escape at each HTML output point.
Reveal a possible solution or check
Use escapeHtml in both paragraph text and quoted value attributes. Inspect response source for escaped characters and confirm no submitted element appears in the DOM. This is not a reason to use the same encoder for SQL or JavaScript.
CHALLENGE 08 | KEEP GOOD INPUT AFTER ERRORS
Submit a valid name and club but an invalid email. Check that the form keeps the other values and reports the email problem.
Need a hint?
Initialise a values map; display escaped values independently of success.
Reveal a possible solution or check
The sample stores each received scalar value, collects errors and displays the form again. It must not display a validated preview while errors remain.
CHALLENGE 09 | SESSION ACROSS PAGES
Save a favourite, read it in session-summary.php and explain why it works without passing the club in the link.
Need a hint?
The browser sends a session identifier on later requests.
Reveal a possible solution or check
Both pages start the same session using the cookie. favourite.php stores the choice in $_SESSION and the summary reads it. The cookie ordinarily carries the session ID rather than the whole choice array.
CHALLENGE 10 | PROVE A REJECTION
POST a save action without csrf, then with a wrong token. Confirm the existing favourite remains unchanged.
Need a hint?
Check response status and state separately.
Reveal a possible solution or check
The example returns 403 and skips the state-changing branch. A correct token plus an unknown club still fails the independent allow-list check. CSRF protection is not input validation.
CHALLENGE 11 | COOKIE TRUST AND TIMING
Save compact in preference.php, then submit an unexpected mode. Explain why setcookie does not instantly rewrite $_COOKIE.
Need a hint?
A response sets a cookie for a later request.
Reveal a possible solution or check
After the successful POST redirect, a GET reads compact. An invalid posted mode is not saved. On each later request, incoming cookies are checked against the allow-list; an unexpected cookie falls back to comfortable. Cookies are not trusted permission claims.
CHALLENGE 12 | CAPSTONE: A CLUB REQUEST JOURNEY
Combine a shared layout, search, validated request preview and remembered favourite into one local site. Keep all request/session logic before output. Show a short journey from Home to search, preview and favourite summary.
Need a hint?
Build on the working files rather than copying headers before session setup.
Reveal a possible solution or check
Use the complete examples as the baseline. Add fixed navigation links in the shared header. In each page, load the helper or session bootstrap, process the request, then require the layout header and provide the page-specific HTML. Avoid duplicate document wrappers. Keep preview clearly labelled as unsaved, retain validation and output escaping, and keep the session token on state-changing forms.
Test initial GET, valid and invalid POST, refresh after redirect, a separate browser context and an absent token. Explain one thing not implemented, such as real registration storage or login.
STRETCH | ADD ONE WELL-DEFINED FEATURE
Add a remembered display preference to the common navigation, or add an optional comment to the request preview. State whether the data is request-only, session-held or stored in a direct cookie.
Use a fixed set of known preference options; validate the value again when reading it. Do not create a “logged in” flag from an arbitrary submitted cookie and call that authentication.
FINISH | EXPLAIN THE BOUNDARIES
Explain validation versus escaping, GET versus POST, session ID versus session contents, and include path versus browser URL. If you can trace those distinctions through your own pages, you are ready to introduce files and databases.
Keep preview data fictional. The examples demonstrate local request handling and state, not a completed deployed membership service.
KEEP LEARNING
Previous page 03.1 Reading files and JSON data
Official references: Includes and control structures · POST values · HTML escaping · Sessions · Cookies