Swingin’ the Alphabet
A 2019 PHP app meets the 2026 TNT framework.
Seven years of PHP, explained one comment at a time.
This log documents the renovation of a PHP app originally written in August 2019 into the TNT 2026 ecosystem. The core algorithm — extracting unique consonants from a phrase and generating song lyrics — is preserved exactly. Everything else changed: the framework, the HTML shell, the form method, the output structure, and the inline documentation.
The primary goal was educational: take a working PHP app and make it readable as a teaching document. Watch for Prompt Critique boxes (amber) and Design Decision boxes (green). The PHP teaching moments are concentrated in the Analysis section.
I have a legacy PHP app, swingAlphabetSPARK.php that needs renovation and updating to better fit into our current PHP framework, as demonstrated in our ambigMsgPHPIndex.php app. That had a chatlog, and we should have a chatlog for this new app too. I have provided artwork for the hero background and app icon. Can you upgrade the former app and also teach/refresh us on the use of PHP using comments and providing analysis in the chatlog? Be sure to link to the chatlog for the swingAlphabet app like you did for the ambigMsg app.
The prompt establishes three distinct deliverables: the renovated PHP app, the chatlog, and the link connecting them. Naming all three before any code is written ensures none gets treated as optional.
“Better fit into our current PHP framework, as demonstrated in our ambigMsgPHPIndex.php app” is a reference prompt — it uses an existing, known-good file as the specification rather than describing the target in abstract terms. This is often more precise than any written spec.
“Also teach/refresh us on the use of PHP” makes the educational obligation explicit. Without this, the renovation might produce a working app with minimal comments. With it, the source code becomes a teaching document in its own right.
Here is the inventory of PHP concepts at work in the renovation, in the order they appear in the file.
1. The server-side block.
Everything between <?php and the closing ?> runs on the web server before any HTML is sent to the browser. The visitor never receives this code — only its output. This is the fundamental difference between PHP and JavaScript.
2. Variables.
PHP variables begin with $ and are loosely typed — $stageStr = "1" is a string; $wasSubmitted = false is a boolean; $consonants = [] is an array. The type is inferred from the assigned value, not declared in advance.
3. Short-array syntax.
$arr = [] is the PHP 5.4+ equivalent of $arr = array(). Prefer the short form in modern PHP.
4. isset() for safe key access.
isset($_GET['phrase']) checks whether the key exists before reading it. Without isset(), accessing a missing key produces a PHP Notice (a logged warning). Always check before reading from superglobals.
5. String functions.
The algorithm chain is: strtolower() → str_split() → array_unique() → preg_match() filter → result arrays. Each step is a built-in PHP function operating on the output of the previous one.
6. Pass-by-reference (&).
function getUniqueLowerCaseConsonants(string $str, array &$excluded) — the & before $excluded passes the array by reference. PHP normally passes arrays by value (a copy). The & means changes inside the function modify the caller’s original variable.
7. Regular expressions.
preg_match('/^\w$/', $ch) and preg_match('/^[^aeiou\d_]$/', $ch) test single characters against PCRE patterns. [^...] is a negated character class. \d matches digits. Together they isolate consonant letters.
8. Type hints.
function f(string $s, array &$a): array declares expected types for parameters and return value. Added in PHP 7. They catch type mismatches early and document intent.
9. Alternative control syntax.
foreach ($arr as $item): ... endforeach; is cleaner than foreach ($arr as $item) { ... } inside HTML templates because the end-keyword is named — easier to match visually when PHP and HTML are interleaved.
10. Output buffering.
ob_start() redirects all output to a memory buffer. ob_get_clean() retrieves the buffer and clears it. Together they allow a function to use echo and template blocks while still returning a string — separating logic from output.
11. String interpolation.
PHP expands variables inside double-quoted strings: "$C-A: {$C}ay". The curly-brace form {$C} is required when the variable is immediately followed by other word characters (e.g., {$C}oo instead of $Coo, which would look for an undefined variable).
12. htmlspecialchars().
Converts <, >, &, ", ' to safe HTML entities. Always apply before echoing any user-supplied string into HTML. This is the primary defence against Cross-Site Scripting (XSS) attacks.
13. date('Y').
Returns the current four-digit year from the server clock. Used in the footer copyright line so the year is always current without touching the file.
The algorithm at the heart of this app is a six-step chain:
This six-function chain — strtolower, str_split, array_unique, foreach, two preg_match calls — covers the core of PHP string and array manipulation in one cohesive example. Any student who understands this chain has the toolkit for most string-processing exercises in the language.
Why GET, not POST?
The HTTP specification defines the semantics of each method. POST is for operations that change server state: submitting a password, saving a record, placing an order. The browser protects users from accidentally repeating such operations — refreshing a POST page triggers the “Resubmit form data?” warning documented in DWR & Eureka Entry #16.
GET is for queries: requests that read data without side effects.
A GET form appends its values to the URL (?phrase=three+stooges), which
makes the result bookmarkable, shareable, and safe to refresh.
Generating song lyrics from a phrase is a read-only query with zero side effects. GET is the semantically correct choice. Using POST here would be the same category of mistake as using a hammer to turn a screw: it would work, but it signals a misunderstanding of the tool.
Placed alongside ambigMsgPHPIndex.php (which correctly uses POST for
a password check), the two apps form a natural pair for teaching the GET/POST distinction
in a concrete, side-by-side context.
| GET | POST | |
|---|---|---|
| PHP superglobal | $_GET | $_POST |
| Data location | URL query string | Request body (hidden) |
| Bookmarkable? | Yes | No |
| Refresh behavior | Reruns safely | Browser warns “Resubmit?” |
| Use for | Queries (no side effects) | State changes (passwords, saves) |
| This app | ✓ Correct — song query | |
| ambigMsgPHP | ✓ Correct — password check |
The original app depended on server-side PHP include files:
require_once("../../php_fragments/site_wide_constants.php"),
require_once('../../components/app_header.php'), and
require_once('../../components/common_footer.php').
These paths no longer resolve in the 2026 folder structure.
The renovation removes all includes and builds a self-contained HTML shell using
Bootstrap 5.3.3 CDN and ../styles/tnt-base-styles.css. This is the same pattern
used across all 2026 TNT PHP apps. A standalone shell has no hidden dependencies, deploys
anywhere, and fails visibly rather than silently when a path is wrong.
buildSongHTML() vs Direct EchoThe original writeSong($arr) function used echo() to
stream HTML directly to the browser. This works, but it tightly couples the logic to the output
— you cannot test, inspect, or conditionally include the result without restructuring the
function.
The renamed buildSongHTML(array $consonants): string uses output
buffering (ob_start() / ob_get_clean()) to capture its output into
a string. The caller stores the result in $songHTML and echoes it when needed.
This is the principle of separation of concerns: computation and output are handled
independently.
htmlspecialchars() on Every User ValueThe original app echoed the phrase directly:
echo("<p>Original phrase: $phrase</p>").
If the phrase contained <, >, or &, the
browser would interpret them as HTML. In a worst-case scenario, a crafted input could inject
script tags into the page (Cross-Site Scripting, XSS).
The renovation wraps every user-supplied value in
htmlspecialchars() before echoing it into HTML, and also applies it to the
form’s value attribute and to $_SERVER['PHP_SELF'].
These are the three most common XSS injection points in a PHP form handler.
The original song output was a series of <p> elements stacked
vertically. For a phrase like “three stooges” (5 consonants) this was fine.
For a long phrase with many unique consonants, the output became a very tall single column.
The renovation uses Bootstrap’s responsive grid
(col-12 col-sm-6 col-md-4 col-lg-3) to display each consonant’s stanza as
a card. On a large screen, four stanzas sit side by side. On mobile, they stack to a single
column. The layout adapts to the data without any JavaScript — CSS grid alone.
- GET for queries, POST for state changes.
Song generation is a read-only query.
$_GETis correct. Passwords, purchases, and saves are state changes.$_POSTis correct for those. Using the wrong one is not just a style choice — it determines whether the browser’s “Resubmit?” warning fires on refresh. - Always
htmlspecialchars()before echoing user input. Apply it to form values, URL parameters, and any server variable you echo into HTML. The three most common injection points:$_GET/$_POSTvalues, the formvalue=""attribute, and$_SERVER['PHP_SELF']. - Pass-by-reference (
&) for output parameters. When a function needs to return multiple results, one via return value and one via a secondary output, pass the secondary as&$ref. The function modifies the caller’s original variable directly. Cleaner than returning an array or using a global. - Output buffering separates logic from output.
ob_start()/ob_get_clean()lets a function build HTML using template blocks and echo, while still returning a string. The caller decides when and where to output the result. - Regex in PHP:
preg_match()with PCRE. The two-regex filter (/^\w$/for word characters,/^[^aeiou\d_]$/for consonants) is a textbook example of character classification with negated character classes. Learn[^...]and\dand you can classify any character in any string. - Standalone PHP shells have no hidden dependencies. A PHP app that uses CDN links and a single shared CSS file deploys anywhere without broken include paths. When an include fails, PHP emits a fatal error and the page dies silently. A standalone shell fails visibly, with a browser network error that names the missing resource.
| File | Status | Change |
|---|---|---|
swingAlphabetSPARK.php |
Stage 1 Renovated | Full 2026 shell; GET form; buildSongHTML(); Bootstrap card grid; chalkboard-green palette; no legacy includes; htmlspecialchars() throughout |
swingAlphabetSPARKChatlog.html |
Created | This document — renovation log, PHP teaching reference, GET/POST analysis, six takeaways |
movie_clips/swingAlphabetSPARK.html |
Updated | Legacy PHP app link updated to new app; chatlog link added in CS Connection section |