<?php declare( strict_types=1 ); final class BlogIndex { private string $baseDir; private array $posts; public function __construct() { $this->baseDir = __DIR__; $this->posts = []; $this->scan(); } private function scan() : void { $flags = FilesystemIterator::SKIP_DOTS; $dirIter = new RecursiveDirectoryIterator( $this->baseDir, $flags ); $iter = new RecursiveIteratorIterator( $dirIter, RecursiveIteratorIterator::SELF_FIRST ); foreach ( $iter as $file ) { if ( $file->isDir() === false ) { continue; } $path = $file->getPathname(); if ( $path === $this->baseDir ) { continue; } $relative = str_replace( $this->baseDir, '', $path ); $relative = ltrim( $relative, '/' ); $parts = explode( '/', $relative ); if ( count( $parts ) !== 4 ) { continue; } list( $year, $month, $day, $slug ) = $parts; if ( ctype_digit( $year ) && ctype_digit( $month ) && ctype_digit( $day ) ) { $title = $this->readTitle( $path . '/index.html' ); $this->posts[] = [ 'year' => $year, 'month' => $month, 'day' => $day, 'slug' => $slug, 'url' => $relative . '/', 'title' => $title ]; } } usort( $this->posts, function ( $a, $b ) : int { $ka = $b['year'] . $b['month'] . $b['day']; $kb = $a['year'] . $a['month'] . $a['day']; return strcmp( $ka, $kb ); } ); } private function readTitle( string $filePath ) : string { $title = ''; $html = ''; if ( file_exists( $filePath ) ) { $content = file_get_contents( $filePath ); $html = $content === false ? '' : $content; } if ( $html !== '' ) { if ( preg_match( '/<title[^>]*>(.*?)<\/title>/is', $html, $m ) ) { $t = trim( $m[1] ); $title = $t === '' ? '' : $t; } } return $title; } public function render() : void { ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Blog Index</title> <link rel="stylesheet" href="../styles/base.css"> </head> <body> <header> <img src="../images/logo/title.svg" alt="KeenWrite" class="title"> <p> A free, cross-platform desktop text editor for producing <a href="../docs/user-manual.pdf">beautifully typeset</a> PDF files. </p> </header> <main> <h1>Blog</h1> <ul> <?php foreach ( $this->posts as $post ) { ?> <li> <a href="<?= htmlspecialchars( $post['url'], ENT_QUOTES | ENT_SUBSTITUTE ) ?>"><?= htmlspecialchars( $post['title'] !== '' ? $post['title'] : $post['slug'], ENT_QUOTES | ENT_SUBSTITUTE ) ?></a> <?= ' (' . htmlspecialchars( $post['year'] . '-' . $post['month'] . '-' . $post['day'], ENT_QUOTES | ENT_SUBSTITUTE ) . ')' ?> </li> <?php } ?> </ul> </main> </body> </html> <?php return; } } $index = new BlogIndex(); $index->render();