⚡ Lightweight Flat-File PHP Blog Script

December 18, 2025

⚡ A tiny flat-file PHP blog script with zero database. Posts are simple .md files inside a posts/ folder. This version includes optional starter posts and fully scoped CSS so it won’t interfere with the rest of your site.

Live demo: View working example

<?php
/**
 * Flat-File PHP Blog (No DB, Demo-Safe)
 *
 * Install:
 *   /blog/index.php
 *   /blog/posts/
 *
 * URLs:
 *   /blog/
 *   /blog/?post=slug
 */

declare(strict_types=1);

// ---------------- CONFIG ----------------
$posts_dir       = __DIR__ . '/posts';
$site_title      = 'My Flat-File Blog';
$site_tagline    = 'Tiny PHP blog powered by Markdown files.';
$auto_seed_posts = true;

// ---------------- HEADERS ----------------
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: SAMEORIGIN');

// ---------------- HELPERS ----------------
function esc(string $s): string {
    return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

function safe_slug(string $s): string {
    $s = strtolower(trim($s));
    $s = preg_replace('/[^a-z0-9]+/', '-', $s);
    return trim($s, '-');
}

function parse_post(string $file): array {
    $raw = @file_get_contents($file);
    if (!$raw) return [];

    [$head, $body] = array_pad(preg_split("/\R\R/", $raw, 2), 2, '');
    $out = ['title'=>'Untitled','date'=>'','excerpt'=>'','body'=>$body];

    foreach (preg_split("/\R/", $head) as $line) {
        if (strpos($line, ':') === false) continue;
        [$k,$v] = array_map('trim', explode(':',$line,2));
        $k = strtolower($k);
        if (isset($out[$k])) $out[$k] = $v;
    }
    return $out;
}

function markdown(string $t): string {
    $t = esc($t);
    $t = preg_replace('/^### (.+)$/m','<h3>$1</h3>',$t);
    $t = preg_replace('/^## (.+)$/m','<h2>$1</h2>',$t);
    $t = preg_replace('/^# (.+)$/m','<h1>$1</h1>',$t);
    $t = preg_replace('/\*\*(.+?)\*\*/','<strong>$1</strong>',$t);
    $t = preg_replace('/\*(.+?)\*/','<em>$1</em>',$t);
    return nl2br($t,false);
}

function seed_posts(string $dir): void {
    if (glob($dir.'/*.md')) return;
    @mkdir($dir,0755,true);

    file_put_contents($dir.'/welcome.md',
"Title: Welcome
Date: 2025-12-18
Excerpt: Your blog is live.

# Welcome
This is a **flat-file** blog.
Each post is just a Markdown file.");

    file_put_contents($dir.'/adding-posts.md',
"Title: Adding Posts
Date: 2025-12-17
Excerpt: Create a file and refresh.

# How it works
Create a new <code>.md</code> file in the posts folder.
Refresh the page. That’s it.");
}

// ---------------- RUN ----------------
if ($auto_seed_posts) seed_posts($posts_dir);

$slug = isset($_GET['post']) ? safe_slug($_GET['post']) : '';
$posts = [];

foreach (glob($posts_dir.'/*.md') as $f) {
    $p = parse_post($f);
    if (!$p) continue;
    $p['slug'] = safe_slug(basename($f,'.md'));
    $p['ts'] = $p['date'] ? strtotime($p['date']) : filemtime($f);
    $posts[] = $p;
}

usort($posts, fn($a,$b)=>$b['ts']<=>$a['ts']);

// ---------------- VIEW (SCOPED) ----------------
echo '<div class="ffb">';
echo '<style>
.ffb{font-family:system-ui,Arial,sans-serif;background:#eef0ff;padding:20px}
.ffb .box{max-width:820px;margin:auto;background:#fff;border-radius:16px;padding:16px}
.ffb h1{margin:0}
.ffb a{color:#A829A7;font-weight:700;text-decoration:none}
.ffb .small{color:#5a6177;font-size:13px}
.ffb ul{list-style:none;padding:0}
.ffb li{margin:12px 0}
</style>';

echo '<div class="box">';
echo '<h1>'.esc($site_title).'</h1>';
echo '<div class="small">'.esc($site_tagline).'</div>';

if ($slug) {
    foreach ($posts as $p) {
        if ($p['slug'] === $slug) {
            echo '<hr><h2>'.esc($p['title']).'</h2>';
            echo '<div class="small">'.esc($p['date']).'</div>';
            echo markdown($p['body']);
            echo '<p><a href="./">&#10140; Back</a></p>';
            echo '</div></div>';
            exit;
        }
    }
    http_response_code(404);
    echo '<p>Post not found.</p></div></div>';
    exit;
}

echo '<hr><ul>';
foreach ($posts as $p) {
    echo '<li><a href="?post='.$p['slug'].'">'.esc($p['title']).'</a>
          <div class="small">'.esc($p['excerpt']).'</div></li>';
}
echo '</ul></div></div>';
?>

Comments (0)

No comments yet — be the first.

← Back to all scripts