If you gain a basic understanding of how to build a website using PHP, you’ll have knowledge of file inclusions and producing output—the very basics of customizing a web page. Maybe you’re wondering where to begin, or what next steps to take when building more functionality into your site.

In this tutorial, I present a simple site structure that allows for straightforward updates. It also demonstrates some useful PHP functions to add value.

The Website You’ll Be Building in PHP

The sample website—available in a GitHub repository—is a simple site about birds in all their feathery goodness. There are a couple of sections, some info pages, and a home page. The final site isn’t as important as the techniques you’ll read about, and the ideas they may inspire in you.

grab 2022-02-14 at 11.28.25

If you can, download the site from GitHub and install it on your local computer before reading on.

Overall Structure of the Site

Here are some of the key files and directories you should investigate.

        composer.json
data/
funcs.php
md/
site/
 index.php
tpl/
  • Every page corresponds to a PHP script in the site directory. This introduces some redundancy, but it keeps things simple. When setting up your web server, make sure you point your DOCROOT at the site directory.
  • The md directory contains markdown source files that store the main content of some individual pages.
  • The tpl directory contains templates and files that define the overall HTML structure that several pages can share.
  • Most of the PHP functionality is in funcs.php.

Related: How to Set Up Your Own WAMP Server

How the Site Works

Bootstrap

Bootstrap is a front-end framework for building websites. It has built-in styles and JavaScript functionality, covering most common web dev needs. It’s an excellent way of quickly getting a site up and running before you spend time fine-tuning the design.

You can install and host the Bootstrap files on your server, but for maximum speed, you can just reference them from a CDN. Take a look at tpl/head.php and you should see an example like:

        <link
   href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
   rel="stylesheet"
   integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
   crossorigin="anonymous"
/>

Basic Templating

Start with site/index.php. This is the file that represents the site’s home page. Depending on how your web server is set up, you should be able to access it at http://yoursite.example.org/ or, failing that, http://yoursite.example.org/index.php.

Note the two includes right at the beginning of this file: funcs.php and TPL_DIR."/home.php". The funcs.php file defines that TPL_DIR constant as the full absolute path of the tpl directory at the top level of the site.

Take a look at tpl/home.php. This is the very outermost skeleton of an html document: it only includes a doctype and an HTML element. Within the HTML element, it uses two includes for templates representing the head and body.

        <!DOCTYPE html>
<html lang="en">
    <?php include TPL_DIR."/head.php"; ?>
    <?php include TPL_DIR."/home/body.php"; ?>
</html>

In contrast, the template for the birds section loads a different body template, tpl/birds/body.tpl. This contains a different layout for pages within that section, one with a sidebar.

Parsing Markdown

In the composer.json file, a third-party library called erusev/parsedown is required. This is a Markdown-parsing library. It allows you to, very easily, convert Markdown text into HTML.

Many static sites generate final HTML from documents written in another language, such as Markdown. It offers a nicer syntax for authoring, one which is more readable than HTML. The sample site presents this as an option. Take a look at the show_content() function in funcs.php:

        function show_content() {
    $file = MD_DIR.PAGE.'.md';
    
    if (file_exists($file)) {
        $Parsedown = new Parsedown();
        echo $Parsedown->text(file_get_contents($file));
    } else if (function_exists("content")) {
        echo content();
    }
}

If a Markdown file corresponding to the requested page exists, a Parsedown object converts its contents into HTML and outputs it. If there’s no Markdown file, it looks for a function named content() instead. Individual pages can define this function (see site/index.php for an example) if their content needs to go beyond what static Markdown can achieve.

Loading Metadata

Take a look at the get_json function in funcs.php:

        function get_json($file) {
    $data_file = DATA_DIR."/".$file;

    if (!file_exists($data_file)) {
        return array();
    }

    if (($json = file_get_contents($data_file)) === false) {
        return array();
    }

    if (($out = json_decode($json, true)) === null) {
        return array();
    }

    return $out;
}

Essentially, the function calls two others to fetch and parse data from a given file. It dresses that up with a lot of error-checking to return an empty array if anything goes wrong. Calling json_decode() like this returns an associative array which makes it very easy to work with the data immediately. This approach is so convenient that you might prefer it to using a database, particularly for simple tasks like global configuration.

The site uses two metadata files: data/titles.json and data/featured.json. The former stores the title of each page, useful for auto-linking and in other cases.

        {
    "/": "Home",
    "/about": "About",
    "/birds": "Bird profiles",
    ...
}

This is a handy way of keeping all page titles in one place (file) which is easy to update when you need to. These titles contribute to the navigation in the top menu, breadcrumb list, and side panels.

        function page_title($page = PAGE) {
    $titles = get_titles();
    return array_key_exists($page, $titles) ? $titles[$page] : basename($page);
}
A simple website page showing information about a bird with its photo

Here’s the first part of the breadcrumbs() function (from funcs.php) which builds an array of page titles for each part of the URL. For example, for the /birds/blue-tit page, it fetches the title for the / page, then the title for /birds, then finally the title for /birds/blue-tit. The breadcrumb list will then consist of one link for each page in the hierarchy.

        function breadcrumbs() {
    $items = array();
    $titles = get_titles();
    $parts = explode("/", PAGE);
    $href = "";

    foreach ($parts as $part) {
        $href .= ($href == "/" ? "" : "/").$part;
        $items[$href] = $titles[$href];
    }
    ...
}

Getting Hold of Other Metadata

If you view the About page, a specific news story, or a specific bird profile, you should be able to spot a Last updated message in the footer. The site’s footer is stored in tpl/footer.php. Open this file and note the snippet of PHP inside it:

        if (file_exists($file = MD_DIR.PAGE.'.md')) {
    echo 'Last updated: '.date('r', filemtime(MD_DIR.PAGE.'.md'));
}

This is a simple example of fetching metadata directly from a file rather than a database or other source. The filemtime function returns the last modified time of a file. Note that this is a very convenient method for getting the content’s date, but it’s not without its flaws.

See also: Understanding Linux File Timestamps: mtime, ctime, and atime

Extracting Data Using Regular Expressions

It’s often necessary to reuse content across a site, to show news summaries on a contents page, for example. With a full-blown CMS, you might store this data as extra fields in a database. With a simple flat-file approach, you need to think differently.

The news stories on this site are stored as Markdown files, so the raw content is available. Regular expressions are a handy tool in a developer’s kit, giving you the ability to match and extract content. The news/index.php file does just that to show its content. It fetches the contents of each file in the md/news directory as a string. It then runs several regular expressions to extract data.

A simple website showing two news stories about birds, with photographs

The title is a line that begins with a # symbol, according to Markdown syntax. Optional whitespace then follows, before the actual title. The regular expression ^#\s+(.+) matches that pattern of text and preg_match() returns everything that matched between the brackets: the title.

        if (preg_match("/^#\​s+(.+)/", $contents, $matches)) {
    $title = $matches[1];
}

Two more regular expressions follow to extract the image and first sentence from the news markdown file. There are downsides to this approach; in particular, it requires discipline to ensure Markdown files are always formatted exactly as the code expects.

See also: The Beginner’s Guide to Regular Expressions With Python

Selecting Random Content

The /birds page doesn’t do much, so a random bird image livens it up. This is easy to achieve with a bit of file system inspection and a function built into PHP. The magic happens in site/birds/index.php:

        function content() {
    $files = scandir(SITE_DIR."/img");
    $files = array_filter($files, function($file) { return $file[0] != '.'; });
    $file = $files[array_rand($files)];
    
    echo '<h1>Birds</h1>';
    echo '<img src="/img/'.$file.'" />';
}

The trick is organizing all images for this specific function into a single directory. The scandir() function then reads the files from this directory into an array and array_rand() gives us a random key from the array so the function can pick an individual file.

Building Websites and Skill in PHP

I hope you've learned at least one new thing from this article. More importantly, I hope it has inspired you to consider various approaches and demonstrated how you can use PHP in many different ways.

Once you’re familiar with PHP, you’ll start thinking of alternative solutions to each problem. This is a natural progression and means you’re starting to think at a higher level, focussing less on the programming language itself and more on what you can do with it.