A simple PHP webcomic site

My friend Alex posted recently on mastodon that they wanted to update their comics sites to add a “news” post underneath each page, and asked if anyone wanted to take a stab at the PHP for it.

So, I, having Copious Free Time[1], immediately volunteered to do this very trivial task. Which quickly turned into “redo the entire thing virtually from scratch so that it sucks less”. Anyway, so that happened, and I now have this big piece of PHP code for a very particular kind of lightweight comics website, and I am going to share it with the universe for anyone to use! Huzzah?

This code functions on certain basic assumptions. Or, rather, two basic assumptions, which are that all of your comic page files are images numbered sequentially using the same number of digits (e.g. 001.png 002.png 003.png) and that a “news” post will be a .html file named the same number as the comic page it goes with (e.g. 001.html 003.html).

Beyond that, it has a few customizable variables at the top for folder names, navigation button image names, your homepage URL, stuff like that, and the HTML itself is all kept neatly at the bottom so you can add or rearrange things to suit your layout.

If you have any questions, you can always ping me on mastodon. I’ll eventually update this post to put in proper licensing documentation or whatever is necessary to put it officially in the public domain, but for the moment, feel free to go ahead and use or modify the following PHP/HTML code for whatever you like.

[1]: also known as None

The style below applies to the comic image and all navigation images.
You can change it or move it to your own stylesheet.
<style>.comic_img { border:0px; }</style>

// Change these to suit your site!
$homepage  = "http://example.com"; // The home page of your comic
$comic_dir = "comics";             // The folder where comic image files are kept
$news_dir  = "news";               // The folder where post/news HTML files are kept
$digits    = 3;                    // The number of digits in each comic ID. e.g. 3 for 001

$firstlink = "first.gif";  // first page button
$prevlink  = "back.gif";   // previous page button
$homelink  = "home.gif";   // Home button
$nextlink  = "next.gif";   // next page button
$newlink   = "newest.gif"; // newest page button

// !! DON'T change any PHP below this line, unless you know what you're doing
// Skip to the end if you need to add styling or move page elements.

// These two functions print out all the comic image IDs as a dropdown menu
function add_option($item, $key, $target_dir) {
  if (substr(mime_content_type("$target_dir/$item"),0,5) == 'image') {
    $item_id = explode(".", $item)[0];
    if ($item_id == $_GET['id']) echo "<option selected>$item_id</option>\n";
    else echo "<option>$item_id</option>\n";
function comics_dropdown($target_dir) {
  echo '<form name="comiclist">';
  echo '<select name="id" id="id">';
  if (filetype($target_dir) == 'dir') {
    $file_list = scandir($target_dir,1);
    array_walk($file_list, 'add_option', $target_dir);
  echo '</select><input type="submit" value="Go" method="get"></form>';


// Checks whether a page exists for a given ID in the directory
// If it does, returns the file name
function page_exists($idnum, $target_dir) {
  global $digits;
  if (strlen($idnum) < $digits) $idnum = str_pad($idnum, $digits, '0', STR_PAD_LEFT);
  if (filetype($target_dir) == 'dir') {
    $flist = glob("$target_dir/$idnum.*");
    if (count($flist) > 0) return $flist[0];
    else return false;
  else return false;

// Retrieves the newest comic or alphabetically "largest" file
function get_newest($target_dir, $comic) {
  // scandir in reverse order, grab the top one for a while loop until it's an image
  if (filetype($target_dir) == 'dir') {
    $templist = scandir($target_dir,1);
    if ($comic) {
      foreach($templist as $item) {
        if (substr(mime_content_type("$target_dir/$item"),0,5) == 'image') break;
        else $i++;
    $newest = explode(".", $templist[$i])[0];
    return $newest;
  else return false;

// Displays the navigation buttons
function display_nav($target_dir) {
  global $firstlink, $prevlink, $homelink, $nextlink, $newlink, $homepage;
  $php_file = $_SERVER['PHP_SELF'];
  if ($newest = get_newest($target_dir, true)) {
    if (empty($_GET['id'])) {
      $prev = $newest-1;
      $next = $newest;
      $page_id = $newest;
    else {
      $prev = $_GET['id']-1;
      $next = $_GET['id']+1;
      $page_id = $_GET['id'];
      if ($next > $newest) $next = $newest;
    if ($prev > 1) echo "<a href=\"$php_file?id=1\"><img src=\"$firstlink\" alt=\"First\" class=\"comic_img\" /></a>";
    if ($prev > 0) echo "<a href=\"$php_file?id=$prev\"><img src=\"$prevlink\" alt=\"Previous\" class=\"comic_img\" /></a>";
    echo "<a href=\"$homepage\"><img src=\"$homelink\" alt=\"Home\" class=\"comic_img\" /></a>";
    if ($page_id < $newest) echo "<a href=\"$php_file?id=$next\"><img src=\"$nextlink\" alt=\"Next\" class=\"comic_img\" /></a>";
    if ($next < $newest) echo "<a href=\"$php_file?id=$newest\"><img src=\"$newlink\" alt=\"Newest\" class=\"comic_img\" /></a>\n";

// Displays the comic image for the current page
function display_comic($target_dir) {
  if (empty($_GET['id'])) $page_id = get_newest($target_dir, true);
  else $page_id = $_GET['id'];
  if ($comic_img = page_exists($page_id, $target_dir)) {
    echo "<img src=\"$comic_img\" class=\"comic_img\" />";

// Displays the news file for the current page.
function display_news($target_dir) {
  if (empty($_GET['id'])) $page_id = get_newest($target_dir, false);
  else $page_id = $_GET['id'];
  if ($news_file = page_exists($page_id, $target_dir)) include($news_file);

<!-- This is the part that actually displays the page.
You can add to it as needed for extra menus, to insert style classes, etc. -->

<div align="center">
<?php display_comic($comic_dir); /*displays the comic image*/ ?>
<?php display_nav($comic_dir); /*displays the comic navigation buttons*/ ?>
<?php comics_dropdown($comic_dir); /*displays the archive dropdown*/ ?>

<?php display_news($news_dir); /*inserts the news post html*/ ?>