The Code Adventurer: Episode Three – PHP and the Server Room of Doom

·

Person using laptop facing a robotic dragon breathing binary code inside a server room

Every adventurer has a moment when they look at the map, and the map ends.

The safe, charted territory, the CSS that could be learned from videos, the HTML that had documentation and clear rules and a finite vocabulary, gives way to something larger and darker and considerably more complex. In the geography of web development, that territory has a name.

That name is PHP.

Our Adventurer had been avoiding PHP with the specific avoidance strategy of someone who knows the difficult thing is there but has decided that this particular week is not the week to confront it. Then the next week the same decision is made. And the week after. Until eventually avoiding the difficult thing requires more energy than simply doing it, which is the true reason most people eventually tackle the things they have been putting off: not courage, but the exhaustion of avoidance.

The trigger, in Our Adventurer’s case, was a template.

A WordPress theme had been found, on a website called ThemeForest, that was perfect. Exactly the aesthetic Our Adventurer wanted for the blog: clean, minimal, with a particular way of displaying featured images that was genuinely beautiful. It was paid, twelve dollars, which Our Adventurer purchased without hesitation because twelve dollars for something this good felt like an extraordinary bargain, the kind of price that makes you worried about whether the creator was being compensated fairly.

The theme was installed. It looked beautiful.

But the blog post dates were displaying in the wrong format. American format, month then day then year, and Our Adventurer, being very British about this, wanted day then month then year. The theme’s settings panel did not have an option for this. The WordPress general settings had a date format option but the theme appeared to be ignoring it.

A forum post on the theme’s support forum said: “You will need to edit the template file. Look for the_date() in single.php and adjust the format parameter.”

And just like that, PHP.

The Dragon Reveals Itself

PHP: Hypertext Preprocessor. Or, in the recursive acronym tradition that programmers love with the devotion usually reserved for private jokes, PHP: PHP Hypertext Preprocessor, where the P stands for PHP which stands for PHP, forever, recursively, a snake eating its own tail.

PHP is a programming language. Not a markup language like HTML, not a style language like CSS, but a proper programming language, with variables and functions and loops and conditions and all the infrastructure of computational thinking. It runs on a server. When someone visits a WordPress website, PHP on the server reads template files, queries a database, assembles content, and produces the HTML that gets sent to the browser. The visitor never sees the PHP. They see the result of the PHP.

WordPress is built in PHP. The entire system, the core, the plugins, the themes, all of it is PHP. This means that to truly understand WordPress, to move beyond the surface and reach the engine room, PHP is not optional. PHP is the ship.

Our Adventurer opened a PHP file for the first time using an FTP client called FileZilla, which connected to the hosting server and let you see the files there as though they were on your own computer. This felt very technical, very real, the kind of thing people in films do when they are hacking something while being dramatically lit from below.

The file in question was single.php. It contained, among other things, this:

<?php the_date('F j, Y'); ?>

The <?php is how PHP code begins. The ?> is how it ends. Everything in between is PHP. the_date() is a WordPress function, one of thousands, each one doing a specific thing. The parameter ‘F j, Y’ is a format string, where F means the full month name, j means the day without a leading zero, and Y means the four-digit year. American format: December 25, 2024.

To get British format, the string needed to be: ‘j F Y’. Day, then full month name, then year. 25 December 2024.

Our Adventurer changed the string. Uploaded the file. Refreshed the website.

The dates were now in British format.

The whole operation took approximately four minutes.

Our Adventurer sat very still for a moment.

Then, quietly, with great solemnity: “So that is PHP.”

Learning the Language of the Server

Having successfully made a one-line change, Our Adventurer decided this was a good moment to actually learn PHP rather than continuing to make small edits based on forum advice. The “change one thing and hope” approach had its place, but it was not a foundation for understanding. Understanding required learning the language.

PHP tutorials are, on average, less visually compelling than CSS or HTML tutorials, because PHP produces no visual output of its own: it produces HTML which then produces visual output. This is one step removed from the satisfaction of changing a colour and immediately seeing it change. With PHP you write logic and the logic affects content and the content becomes HTML and HTML is styled by CSS.

But PHP has its own satisfactions.

The first is variables. A variable in PHP is a container for a value. A variable looks like this:

$blog_title = "The Adventures of a Learning Human";
echo $blog_title;

The dollar sign before the variable name is a PHP-specific convention that always identifies something as a variable, which Our Adventurer found charming. Other programming languages use different conventions. PHP uses dollar signs. It is one of PHP’s more distinctive visual features, the reason PHP code is immediately recognisable in screenshots and forum posts: all those dollar signs, creating the inadvertent impression that money is involved in everything.

The second satisfaction is conditionals. An if statement is a piece of logic that says: if this thing is true, do this; otherwise, do that. In web development this is enormously useful:

if ( is_user_logged_in() ) {
echo "Welcome back!";
} else {
echo "Please log in.";
}

WordPress provides hundreds of these conditional tag functions. is_home() to check if you are on the homepage. is_single() to check if you are on a single post page. is_category() to check if you are on a category archive page. They are the vocabulary with which WordPress templates make decisions about what to display.

The third satisfaction is loops. A loop in PHP executes the same code multiple times for multiple values. In WordPress, the most important loop is called, with endearing lack of imagination, “The Loop,” capitalised, like a proper noun, because it is the engine of every WordPress theme:

if ( have_posts() ) {
while ( have_posts() ) {
the_post();
the_title();
the_content();
}
}

This loop checks if there are posts to display, and if there are, goes through them one by one, for each one calling the_post() to set it up and then the_title() and the_content() to display its content. It is deceptively simple code that does something very powerful: it is the mechanism by which every blog post list on every WordPress website in the world is generated.

Our Adventurer read this code several times with the specific focus of someone trying to understand a magic trick, not just the effect but the method. Then wrote it out from memory in the notebook. Then wrote the plain English explanation next to it: check if posts exist. If yes, loop through them. For each one, display its title and content.

Simple. Logical. Beautiful, in its own functional way.

The First PHP Error: The White Screen of Shame

No PHP story is complete without the white screen.

The white screen is what happens when PHP encounters a fatal error. The server tries to execute the PHP, hits a problem, gives up, and the result is a white page with nothing on it, or possibly with a PHP error message in small text at the top if error reporting is enabled, which on production sites it usually is not because you do not want your visitors reading your error messages.

Our Adventurer’s first white screen occurred on a Tuesday, which by this point had become something of a significant day for developments, at approximately eleven in the evening after two hours of increasingly ambitious PHP editing.

Our Adventurer had been trying to create a custom function in the theme’s functions.php file that would display a random encouraging quote at the bottom of every blog post. The function had been written. The quotes were defined as an array. The code to select a random one with PHP’s built-in array_rand() function was there. The hook to attach it to the end of post content using WordPress’s the_content filter had been added.

Our Adventurer had forgotten a semicolon at the end of one line.

This single missing punctuation mark, this tiny absence, this infinitesimal gap in the code, caused PHP to be unable to parse the entire file, which caused WordPress to be unable to load, which caused the website to produce a white screen for every visitor.

Including, at this precise moment, Our Adventurer.

A moment of terror.

Then a very deep breath.

Then: FTP client. Open the functions.php file. Scan the code. Find the line without the semicolon. Add the semicolon. Save. Upload. Refresh.

The website was back.

The whole crisis had lasted eleven minutes, of which approximately nine were spent scanning the code looking for the missing semicolon and two were spent staring at the recovered website making sure it was actually real.

The random quote appeared at the bottom of every post. Today it said: “Every expert was once a beginner.”

Our Adventurer appreciated the timing enormously.

WordPress Hooks: The Architecture of Everything

There is a concept in WordPress development that, once understood, unlocks the entire architecture of the platform. That concept is hooks.

WordPress hooks are points in the code where you can attach your own functions. WordPress runs its code, reaches a hook, and calls any functions that have been attached to that hook. This means you can change or extend WordPress’s behaviour without modifying the core files, which you should never modify because your modifications would be overwritten when WordPress updates.

There are two types of hooks: actions and filters.

An action hook is a point where something happens. WordPress loads, WordPress saves a post, WordPress sends an email. You can attach a function to an action hook and that function will run when that action occurs.

A filter hook is a point where data passes through. WordPress is about to display some content, or generate a page title, or calculate a price. You can attach a function to a filter hook, that function receives the data, modifies it, and returns the modified version, which WordPress then uses instead of the original.

This system is what makes WordPress endlessly extensible. Every plugin you install is, at its core, a set of PHP functions attached to hooks. No core files are touched. The plugin can be removed and WordPress returns to its original state.

Our Adventurer spent a full day reading about hooks and looking at examples of plugins that used them, tracing the logic from the hook attachment to the function to the output. It was like learning to read music notation after years of playing by ear: suddenly the invisible structure was visible, and the visible structure was beautiful.

The Child Theme: Doing Things Right

Armed with a growing understanding of PHP and WordPress’s architecture, Our Adventurer made an important discovery: a child theme.

A child theme is a separate theme that inherits all its styles and templates from a “parent” theme but allows you to override specific files or add custom CSS without touching the parent theme’s files. When the parent theme updates, your customisations in the child theme are preserved, because they are in a separate directory that the update does not touch.

Our Adventurer had been editing the parent theme’s files directly.

This was, Our Adventurer learned from a very patient comment on a web development forum, “absolutely the correct approach if your goal is to lose all your customisations the next time you click Update.”

Creating a child theme requires, at minimum, two files: a style.css with specific header comments that declare the theme’s name and parent, and a functions.php that imports the parent theme’s styles. Both files contain very little code. The child theme directory goes in the wp-content/themes/ folder. The child theme is activated in the WordPress admin.

Our Adventurer created a child theme. All the PHP customisations from the previous weeks were moved into the child theme’s functions.php. The parent theme was updated. The website was tested. The customisations were all still there.

Our Adventurer felt, for the first time, genuinely professional about something. Not just functional but correct. Doing it the way it was supposed to be done, not because someone was watching but because it was the right way, and knowing the right way felt good.

The First Real Plugin

By this point in the adventure, Our Adventurer had been using plugins extensively but treating them as black boxes, magical things that did things without wanting to know how. Now, with PHP beginning to make sense, Our Adventurer decided to write a plugin.

Not a complex plugin. A very small one. A plugin that added a “Last Updated” line to blog posts showing when the post was most recently edited, because several posts had been updated since being published and Our Adventurer wanted readers to know the information was current.

A WordPress plugin, at minimum, is a PHP file with a specific comment block at the top:

<?php
/*
Plugin Name: Last Updated Notice
Description: Shows when a post was last updated.
Version: 1.0
Author: Our Adventurer
*/
function show_last_updated( $content ) {
if ( is_single() ) {
$updated_time = get_the_modified_date( 'j F Y' );
$notice = '<p class="last-updated">Last updated: ' . $updated_time . '</p>';
$content = $content . $notice;
}
return $content;
}
add_filter( 'the_content', 'show_last_updated' );

Our Adventurer wrote this file, named it last-updated-notice.php, put it in a folder called last-updated-notice inside wp-content/plugins/, and activated it from the WordPress admin.

At the bottom of every blog post, in small text: “Last updated: 15 January 2025.”

It was the most Our Adventurer had ever felt like a developer.

There was the function: defined, named, purposeful. There was the logic: check if we are on a single post page, if yes get the modification date and append a notice to the content. There was the hook: add_filter telling WordPress to run this function every time it processes post content. Clean. Contained. Functional.

Our Adventurer looked at the bottom of the blog post for a long time.

“Last updated.” Two words and a date, generated dynamically by code that Our Adventurer had written from scratch. Code that was now running on a server somewhere, being executed as posts were viewed, doing exactly the thing it was told to do.

The notebook got a new page: “I wrote a plugin. It works. PHP is actually comprehensible.”

Followed by, after a moment’s reflection: “PHP is not actually fully comprehensible yet but it is more comprehensible than it was.”

Which is all you can ever really ask.

PHP’s Peculiarities and Why They Are Historically Explicable If Not Entirely Forgivable

PHP has a reputation. Among programmers, particularly those who work in other languages, PHP is sometimes spoken of with a kind of fond exasperation, like a family member who is well-meaning and has contributed enormously to the household but has some habits that other family members find baffling.

The function naming, for instance. PHP’s built-in functions have no consistent naming convention. Some use underscores: str_replace(). Some use no separators at all: strlen(). Some seem to have been named by someone who was in a hurry: htmlspecialchars(). The parameter order is inconsistent: some functions take the string first and the search term second; others reverse this. This is because PHP grew organically over decades, different functions added at different times by different people with different conventions, and nobody ever went back and standardised it.

Our Adventurer wrote in the notebook: “PHP is like a city that grew without planning. The old part is charming but confusing. The new part is logical. You need a map for both.”

PHP is also, increasingly, a modern and capable language. PHP 8.x has features that make it genuinely pleasant to work with: named arguments, enums, union types, the match expression that is cleaner than the old switch statement, the nullsafe operator that handles the deeply common situation of “call a method on a value that might be null.” PHP has evolved, substantially and credibly, and dismissing it based on its 2005 reputation is like dismissing a person based on a school report.

WordPress runs on PHP, and WordPress powers forty percent of the entire web, which means that however you feel about PHP as a language, you must acknowledge its staggering real-world impact. It is there. It is doing things. Forty percent of everything is an enormous portfolio.

Our Adventurer thought about this, and felt a kind of solidarity with PHP. Also a language that had been judged on early impressions and found to be more interesting on closer acquaintance. Also something that had been built incrementally over time by many different hands and bore the marks of that history in its structure. Also something that worked, consistently, reliably, even if the internal design was not always what a purist would choose.

PHP, Our Adventurer decided, was all right.

The Functions.php File: A Grand Tour

Every WordPress theme has a functions.php file, and every theme developer treats it differently. Some are tidy, with code organised into sections with clear comments. Some are chaotic, with dozens of small functions appended over years without any organising principle. Our Adventurer’s child theme’s functions.php had grown from two lines (importing the parent theme’s styles) to about two hundred, containing the random quote function, the last-updated notice (later moved to its own plugin), several small customisations to the way archives were displayed, a function that added a custom class to the body element on the homepage for special homepage styling, and a function that added proper social media meta tags to every page.

The social media meta tags function was a notable achievement. Open Graph tags are HTML meta elements in the <head> that tell social media platforms how to display your page when it is shared: what image to use, what title, what description. Without them, social media platforms make their own decisions about this and the results are often suboptimal, sometimes showing the wrong image or a truncated version of the first paragraph as the description.

The function queried the post’s featured image for the og:image tag, the post title for og:title, and the post excerpt for og:description. It added proper Twitter Card tags as well (Twitter, whatever it is calling itself this week, uses its own variation of Open Graph). The result was that shared posts on social media showed beautiful preview cards with the correct image and a properly written description.

Our Adventurer shared a post link on social media to test it.

The preview card appeared correctly.

This felt like magic. It was not magic. It was PHP outputting HTML meta tags in the document head. But the gap between understanding the mechanism and experiencing the result retained, somehow, the quality of a small miracle.

The PHP Battle: Concluded for Now

PHP was not defeated in a single dramatic encounter. It was understood gradually, in layers, each layer revealing another layer beneath it. Functions, then hooks, then the template hierarchy, then the query object, then the database interactions, then object-oriented PHP, each one a new territory accessed through the door that the previous territory had opened.

But the first boss had been defeated. The one guarding the entrance to the server-side world. Our Adventurer was now inside the engine room of WordPress, able to read the code and understand what it did, able to write functions and attach them to hooks and produce real, working functionality.

The blog was better for it. The dates were in British format. The last-updated notices were there. The random quotes were appearing. The social media cards were correct. The child theme was protecting all of it from accidental destruction by theme updates.

And somewhere on the horizon, growing larger as Our Adventurer’s PHP understanding grew, was the outline of the next major challenge.

JavaScript was waiting.

JavaScript was alive.

JavaScript was next.

Leave a Reply

Get updates

From art exploration to the latest archeological findings, all here in our weekly newsletter.

Subscribe

Discover more from Richard Morrison

Subscribe now to keep reading and get access to the full archive.

Continue reading