From 20290aebe22ae8ba4a28611e99f57814e79a0146 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 23 Sep 2026 13:56:38 -0700 Subject: [PATCH 1/6] Blog CSS cleanup --- resources/blog-styles.css | 92 +++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/resources/blog-styles.css b/resources/blog-styles.css index f8b18c1..8cd899c 100644 --- a/resources/blog-styles.css +++ b/resources/blog-styles.css @@ -14,56 +14,62 @@ > *:last-child { margin-bottom: 0px; } -} -.blog-content h1, -.blog-preview h2, -.blog-preview h3 { - margin-bottom: 0px; + h2, h3 { + margin-bottom: 0px; + } } -.blog-content img { - width: 50%; - display: block; - margin: auto; -} +.blog-content { + code { + white-space: nowrap; + } -.blog-page--sidebar-image p:has(img) { - width: 40%; - float: right; -} + h1 { + margin-bottom: 0px; + } -.blog-page--sidebar-image img { - width: fit-content; -} + img { + width: 50%; + display: block; + margin: auto; + } -.blog-content .pygments-highlighter pre { - background-color: #f8f8f8; - overflow-x: auto; -} + .footnotes { + li { + list-style-position: inside; + } -.blog-content .pygments-highlighter-failed { - /* Accessibility: `red` does not provide enough contrast */ - color: #ef0000; -} + ol { + padding-inline-start: 0px; + } -.blog-content .footnotes { - p { - font-size: smaller; - display: inline; + p { + font-size: smaller; + display: inline; + } } - ol { - padding-inline-start: 0px; + .pygments-highlighter pre { + background-color: #f8f8f8; + overflow-x: auto; } - li { - list-style-position: inside; + .pygments-highlighter-failed { + /* Accessibility: `red` does not provide enough contrast */ + color: #ef0000; } } -.blog-content code { - white-space: nowrap; +.blog-page--sidebar-image { + img { + width: fit-content; + } + + p:has(img) { + width: 40%; + float: right; + } } .blog-page--unhighlighted-block pre:not(.pygments-highlighter pre) { @@ -117,12 +123,14 @@ top: unset; margin-top: 20px; } - .blog-page--sidebar-image p:has(img) { - float: unset; - margin: auto; - max-width: 100%; - } - .blog-page--sidebar-image img { - max-width: 100%; + .blog-page--sidebar-image { + img { + max-width: 100%; + } + p:has(img) { + float: unset; + margin: auto; + max-width: 100%; + } } } From dc9cbb6c5acedc01d7afcdb159d504f3e29663a5 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 23 Sep 2026 15:29:27 -0700 Subject: [PATCH 2/6] Blog: add tags support --- Dockerfile | 4 +- resources/blog-styles.css | 105 ++++++++++++++++++ resources/error-styles.css | 21 +++- src/Blog/BlogPost.php | 4 + src/Blog/BlogTags.php | 89 +++++++++++++++ src/Blog/posts/20250409-website-launch.md | 1 + .../posts/20250417-php85-release-manager.md | 1 + .../posts/20250429-attributes-on-constants.md | 1 + src/Blog/posts/20250622-two-rfcs.md | 1 + .../posts/20250718-pygments-highlighting.md | 1 + src/Blog/posts/20250801-no-alpha-3.md | 1 + .../20250820-delayed-target-validation.md | 1 + src/Blog/posts/20250829-out-of-band.md | 1 + src/Blog/posts/20250906-mago-rust.md | 1 + src/Blog/posts/20250912-hello-world-rust.md | 1 + src/Blog/posts/20250919-rust-euler.md | 1 + src/Blog/posts/20251029-longhorn-php.md | 1 + .../posts/20251113-release-candidate-5.md | 1 + src/Blog/posts/20251118-mergephp-talk.md | 1 + src/Blog/posts/20251125-unplanned-downtime.md | 1 + src/Blog/posts/20251201-rust-euler-2.md | 1 + src/Blog/posts/20260120-php-2025.md | 1 + .../posts/20260218-release-manager-notes.md | 1 + src/Blog/posts/20260302-confoo.md | 1 + src/Blog/posts/20260309-php-friends.md | 1 + src/Blog/posts/20260410-define-deprecated.md | 1 + .../posts/20260416-php86-release-manager.md | 1 + src/Blog/posts/20260430-php856-rc-3.md | 1 + .../posts/20260510-release-manager-notes-2.md | 1 + src/Blog/posts/20260517-blog-rss.md | 1 + src/Blog/posts/20260525-phptek-2026.md | 1 + src/Blog/posts/20260710-reflection-rust.md | 1 + src/Blog/posts/20260810-fossy-2026.md | 1 + src/Blog/posts/20260909-ecosystem-security.md | 1 + src/Blog/posts/20260923-no-php86-rc-1.md | 1 + src/Pages/BlogIndexPage.php | 98 ++++++++++++++-- src/Pages/BlogPostPage.php | 27 ++++- src/Pages/RedirectPage.php | 2 +- src/Router.php | 28 ++--- tests/Blog/BlogPostTest.php | 6 + tests/Blog/BlogTagsTest.php | 55 +++++++++ tests/SitemapGeneratorTest.php | 4 +- tests/StaticOutputTest.php | 2 + tests/data/blog-highlighting.html | 2 +- tests/data/blog-image.html | 2 +- tests/data/blog-index.html | 60 +++++----- tests/data/blog-launch.html | 2 +- tests/data/blog-tag-filter.html | 10 ++ tests/data/blog-tag-unknown.html | 91 +++++++++++++++ 49 files changed, 565 insertions(+), 77 deletions(-) create mode 100644 src/Blog/BlogTags.php create mode 100644 tests/Blog/BlogTagsTest.php create mode 100644 tests/data/blog-tag-filter.html create mode 100644 tests/data/blog-tag-unknown.html diff --git a/Dockerfile b/Dockerfile index a175733..f09eeb7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM php:8.3.19-apache +FROM php:8.5.10-apache # Add git RUN apt-get -y update @@ -10,7 +10,7 @@ RUN apt-get install -y libzip-dev zip RUN docker-php-ext-install zip && docker-php-ext-enable zip # Add composer -COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer +COPY --from=composer:2.10.3 /usr/bin/composer /usr/local/bin/composer # For test coverage RUN pecl install pcov diff --git a/resources/blog-styles.css b/resources/blog-styles.css index 8cd899c..c0e61a5 100644 --- a/resources/blog-styles.css +++ b/resources/blog-styles.css @@ -1,3 +1,15 @@ +.blog-index { + h1 { + display: inline-block; + } + .blog-index--clear-filter { + display: inline-block; + font-size: 20px; + margin-block-end: 0.67em; + padding-left: 20px; + } +} + .blog-preview-date { font-style: italic; } @@ -20,6 +32,29 @@ } } +.blog-preview--has-tags { + columns: 2; + + h2, h3 { + break-after: avoid-column; + width: max-content; + } + + .blog-tags { + justify-self: right; + padding-top: 5px; + break-before: column; + } + + .blog-tags--pill:last-child { + margin-right: 0px; + } + + p { + column-span: all; + } +} + .blog-content { code { white-space: nowrap; @@ -35,6 +70,10 @@ margin: auto; } + .blog-tags { + padding-top: 20px; + } + .footnotes { li { list-style-position: inside; @@ -107,6 +146,56 @@ } } +.blog-tags { + font-size: 18px; +} + +.blog-tags--list { + display: inline; + padding-inline-start: 0px; +} + +.blog-tags--pill { + height: 1.5rem; + border-radius: 0.75rem; + display: inline; + padding: 5px 10px 5px 10px; + margin: 5px; + + a { + text-decoration: none; + } + + &.blog-tags--pill-conferences { + background-color: #53fd89; + a { + color: #000; + } + } + + &.blog-tags--pill-website { + background-color: #ffb900; + a { + color: #000; + } + } + + &.blog-tags--pill-php, + &[class*=" blog-tags--pill-php-"] { + background-color: #4f5b93; + a { + color: #fff; + } + } + + &.blog-tags--pill-rust { + background-color: #D34516; + a { + color: #fff; + } + } +} + @media (max-width: 1200px) { .blog-page--has-toc.content-wrapper { grid-template-columns: 20% 70% 10%; @@ -133,4 +222,20 @@ max-width: 100%; } } + .blog-preview--has-tags { + columns: unset; + + p { + margin-top: 10px; + } + + .blog-tags { + justify-self: unset; + } + + .blog-tags--pill:first-child { + margin-left: 0px; + } + } + } diff --git a/resources/error-styles.css b/resources/error-styles.css index fed546d..508063e 100644 --- a/resources/error-styles.css +++ b/resources/error-styles.css @@ -1,6 +1,5 @@ -.error-box { - background-color: #fcdad4; - border: 3px solid #fc9685; +.error-box, +.warning-box { padding: 10px; margin-top: 20px; @@ -9,6 +8,16 @@ max-width: 60%; } -h1 { - margin-top: 0px; -} \ No newline at end of file +.error-box { + background-color: #fcdad4; + border: 3px solid #fc9685; + + h1 { + margin-top: 0px; + } +} + +.warning-box { + background-color: #fae17a; + border: 3px solid #c9a202; +} diff --git a/src/Blog/BlogPost.php b/src/Blog/BlogPost.php index 03b51f3..d126506 100644 --- a/src/Blog/BlogPost.php +++ b/src/Blog/BlogPost.php @@ -29,6 +29,10 @@ public function getTitle(): string { return $this->config['title'] ?? $this->slug; } + public function getTags(): array { + return $this->config['tags'] ?? []; + } + public function getExtraClasses(): array { if ( $this->config !== null ) { $extraClasses = $this->config['extra-classes'] ?? []; diff --git a/src/Blog/BlogTags.php b/src/Blog/BlogTags.php new file mode 100644 index 0000000..9c6dca4 --- /dev/null +++ b/src/Blog/BlogTags.php @@ -0,0 +1,89 @@ + 'conferences and other events', + 'php' => [ + 'description' => 'programming with PHP', + 'display' => 'PHP', + ], + 'php-rm' => [ + 'description' => 'being a PHP release manager', + 'display' => 'PHP-RM', + ], + 'php-rm-85' => [ + 'description' => 'being a PHP 8.5 rookie release manager', + 'display' => 'PHP-RM-8.5', + ], + 'php-rm-86' => [ + 'description' => 'being the PHP 8.6 veteran release manager', + 'display' => 'PHP-RM-8.6', + ], + 'php-dev' => [ + 'description' => 'development of PHP', + 'display' => 'PHP-dev', + ], + 'rust' => 'programming with Rust', + 'website' => 'my website', + ]; + + public static function isKnown( string $tag ): bool { + return array_key_exists( $tag, self::KNOWN_TAGS ); + } + + public static function getDescription( string $tag ): string { + if ( !self::isKnown( $tag ) ) { + throw new InvalidArgumentException( "Unknown tag: $tag" ); + } + $info = self::KNOWN_TAGS[$tag]; + if ( is_string( $info ) ) { + return $info; + } + return $info[ 'description' ]; + } + + private static function getPillForTag( string $tag ): FluentHTML { + if ( !self::isKnown( $tag ) ) { + throw new InvalidArgumentException( "Unknown tag: $tag" ); + } + $info = self::KNOWN_TAGS[$tag]; + if ( is_string( $info ) ) { + $description = $info; + $display = ucfirst( $tag ); + } else { + $description = $info[ 'description' ]; + $display = $info[ 'display' ]; + } + + return FluentHTML::make( + 'li', + [ 'class' => [ 'blog-tags--pill', "blog-tags--pill-$tag" ] ], + FluentHTML::make( + 'a', + [ + 'href' => "/Blog?tag=$tag", + 'title' => "Blog posts about $description", + ], + "#$display", + ) + ); + } + + public static function getListForTags( array $tags ): FluentHTML { + return FluentHTML::make( + 'ul', + [ 'class' => 'blog-tags--list' ], + array_map( self::getPillForTag( ... ), $tags ), + ); + } + +} diff --git a/src/Blog/posts/20250409-website-launch.md b/src/Blog/posts/20250409-website-launch.md index ad9197a..a8a7cf2 100644 --- a/src/Blog/posts/20250409-website-launch.md +++ b/src/Blog/posts/20250409-website-launch.md @@ -1,5 +1,6 @@ --- title: Website Launch +tags: ["website"] --- # Website launch diff --git a/src/Blog/posts/20250417-php85-release-manager.md b/src/Blog/posts/20250417-php85-release-manager.md index 18b0842..3ee7fd1 100644 --- a/src/Blog/posts/20250417-php85-release-manager.md +++ b/src/Blog/posts/20250417-php85-release-manager.md @@ -1,5 +1,6 @@ --- title: PHP 8.5 Release Manager +tags: ["php", "php-rm", "php-rm-85"] --- # PHP 8.5 Release Manager diff --git a/src/Blog/posts/20250429-attributes-on-constants.md b/src/Blog/posts/20250429-attributes-on-constants.md index 76394de..7524380 100644 --- a/src/Blog/posts/20250429-attributes-on-constants.md +++ b/src/Blog/posts/20250429-attributes-on-constants.md @@ -3,6 +3,7 @@ extensions: pygments: true toc: true title: Attributes on Constants +tags: ["php", "php-dev"] --- # Attributes on constants diff --git a/src/Blog/posts/20250622-two-rfcs.md b/src/Blog/posts/20250622-two-rfcs.md index eb010d7..70f062d 100644 --- a/src/Blog/posts/20250622-two-rfcs.md +++ b/src/Blog/posts/20250622-two-rfcs.md @@ -3,6 +3,7 @@ extensions: pygments: true toc: true title: A Tale of Two RFCs +tags: ["php", "php-dev"] --- # A Tale of Two RFCs diff --git a/src/Blog/posts/20250718-pygments-highlighting.md b/src/Blog/posts/20250718-pygments-highlighting.md index 30a4f7d..99a7580 100644 --- a/src/Blog/posts/20250718-pygments-highlighting.md +++ b/src/Blog/posts/20250718-pygments-highlighting.md @@ -4,6 +4,7 @@ extensions: pygments: true toc: true title: Pygments Syntax Highlighting for Markdown +tags: ["php"] --- # Pygments Syntax Highlighting for Markdown diff --git a/src/Blog/posts/20250801-no-alpha-3.md b/src/Blog/posts/20250801-no-alpha-3.md index a19a6e7..2946a6b 100644 --- a/src/Blog/posts/20250801-no-alpha-3.md +++ b/src/Blog/posts/20250801-no-alpha-3.md @@ -2,6 +2,7 @@ title: No PHP 8.5.0alpha3 extra-classes: - blog-page--sidebar-image +tags: ["php", "php-rm", "php-rm-85"] --- # No PHP 8.5.0alpha3 diff --git a/src/Blog/posts/20250820-delayed-target-validation.md b/src/Blog/posts/20250820-delayed-target-validation.md index a891006..1d62448 100644 --- a/src/Blog/posts/20250820-delayed-target-validation.md +++ b/src/Blog/posts/20250820-delayed-target-validation.md @@ -2,6 +2,7 @@ title: "#[\\DelayedTargetValidation] Attribute Explained" extensions: pygments: true +tags: ["php", "php-dev"] --- # #[\DelayedTargetValidation] Attribute Explained diff --git a/src/Blog/posts/20250829-out-of-band.md b/src/Blog/posts/20250829-out-of-band.md index 343601b..283c304 100644 --- a/src/Blog/posts/20250829-out-of-band.md +++ b/src/Blog/posts/20250829-out-of-band.md @@ -1,5 +1,6 @@ --- title: "Out-of-Band Signaling" +tags: ["php", "php-dev"] --- # Out-of-Band Signaling diff --git a/src/Blog/posts/20250906-mago-rust.md b/src/Blog/posts/20250906-mago-rust.md index 4bbc187..46d0bd2 100644 --- a/src/Blog/posts/20250906-mago-rust.md +++ b/src/Blog/posts/20250906-mago-rust.md @@ -2,6 +2,7 @@ title: "First Rust Patch" extensions: pygments: true +tags: ["rust"] --- # First Rust Patch diff --git a/src/Blog/posts/20250912-hello-world-rust.md b/src/Blog/posts/20250912-hello-world-rust.md index 33f1e70..435cb03 100644 --- a/src/Blog/posts/20250912-hello-world-rust.md +++ b/src/Blog/posts/20250912-hello-world-rust.md @@ -2,6 +2,7 @@ title: "\"Hello, World!\" in Rust" extensions: pygments: true +tags: ["rust"] --- # "Hello, World!" in Rust diff --git a/src/Blog/posts/20250919-rust-euler.md b/src/Blog/posts/20250919-rust-euler.md index 8517992..a64dbd6 100644 --- a/src/Blog/posts/20250919-rust-euler.md +++ b/src/Blog/posts/20250919-rust-euler.md @@ -5,6 +5,7 @@ extensions: extra-classes: - blog-page--unhighlighted-block title: Project Euler in Rust +tags: ["rust"] --- # Project Euler in Rust diff --git a/src/Blog/posts/20251029-longhorn-php.md b/src/Blog/posts/20251029-longhorn-php.md index 9522d43..15b1c4e 100644 --- a/src/Blog/posts/20251029-longhorn-php.md +++ b/src/Blog/posts/20251029-longhorn-php.md @@ -2,6 +2,7 @@ extensions: toc: true title: Longhorn PHP 2025 +tags: ["conferences", "php"] --- # Longhorn PHP 2025 diff --git a/src/Blog/posts/20251113-release-candidate-5.md b/src/Blog/posts/20251113-release-candidate-5.md index b5e59d9..26e7080 100644 --- a/src/Blog/posts/20251113-release-candidate-5.md +++ b/src/Blog/posts/20251113-release-candidate-5.md @@ -2,6 +2,7 @@ title: The Story of PHP 8.5.0 Release Candidate 5 extra-classes: - blog-page--sidebar-image +tags: ["php", "php-rm", "php-rm-85"] --- # The Story of PHP 8.5.0 Release Candidate 5 diff --git a/src/Blog/posts/20251118-mergephp-talk.md b/src/Blog/posts/20251118-mergephp-talk.md index 9ec07ee..72c4ef9 100644 --- a/src/Blog/posts/20251118-mergephp-talk.md +++ b/src/Blog/posts/20251118-mergephp-talk.md @@ -1,5 +1,6 @@ --- title: MergePHP, November 2025 +tags: ["conferences", "php"] --- # MergePHP, November 2025 diff --git a/src/Blog/posts/20251125-unplanned-downtime.md b/src/Blog/posts/20251125-unplanned-downtime.md index 4c99e79..a1fd3af 100644 --- a/src/Blog/posts/20251125-unplanned-downtime.md +++ b/src/Blog/posts/20251125-unplanned-downtime.md @@ -2,6 +2,7 @@ title: Unplanned Downtime, November 2025 extensions: footnotes: true +tags: ["website"] --- # Unplanned Downtime, November 2025 diff --git a/src/Blog/posts/20251201-rust-euler-2.md b/src/Blog/posts/20251201-rust-euler-2.md index 496fff1..5c6ac0b 100644 --- a/src/Blog/posts/20251201-rust-euler-2.md +++ b/src/Blog/posts/20251201-rust-euler-2.md @@ -2,6 +2,7 @@ extensions: pygments: true title: Project Euler in Rust, Part 2 +tags: ["rust"] --- # Project Euler in Rust, Part 2 diff --git a/src/Blog/posts/20260120-php-2025.md b/src/Blog/posts/20260120-php-2025.md index 4ee7962..474b72c 100644 --- a/src/Blog/posts/20260120-php-2025.md +++ b/src/Blog/posts/20260120-php-2025.md @@ -3,6 +3,7 @@ extensions: pygments: true toc: true title: This Year in PHP (2025) +tags: ["php", "php-dev"] --- # This Year in PHP (2025) diff --git a/src/Blog/posts/20260218-release-manager-notes.md b/src/Blog/posts/20260218-release-manager-notes.md index cc2fa4c..256a558 100644 --- a/src/Blog/posts/20260218-release-manager-notes.md +++ b/src/Blog/posts/20260218-release-manager-notes.md @@ -1,5 +1,6 @@ --- title: Notes for PHP Release Managers +tags: ["php", "php-rm"] --- # Notes for PHP Release Managers diff --git a/src/Blog/posts/20260302-confoo.md b/src/Blog/posts/20260302-confoo.md index 14e7ff5..a5ac96f 100644 --- a/src/Blog/posts/20260302-confoo.md +++ b/src/Blog/posts/20260302-confoo.md @@ -2,6 +2,7 @@ extensions: toc: true title: ConFoo 2026 +tags: ["conferences", "php", "rust"] --- # ConFoo 2026 diff --git a/src/Blog/posts/20260309-php-friends.md b/src/Blog/posts/20260309-php-friends.md index 6a413b1..3253341 100644 --- a/src/Blog/posts/20260309-php-friends.md +++ b/src/Blog/posts/20260309-php-friends.md @@ -3,6 +3,7 @@ extensions: footnotes: true pygments: true title: Friends in PHP +tags: ["php", "php-dev"] --- # Friends in PHP diff --git a/src/Blog/posts/20260410-define-deprecated.md b/src/Blog/posts/20260410-define-deprecated.md index fc9ccfd..352298c 100644 --- a/src/Blog/posts/20260410-define-deprecated.md +++ b/src/Blog/posts/20260410-define-deprecated.md @@ -2,6 +2,7 @@ extensions: pygments: true title: Introducing define_deprecated() for PHP +tags: ["php"] --- # Introducing define_deprecated() for PHP diff --git a/src/Blog/posts/20260416-php86-release-manager.md b/src/Blog/posts/20260416-php86-release-manager.md index 9db4cd5..f9c7cda 100644 --- a/src/Blog/posts/20260416-php86-release-manager.md +++ b/src/Blog/posts/20260416-php86-release-manager.md @@ -1,5 +1,6 @@ --- title: PHP 8.6 Release Manager +tags: ["php", "php-rm", "php-rm-86"] --- # PHP 8.6 Release Manager diff --git a/src/Blog/posts/20260430-php856-rc-3.md b/src/Blog/posts/20260430-php856-rc-3.md index 89554fb..27177e1 100644 --- a/src/Blog/posts/20260430-php856-rc-3.md +++ b/src/Blog/posts/20260430-php856-rc-3.md @@ -2,6 +2,7 @@ title: The Story of PHP 8.5.6 Release Candidate 3 extra-classes: - blog-page--sidebar-image +tags: ["php", "php-rm", "php-rm-85"] --- # The Story of PHP 8.5.6 Release Candidate 3 diff --git a/src/Blog/posts/20260510-release-manager-notes-2.md b/src/Blog/posts/20260510-release-manager-notes-2.md index a0576fb..0c9becb 100644 --- a/src/Blog/posts/20260510-release-manager-notes-2.md +++ b/src/Blog/posts/20260510-release-manager-notes-2.md @@ -1,5 +1,6 @@ --- title: Notes for PHP Release Managers, Part 2 +tags: ["php", "php-rm"] --- # Notes for PHP Release Managers, Part 2 diff --git a/src/Blog/posts/20260517-blog-rss.md b/src/Blog/posts/20260517-blog-rss.md index c30e6ac..f3653a9 100644 --- a/src/Blog/posts/20260517-blog-rss.md +++ b/src/Blog/posts/20260517-blog-rss.md @@ -2,6 +2,7 @@ title: Blog RSS Feed extensions: pygments: true +tags: ["website"] --- # Blog RSS Feed diff --git a/src/Blog/posts/20260525-phptek-2026.md b/src/Blog/posts/20260525-phptek-2026.md index 22317ad..8d0ae8d 100644 --- a/src/Blog/posts/20260525-phptek-2026.md +++ b/src/Blog/posts/20260525-phptek-2026.md @@ -1,5 +1,6 @@ --- title: PHPTek 2026 +tags: ["conferences", "php"] --- # PHPTek 2026 diff --git a/src/Blog/posts/20260710-reflection-rust.md b/src/Blog/posts/20260710-reflection-rust.md index 65d61a2..5a19062 100644 --- a/src/Blog/posts/20260710-reflection-rust.md +++ b/src/Blog/posts/20260710-reflection-rust.md @@ -3,6 +3,7 @@ extensions: footnotes: true pygments: true title: Rewriting Reflection with Rust +tags: ["php", "php-dev", "rust"] --- # Rewriting Reflection with Rust diff --git a/src/Blog/posts/20260810-fossy-2026.md b/src/Blog/posts/20260810-fossy-2026.md index 1397eeb..9156731 100644 --- a/src/Blog/posts/20260810-fossy-2026.md +++ b/src/Blog/posts/20260810-fossy-2026.md @@ -1,5 +1,6 @@ --- title: FOSSY 2026 +tags: ["conferences"] --- # FOSSY 2026 diff --git a/src/Blog/posts/20260909-ecosystem-security.md b/src/Blog/posts/20260909-ecosystem-security.md index 191c7b8..00d8d30 100644 --- a/src/Blog/posts/20260909-ecosystem-security.md +++ b/src/Blog/posts/20260909-ecosystem-security.md @@ -1,5 +1,6 @@ --- title: Joining the PHP Ecosystem Security Team +tags: ["php"] --- # Joining the PHP Ecosystem Security Team diff --git a/src/Blog/posts/20260923-no-php86-rc-1.md b/src/Blog/posts/20260923-no-php86-rc-1.md index 81b1b6e..97a520b 100644 --- a/src/Blog/posts/20260923-no-php86-rc-1.md +++ b/src/Blog/posts/20260923-no-php86-rc-1.md @@ -2,6 +2,7 @@ title: No PHP 8.6.0RC1 extra-classes: - blog-page--sidebar-image +tags: ["php", "php-rm", "php-rm-86"] --- # No PHP 8.6.0RC1 diff --git a/src/Pages/BlogIndexPage.php b/src/Pages/BlogIndexPage.php index 9adbeb1..d46b68c 100644 --- a/src/Pages/BlogIndexPage.php +++ b/src/Pages/BlogIndexPage.php @@ -7,6 +7,7 @@ use DanielEScherzer\HTMLBuilder\RawHTML; use DanielWebsite\Blog\BlogDisplay; use DanielWebsite\Blog\BlogPostStore; +use DanielWebsite\Blog\BlogTags; use DanielWebsite\SitemapEntry; use League\CommonMark\Extension\CommonMark\Node\Block\Heading; use League\CommonMark\Extension\CommonMark\Node\Inline\Link; @@ -18,29 +19,65 @@ #[SitemapEntry( 'Blog' )] class BlogIndexPage extends BasePage { - public function __construct() { - parent::__construct(); + private readonly ?string $tag; + + public function __construct( array $matches ) { + $query = $matches['_url']->getQuery(); + $tag = null; + if ( $query ) { + $params = []; + parse_str( $query, $params ); + if ( isset( $params['tag'] ) ) { + $tag = strtolower( $params['tag'] ); + } + } + $this->tag = $tag; + parent::__construct( $tag !== null ); $this->head->append( FluentHTML::fromTag( 'title' )->addChild( 'Blog index' ) ); } protected function build(): void { + $this->contentWrapper->addClass( 'blog-index' ); $this->addStyleSheet( 'blog-styles.css' ); + $tagFilter = $this->checkTagFilter(); + + $heading = 'Blog index'; + if ( $tagFilter ) { + $desc = BlogTags::getDescription( $this->tag ); + $heading = "Blog posts about $desc"; + } $this->contentWrapper->append( - FluentHTML::make( 'h1', [], 'Blog index' ) + FluentHTML::make( 'h1', [], $heading ) ); + if ( $tagFilter ) { + $this->contentWrapper->append( + FluentHTML::make( + 'span', + [ 'class' => 'blog-index--clear-filter' ], + [ + '(', + FluentHTML::make( 'a', [ 'href' => '/Blog' ], 'clear filter' ), + ')', + ] + ) + ); + } $store = new BlogPostStore(); $posts = $store->listBlogPosts(); - // Use `League\CommonMark` library for parsing, since I write all of - // the blog posts no need to escape unsecure stuff - $env = BlogDisplay::makeCommonMarkEnv( false ); + foreach ( $posts as $post ) { + $env = BlogDisplay::makeCommonMarkEnv( $post ); - $parser = new MarkdownParser( $env ); - $renderer = new HtmlRenderer( $env ); + $tags = $post->getTags(); + if ( $tagFilter && !in_array( $this->tag, $tags, true ) ) { + continue; + } + + $parser = new MarkdownParser( $env ); + $renderer = new HtmlRenderer( $env ); - foreach ( $posts as $post ) { $parsedResult = $parser->parse( $post->markdown ); // For any links in the first paragraph of a blog post that are @@ -65,10 +102,20 @@ protected function build(): void { ->findOne( $parsedResult ); $firstParagraph = $renderer->renderNodes( $firstParagraph->children() ); + $tags = $post->getTags(); + $previewClasses = [ 'blog-preview' ]; + if ( $tags ) { + $tags = FluentHTML::make( + 'div', + [ 'class' => 'blog-tags' ], + BlogTags::getListForTags( $tags ), + ); + $previewClasses[] = 'blog-preview--has-tags'; + } $this->contentWrapper->append( FluentHTML::make( 'div', - [ 'class' => 'blog-preview' ], + [ 'class' => $previewClasses ], [ FluentHTML::make( 'h2', [], new RawHTML( $firstHeading ) ), FluentHTML::make( @@ -76,6 +123,7 @@ protected function build(): void { [ 'class' => 'blog-preview-date' ], $post->date->format( 'l, d F Y' ) ), + $tags ? $tags : [], FluentHTML::make( 'p', [], @@ -95,4 +143,34 @@ protected function build(): void { } } + /** + * Returns whether to filter for only posts with the requested tag, and + * adds a warning if an unknown tag is requested. + */ + private function checkTagFilter(): bool { + if ( !$this->tag ) { + return false; + } + if ( BlogTags::isKnown( $this->tag ) ) { + return true; + } + + $tag = $this->tag; + $this->addStyleSheet( 'error-styles.css' ); + $this->contentWrapper->append( + FluentHTML::make( + 'div', + [ 'class' => 'warning-box' ], + [ + FluentHTML::make( + 'p', + [], + "The requested blog tag '$tag' is not recognized; no filtering is applied" + ), + ] + ) + ); + return false; + } + } diff --git a/src/Pages/BlogPostPage.php b/src/Pages/BlogPostPage.php index 88daa24..b51a2ff 100644 --- a/src/Pages/BlogPostPage.php +++ b/src/Pages/BlogPostPage.php @@ -7,6 +7,7 @@ use DanielEScherzer\HTMLBuilder\RawHTML; use DanielWebsite\Blog\BlogDisplay; use DanielWebsite\Blog\BlogPostStore; +use DanielWebsite\Blog\BlogTags; use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode; use League\CommonMark\Extension\CommonMark\Node\Block\Heading; use League\CommonMark\Extension\CommonMark\Node\Inline\Emphasis; @@ -158,13 +159,27 @@ static function ( Node $node ): bool { ); } - $this->contentWrapper->append( - FluentHTML::make( - 'div', - [ 'class' => 'blog-content' ], - new RawHTML( $html ) - ) + $content = FluentHTML::make( + 'div', + [ 'class' => 'blog-content' ], + new RawHTML( $html ) ); + + $tags = $post->getTags(); + if ( $tags ) { + $content->append( + FluentHTML::make( + 'div', + [ 'class' => 'blog-tags' ], + [ + 'Tags: ', + BlogTags::getListForTags( $tags ), + ], + ) + ); + } + + $this->contentWrapper->append( $content ); $this->contentWrapper->addClass( 'blog-page' ); } diff --git a/src/Pages/RedirectPage.php b/src/Pages/RedirectPage.php index 5e712b7..57f39e2 100644 --- a/src/Pages/RedirectPage.php +++ b/src/Pages/RedirectPage.php @@ -17,7 +17,7 @@ class RedirectPage extends AbstractPage { private string $title; public function __construct( array $params ) { - $title = $params['title']; + $title = $params['_path']; $this->title = $title; } diff --git a/src/Router.php b/src/Router.php index 57703e6..c2c7133 100644 --- a/src/Router.php +++ b/src/Router.php @@ -18,6 +18,7 @@ use DanielWebsite\Pages\WorkPage; use FastRoute\Dispatcher; use FastRoute\RouteCollector; +use Uri\WhatWg\Url; class Router { @@ -28,32 +29,25 @@ public static function pageForRequest( string $requestMethod, string $requestURI ): AbstractPage { - // Remove any query parameters - if ( str_contains( $requestURI, '?' ) ) { - $pos = strpos( $requestURI, '?' ); - $requestURI = substr( $requestURI, 0, $pos ); - } - $requestURI = rawurldecode( $requestURI ); - $requestURI = trim( $requestURI, '/' ); - // Router is case insensitive - $requestURI = strtolower( $requestURI ); - // Prefer uppercase first letter in the strings and error messages - $requestURI = ucfirst( $requestURI ); + $url = new Url( $requestURI, new Url( 'https://scherzer.dev' ) ); + $path = $url->getPath() + |> ( static fn ( string $path ): string => trim( $path, '/' ) ) + |> strtolower( ... ) + |> ucfirst( ... ); // phpcs:ignore MediaWiki.WhiteSpace.SpaceyParenthesis $dispatcher = \FastRoute\simpleDispatcher( self::addRoutesCb(...) ); - $routeInfo = $dispatcher->dispatch( $requestMethod, $requestURI ); + $routeInfo = $dispatcher->dispatch( $requestMethod, $path ); switch ( $routeInfo[0] ) { case Dispatcher::NOT_FOUND: - return new Error404Page( $requestURI ); + return new Error404Page( $path ); case Dispatcher::METHOD_NOT_ALLOWED: - return new Error405Page( $requestURI, $requestMethod, $routeInfo[1] ); + return new Error405Page( $path, $requestMethod, $routeInfo[1] ); case Dispatcher::FOUND: $clazz = $routeInfo[1]; $matches = $routeInfo[2]; - if ( $clazz === RedirectPage::class ) { - $matches = [ 'title' => $requestURI ]; - } + $matches['_path'] = $path; + $matches['_url'] = $url; return new $clazz( $matches ); } } diff --git a/tests/Blog/BlogPostTest.php b/tests/Blog/BlogPostTest.php index a98fe30..4bd43b0 100644 --- a/tests/Blog/BlogPostTest.php +++ b/tests/Blog/BlogPostTest.php @@ -22,6 +22,7 @@ public function testBlogPost() { $this->assertSame( 'random-content', $p->markdown ); $this->assertSame( '20251201-testing', $p->getTitle() ); + $this->assertSame( [], $p->getTags() ); $this->assertSame( [], $p->getExtraClasses() ); $p->setConfig( [ @@ -30,12 +31,17 @@ public function testBlogPost() { 'toc' => true, ], 'extra-classes' => [ 'foo' ], + 'tags' => [ 'baz' ], ] ); $this->assertSame( 'Example', $p->getTitle() ); $this->assertSame( [ 'foo', 'blog-page--has-toc' ], $p->getExtraClasses() ); + $this->assertSame( + [ 'baz' ], + $p->getTags() + ); $this->expectException( RuntimeException::class ); $this->expectExceptionMessage( diff --git a/tests/Blog/BlogTagsTest.php b/tests/Blog/BlogTagsTest.php new file mode 100644 index 0000000..b2fe591 --- /dev/null +++ b/tests/Blog/BlogTagsTest.php @@ -0,0 +1,55 @@ +assertTrue( BlogTags::isKnown( 'php' ) ); + $this->assertFalse( BlogTags::isKnown( 'missing' ) ); + } + + public function testDescription() { + $this->assertSame( BlogTags::KNOWN_TAGS['conferences'], BlogTags::getDescription( 'conferences' ) ); + $this->assertSame( + BlogTags::KNOWN_TAGS['php']['description'], + BlogTags::getDescription( 'php' ) + ); + $this->expectException( InvalidArgumentException::class ); + $this->expectExceptionMessage( + "Unknown tag: missing" + ); + BlogTags::getDescription( 'missing' ); + } + + public function testListForTag() { + $this->assertSame( + '', + BlogTags::getListForTags( [ 'conferences' ] )->getHTML() + ); + $this->assertSame( + '', + BlogTags::getListForTags( [ 'php' ] )->getHTML() + ); + $this->expectException( InvalidArgumentException::class ); + $this->expectExceptionMessage( + "Unknown tag: missing" + ); + BlogTags::getListForTags( [ 'missing' ] ); + } + +} diff --git a/tests/SitemapGeneratorTest.php b/tests/SitemapGeneratorTest.php index 1b6c9e6..379846f 100644 --- a/tests/SitemapGeneratorTest.php +++ b/tests/SitemapGeneratorTest.php @@ -9,7 +9,7 @@ use DanielWebsite\Blog\BlogPostStore; use DanielWebsite\Router; use DanielWebsite\SitemapEntry; -use FastRoute\DataGenerator\MarkBased; +use FastRoute\DataGenerator\CharCountBased; use FastRoute\RouteCollector; use FastRoute\RouteParser\Std; use PHPUnit\Framework\Attributes\CoversClass; @@ -30,7 +30,7 @@ protected function tearDown(): void { } private function generateSitemap( string $location ): void { - $collector = new RouteCollector( new Std(), new MarkBased() ); + $collector = new RouteCollector( new Std(), new CharCountBased() ); Router::addRoutesCb( $collector ); $getPaths = $collector->getData()[0]['GET']; $pages = array_unique( $getPaths ); diff --git a/tests/StaticOutputTest.php b/tests/StaticOutputTest.php index 9932ec6..1934c6c 100644 --- a/tests/StaticOutputTest.php +++ b/tests/StaticOutputTest.php @@ -69,6 +69,8 @@ public static function providePages() { yield 'Blog - exists' => [ 'GET', '/Blog/20250409-website-launch', 'blog-launch.html' ]; yield 'Blog - missing' => [ 'GET', '/Blog/missing', 'blog-missing.html', 404 ]; yield 'Blog - index' => [ 'GET', '/Blog', 'blog-index.html' ]; + yield 'Blog - tag filter' => [ 'GET', '/Blog?tag=WEBSITE', 'blog-tag-filter.html' ]; + yield 'Blog - tag unknown' => [ 'GET', '/Blog?tag=missing', 'blog-tag-unknown.html' ]; yield 'Tools - index' => [ 'GET', '/Tools', 'tools-index.html' ]; yield 'Tools - missing' => [ 'GET', '/Tools/missing', 'tools-missing.html', 404 ]; yield 'Tools - dvorak' => [ 'GET', '/Tools/dvorak', 'tools-dvorak.html' ]; diff --git a/tests/data/blog-highlighting.html b/tests/data/blog-highlighting.html index df63d6a..ba3fbb1 100644 --- a/tests/data/blog-highlighting.html +++ b/tests/data/blog-highlighting.html @@ -314,4 +314,4 @@

\ No newline at end of file diff --git a/tests/data/blog-image.html b/tests/data/blog-image.html index fd6feca..febe727 100644 --- a/tests/data/blog-image.html +++ b/tests/data/blog-image.html @@ -61,4 +61,4 @@

Alpha4

so that people who wanted to actually test out the new version (which should only be used for tests, it is not ready for production) could do so properly.

- \ No newline at end of file + \ No newline at end of file diff --git a/tests/data/blog-index.html b/tests/data/blog-index.html index 1a60ac2..c9150b1 100644 --- a/tests/data/blog-index.html +++ b/tests/data/blog-index.html @@ -1,90 +1,90 @@ -Blog index

Blog index

No PHP 8.6.0RC1

Wednesday, 23 September 2026

Tomorrow, the PHP 8.6 release manager team is planning to announce the first +Blog index

Blog index

No PHP 8.6.0RC1

Wednesday, 23 September 2026

Tomorrow, the PHP 8.6 release manager team is planning to announce the first release candidate (RC) of PHP 8.6, PHP 8.6.0RC2. So, what happened to -PHP 8.6.0RC1? Continue reading...

Joining the PHP Ecosystem Security Team

Wednesday, 09 September 2026

I'm excited to share that I am joining the PHP Foundation's Ecosystem Security -Team to help harden the PHP codebase. Continue reading...

FOSSY 2026

Monday, 10 August 2026

Last week, I was in Vancouver for the FOSSY 2026 conference, where I gave a talk +PHP 8.6.0RC1? Continue reading...

Joining the PHP Ecosystem Security Team

Wednesday, 09 September 2026

I'm excited to share that I am joining the PHP Foundation's Ecosystem Security +Team to help harden the PHP codebase. Continue reading...

FOSSY 2026

Monday, 10 August 2026

Last week, I was in Vancouver for the FOSSY 2026 conference, where I gave a talk about my experience as a new open-source contributor. My experiences as a contributor, and previous conferences I have attended, have mostly been focused -on PHP, but FOSSY is a conference about open source software more broadly. Continue reading...

Rewriting Reflection with Rust

Friday, 10 July 2026

Over the last few weeks, I have been working on rewriting the PHP Reflection +on PHP, but FOSSY is a conference about open source software more broadly. Continue reading...

Rewriting Reflection with Rust

Friday, 10 July 2026

Over the last few weeks, I have been working on rewriting the PHP Reflection extension in Rust as an exercise in building extensions in Rust. In addition to understanding how to implement extensions in Rust, the process also -highlighted a number of ways that the Reflection extension could be improved. Continue reading...

PHPTek 2026

Monday, 25 May 2026

Last week, I was in Chicago for the PHPTek 2026 conference, where I gave two +highlighted a number of ways that the Reflection extension could be improved. Continue reading...

PHPTek 2026

Monday, 25 May 2026

Last week, I was in Chicago for the PHPTek 2026 conference, where I gave two talks, one about upcoming changes in PHP 8.6 and the other about semantic versioning. Between the formal sessions and the informal hallway track conversations, I left the conference with plenty of new ideas for both PHP -applications and future language improvements. Continue reading...

Blog RSS Feed

Sunday, 17 May 2026

Last month, I received a request to add an RSS feed to my blog. I am +applications and future language improvements. Continue reading...

Blog RSS Feed

Sunday, 17 May 2026

Last month, I received a request to add an RSS feed to my blog. I am happy to share that I have added such a feed, available at -/Blog/feed/rss.xml. Continue reading...

Notes for PHP Release Managers, Part 2

Sunday, 10 May 2026

A few months ago, I wrote up a blog post about my experience +/Blog/feed/rss.xml. Continue reading...

Notes for PHP Release Managers, Part 2

Sunday, 10 May 2026

A few months ago, I wrote up a blog post about my experience being a PHP release manager and the kind of commitment that the role requires. Now, I want to document some of the decision-making and process related to the -latest release that I oversaw, PHP 8.5.6. Continue reading...

The Story of PHP 8.5.6 Release Candidate 3

Thursday, 30 April 2026

I have previously had to skip a non-stable release +latest release that I oversaw, PHP 8.5.6. Continue reading...

The Story of PHP 8.5.6 Release Candidate 3

Thursday, 30 April 2026

I have previously had to skip a non-stable release (PHP 8.5.0 alpha3), and to create an extra unplanned release candidate (PHP 8.5.0 RC5). For PHP 8.5.6 release candidate 3, I had to do both: release candidate 2 was unplanned, and then an error led to skipping it, resulting in release candidate 3 that I just announced. -Here is what happened. Continue reading...

PHP 8.6 Release Manager

Thursday, 16 April 2026

I'm excited to announce that I will be serving as the "veteran" release manager +Here is what happened. Continue reading...

PHP 8.6 Release Manager

Thursday, 16 April 2026

I'm excited to announce that I will be serving as the "veteran" release manager for the PHP 8.6 release cycle. In that role I will be mentoring two new -"rookie" release managers to ensure a smooth and successful release process. Continue reading...

Introducing define_deprecated() for PHP

Friday, 10 April 2026

In PHP 8.5, I introduced support for attributes on constants, +"rookie" release managers to ensure a smooth and successful release process. Continue reading...

Introducing define_deprecated() for PHP

Friday, 10 April 2026

In PHP 8.5, I introduced support for attributes on constants, which allows marking compile-time global constants as deprecated. However, that functionality was not made easily available when defining constants at runtime, or in code that supports older versions of PHP. My new library, -danielescherzer/define-deprecated, provides that support. Continue reading...

Friends in PHP

Monday, 09 March 2026

As I mentioned in my last blog post, while at ConFoo I and a few +danielescherzer/define-deprecated, provides that support. Continue reading...

Friends in PHP

Monday, 09 March 2026

As I mentioned in my last blog post, while at ConFoo I and a few other developers worked on some ideas for adding friendship support to PHP. To be clear, these are just ideas at the moment, but I figured they were worth -sharing. Continue reading...

ConFoo 2026

Monday, 02 March 2026

Last week, I was in Montreal for the ConFoo conference, where I gave two talks, +sharing. Continue reading...

ConFoo 2026

Monday, 02 March 2026

Last week, I was in Montreal for the ConFoo conference, where I gave two talks, one about Rust and one about PHP. I also attended plenty of fascinating talks -given by others, and absorbed some new ideas along the way. Continue reading...

Notes for PHP Release Managers

Wednesday, 18 February 2026

Each release of PHP has a team of release managers responsible for managing +given by others, and absorbed some new ideas along the way. Continue reading...

Notes for PHP Release Managers

Wednesday, 18 February 2026

Each release of PHP has a team of release managers responsible for managing the release process. Next month, the other PHP 8.5 release managers and I will begin the process of selecting the release managers for PHP 8.6. I figured it might be helpful to document my experiences so far, to give potential volunteers -some extra context. Continue reading...

This Year in PHP (2025)

Tuesday, 20 January 2026

Now that we are a few weeks into 2026, I figured it would be a good opportunity +some extra context. Continue reading...

This Year in PHP (2025)

Tuesday, 20 January 2026

Now that we are a few weeks into 2026, I figured it would be a good opportunity for me to reflect on my PHP contributions in 2025. It was a pretty big year for -me in terms of contributing to the php-src repo. Continue reading...

Project Euler in Rust, Part 2

Monday, 01 December 2025

Over the last few months (I started writing this blog post in September), +me in terms of contributing to the php-src repo. Continue reading...

Project Euler in Rust, Part 2

Monday, 01 December 2025

Over the last few months (I started writing this blog post in September), I've continued to learn Rust by working through Project Euler problems. Since I will be giving a talk about Rust at ConFoo in February, learning Rust is no -longer just for fun - I also need to make sure I don't make a fool of myself. Continue reading...

Unplanned Downtime, November 2025

Tuesday, 25 November 2025

Last week, I had some unexpected downtime for my website. The original cause was +longer just for fun - I also need to make sure I don't make a fool of myself. Continue reading...

Unplanned Downtime, November 2025

Tuesday, 25 November 2025

Last week, I had some unexpected downtime for my website. The original cause was a power issue at the physical server, and out of my control, but an oversight of -mine made the situation worse. Continue reading...

MergePHP, November 2025

Tuesday, 18 November 2025

Last week, I presented "PHP 8.5: New Features from the Source" for a second +mine made the situation worse. Continue reading...

MergePHP, November 2025

Tuesday, 18 November 2025

Last week, I presented "PHP 8.5: New Features from the Source" for a second time, this time through MergePHP, an online meetup combining multiple PHP user groups. The talk generally went better than it did the -previous time I presented on that topic, at Longhorn PHP. Continue reading...

The Story of PHP 8.5.0 Release Candidate 5

Thursday, 13 November 2025

PHP 8.5.0 Release Candidate 4, announced last week, was expected +previous time I presented on that topic, at Longhorn PHP. Continue reading...

The Story of PHP 8.5.0 Release Candidate 5

Thursday, 13 November 2025

PHP 8.5.0 Release Candidate 4, announced last week, was expected to be the last release candidate (RC) of the PHP 8.5.0 development cycle, before the general availability release next week. But, earlier today I announced the release of PHP 8.5.0 RC5. So, why was an extra -release candidate needed? Continue reading...

Longhorn PHP 2025

Wednesday, 29 October 2025

Last week, I attended the Longhorn PHP conference in Austin, Texas. I had the +release candidate needed? Continue reading...

Longhorn PHP 2025

Wednesday, 29 October 2025

Last week, I attended the Longhorn PHP conference in Austin, Texas. I had the opportunity to meet with a slew of open source contributors, listen to some -amazing talks, and even present my own. Continue reading...

Project Euler in Rust

Friday, 19 September 2025

After writing the obligatory "Hello, World!", I wanted to work on something +amazing talks, and even present my own. Continue reading...

Project Euler in Rust

Friday, 19 September 2025

After writing the obligatory "Hello, World!", I wanted to work on something more substantial in Rust. When I learn a new programming language, my go-to source of coding problems to try and solve in the new language is Project Euler, which features hundreds of coding challenges of -various difficulty levels. Continue reading...

"Hello, World!" in Rust

Friday, 12 September 2025

Yesterday, I started to teach myself how to program in Rust. As always, the first -step was a "Hello, World!" program. Continue reading...

First Rust Patch

Saturday, 06 September 2025

As part of preparing for my Longhorn PHP talk about what is new in PHP 8.5, I +various difficulty levels. Continue reading...

"Hello, World!" in Rust

Friday, 12 September 2025

Yesterday, I started to teach myself how to program in Rust. As always, the first +step was a "Hello, World!" program. Continue reading...

First Rust Patch

Saturday, 06 September 2025

As part of preparing for my Longhorn PHP talk about what is new in PHP 8.5, I have been going through various PHP tools and checking to what extent they support the new features. When the support is lacking, I have sometimes tried to -add it myself; this led me to making my first patch in Rust. Continue reading...

Out-of-Band Signaling

Friday, 29 August 2025

Sometimes, code needs to be able to signal an error has occurred. Perhaps some +add it myself; this led me to making my first patch in Rust. Continue reading...

Out-of-Band Signaling

Friday, 29 August 2025

Sometimes, code needs to be able to signal an error has occurred. Perhaps some invariant was violated. Perhaps an invalid parameter was used. Regardless of the reason, there are two ways to signal an issue: "in-band signaling", and -"out-of-band signaling". Continue reading...

#[\DelayedTargetValidation] Attribute Explained

Wednesday, 20 August 2025

PHP 8.5 will come with a new attribute, #[\DelayedTargetValidation], that +"out-of-band signaling". Continue reading...

#[\DelayedTargetValidation] Attribute Explained

Wednesday, 20 August 2025

PHP 8.5 will come with a new attribute, #[\DelayedTargetValidation], that is intended to help libraries that support running on multiple versions of PHP. The attribute can be a bit confusing, so as the author I figured I should write -up a (hopefully helpful) guide. Continue reading...

No PHP 8.5.0alpha3

Friday, 01 August 2025

Yesterday, I announced the release of the last alpha version +up a (hopefully helpful) guide. Continue reading...

No PHP 8.5.0alpha3

Friday, 01 August 2025

Yesterday, I announced the release of the last alpha version of PHP 8.5.0, labeled 8.5.0alpha4. This comes two weeks after the previous -version, 8.5.0alpha2, was announced. So, what happened to 8.5.0alpha3? Continue reading...

Pygments Syntax Highlighting for Markdown

Friday, 18 July 2025

This week, I published a new composer package that adds support for syntax +version, 8.5.0alpha2, was announced. So, what happened to 8.5.0alpha3? Continue reading...

Pygments Syntax Highlighting for Markdown

Friday, 18 July 2025

This week, I published a new composer package that adds support for syntax highlighting of code snippets embedded in Markdown documents. The new package uses the Pygments highlighting library (from Python) and connects it to the -CommonMark PHP library for rendering Markdown. Continue reading...

A Tale of Two RFCs

Sunday, 22 June 2025

A few minutes ago, my third RFC, +CommonMark PHP library for rendering Markdown. Continue reading...

A Tale of Two RFCs

Sunday, 22 June 2025

A few minutes ago, my third RFC, adding support for promoted final properties, was implemented after being approved unanimously. This comes after my second RFC, adding support for never parameters, was rejected by the -community. Continue reading...

Attributes on constants

Tuesday, 29 April 2025

Earlier today, the attributes-on-constants RFC implementation was finally +community. Continue reading...

Attributes on constants

Tuesday, 29 April 2025

Earlier today, the attributes-on-constants RFC implementation was finally merged. This was a project I worked on for months and a significant reason I became more active as a PHP core developer. The initial motivation was to add a way for constants to be marked as deprecated, so that MediaWiki could use that feature to signal deprecated constants instead of relying solely on -documentation. Continue reading...

PHP 8.5 Release Manager

Thursday, 17 April 2025

I'm honored to share that the PHP internals development community has elected +documentation. Continue reading...

PHP 8.5 Release Manager

Thursday, 17 April 2025

I'm honored to share that the PHP internals development community has elected me as one of the two "rookie" release managers for the PHP 8.5 release cycle. Working alongside my fellow release managers, I hope to help ensure a smooth and -successful release process. Continue reading...

Website launch

Wednesday, 09 April 2025

Today, I launched the initial version of my personal website (where you are most +successful release process. Continue reading...

Website launch

Wednesday, 09 April 2025

Today, I launched the initial version of my personal website (where you are most likely reading this blog post). This marks the cumulation of over a month of preparation and development, because I wanted to create the website myself, rather than using a website builder like WordPress. While I do rely on a few diff --git a/tests/data/blog-launch.html b/tests/data/blog-launch.html index 0e21d24..d679d97 100644 --- a/tests/data/blog-launch.html +++ b/tests/data/blog-launch.html @@ -74,4 +74,4 @@

Launching the site

as of writing both production and staging are showing the content from commit b35c425312f646550732877e956d6447fe3c0c15, but when you read this the sites will have been updated.

-
\ No newline at end of file +
Tags:
\ No newline at end of file diff --git a/tests/data/blog-tag-filter.html b/tests/data/blog-tag-filter.html new file mode 100644 index 0000000..dd2f882 --- /dev/null +++ b/tests/data/blog-tag-filter.html @@ -0,0 +1,10 @@ + +Blog index

Blog posts about my website

(clear filter)

Blog RSS Feed

Sunday, 17 May 2026

Last month, I received a request to add an RSS feed to my blog. I am +happy to share that I have added such a feed, available at +/Blog/feed/rss.xml. Continue reading...

Unplanned Downtime, November 2025

Tuesday, 25 November 2025

Last week, I had some unexpected downtime for my website. The original cause was +a power issue at the physical server, and out of my control, but an oversight of +mine made the situation worse. Continue reading...

Website launch

Wednesday, 09 April 2025

Today, I launched the initial version of my personal website (where you are most +likely reading this blog post). This marks the cumulation of over a month of +preparation and development, because I wanted to create the website myself, +rather than using a website builder like WordPress. While I do rely on a few +external libraries, the content and styles are all mine. Continue reading...

\ No newline at end of file diff --git a/tests/data/blog-tag-unknown.html b/tests/data/blog-tag-unknown.html new file mode 100644 index 0000000..a092fc9 --- /dev/null +++ b/tests/data/blog-tag-unknown.html @@ -0,0 +1,91 @@ + +Blog index

The requested blog tag 'missing' is not recognized; no filtering is applied

Blog index

No PHP 8.6.0RC1

Wednesday, 23 September 2026

Tomorrow, the PHP 8.6 release manager team is planning to announce the first +release candidate (RC) of PHP 8.6, PHP 8.6.0RC2. So, what happened to +PHP 8.6.0RC1? Continue reading...

Joining the PHP Ecosystem Security Team

Wednesday, 09 September 2026

I'm excited to share that I am joining the PHP Foundation's Ecosystem Security +Team to help harden the PHP codebase. Continue reading...

FOSSY 2026

Monday, 10 August 2026

Last week, I was in Vancouver for the FOSSY 2026 conference, where I gave a talk +about my experience as a new open-source contributor. My experiences as a +contributor, and previous conferences I have attended, have mostly been focused +on PHP, but FOSSY is a conference about open source software more broadly. Continue reading...

Rewriting Reflection with Rust

Friday, 10 July 2026

Over the last few weeks, I have been working on rewriting the PHP Reflection +extension in Rust as an exercise in building extensions in Rust. In addition +to understanding how to implement extensions in Rust, the process also +highlighted a number of ways that the Reflection extension could be improved. Continue reading...

PHPTek 2026

Monday, 25 May 2026

Last week, I was in Chicago for the PHPTek 2026 conference, where I gave two +talks, one about upcoming changes in PHP 8.6 and the other about semantic +versioning. Between the formal sessions and the informal hallway track +conversations, I left the conference with plenty of new ideas for both PHP +applications and future language improvements. Continue reading...

Blog RSS Feed

Sunday, 17 May 2026

Last month, I received a request to add an RSS feed to my blog. I am +happy to share that I have added such a feed, available at +/Blog/feed/rss.xml. Continue reading...

Notes for PHP Release Managers, Part 2

Sunday, 10 May 2026

A few months ago, I wrote up a blog post about my experience +being a PHP release manager and the kind of commitment that the role requires. +Now, I want to document some of the decision-making and process related to the +latest release that I oversaw, PHP 8.5.6. Continue reading...

The Story of PHP 8.5.6 Release Candidate 3

Thursday, 30 April 2026

I have previously had to skip a non-stable release +(PHP 8.5.0 alpha3), and to create an extra unplanned release +candidate (PHP 8.5.0 RC5). For PHP 8.5.6 release candidate 3, I had +to do both: release candidate 2 was unplanned, and then an error led to skipping +it, resulting in release candidate 3 that I just announced. +Here is what happened. Continue reading...

PHP 8.6 Release Manager

Thursday, 16 April 2026

I'm excited to announce that I will be serving as the "veteran" release manager +for the PHP 8.6 release cycle. In that role I will be mentoring two new +"rookie" release managers to ensure a smooth and successful release process. Continue reading...

Introducing define_deprecated() for PHP

Friday, 10 April 2026

In PHP 8.5, I introduced support for attributes on constants, +which allows marking compile-time global constants as deprecated. However, +that functionality was not made easily available when defining constants at +runtime, or in code that supports older versions of PHP. My new library, +danielescherzer/define-deprecated, provides that support. Continue reading...

Friends in PHP

Monday, 09 March 2026

As I mentioned in my last blog post, while at ConFoo I and a few +other developers worked on some ideas for adding friendship support to PHP. To +be clear, these are just ideas at the moment, but I figured they were worth +sharing. Continue reading...

ConFoo 2026

Monday, 02 March 2026

Last week, I was in Montreal for the ConFoo conference, where I gave two talks, +one about Rust and one about PHP. I also attended plenty of fascinating talks +given by others, and absorbed some new ideas along the way. Continue reading...

Notes for PHP Release Managers

Wednesday, 18 February 2026

Each release of PHP has a team of release managers responsible for managing +the release process. Next month, the other PHP 8.5 release managers and I will +begin the process of selecting the release managers for PHP 8.6. I figured it +might be helpful to document my experiences so far, to give potential volunteers +some extra context. Continue reading...

This Year in PHP (2025)

Tuesday, 20 January 2026

Now that we are a few weeks into 2026, I figured it would be a good opportunity +for me to reflect on my PHP contributions in 2025. It was a pretty big year for +me in terms of contributing to the php-src repo. Continue reading...

Project Euler in Rust, Part 2

Monday, 01 December 2025

Over the last few months (I started writing this blog post in September), +I've continued to learn Rust by working through Project Euler problems. Since I +will be giving a talk about Rust at ConFoo in February, learning Rust is no +longer just for fun - I also need to make sure I don't make a fool of myself. Continue reading...

Unplanned Downtime, November 2025

Tuesday, 25 November 2025

Last week, I had some unexpected downtime for my website. The original cause was +a power issue at the physical server, and out of my control, but an oversight of +mine made the situation worse. Continue reading...

MergePHP, November 2025

Tuesday, 18 November 2025

Last week, I presented "PHP 8.5: New Features from the Source" for a second +time, this time through MergePHP, an online meetup combining +multiple PHP user groups. The talk generally went better than it did the +previous time I presented on that topic, at Longhorn PHP. Continue reading...

The Story of PHP 8.5.0 Release Candidate 5

Thursday, 13 November 2025

PHP 8.5.0 Release Candidate 4, announced last week, was expected +to be the last release candidate (RC) of the PHP 8.5.0 development cycle, before +the general availability release next week. But, earlier today I +announced the release of PHP 8.5.0 RC5. So, why was an extra +release candidate needed? Continue reading...

Longhorn PHP 2025

Wednesday, 29 October 2025

Last week, I attended the Longhorn PHP conference in Austin, Texas. I had the +opportunity to meet with a slew of open source contributors, listen to some +amazing talks, and even present my own. Continue reading...

Project Euler in Rust

Friday, 19 September 2025

After writing the obligatory "Hello, World!", I wanted to work on something +more substantial in Rust. When I learn a new programming language, my go-to +source of coding problems to try and solve in the new language is +Project Euler, which features hundreds of coding challenges of +various difficulty levels. Continue reading...

"Hello, World!" in Rust

Friday, 12 September 2025

Yesterday, I started to teach myself how to program in Rust. As always, the first +step was a "Hello, World!" program. Continue reading...

First Rust Patch

Saturday, 06 September 2025

As part of preparing for my Longhorn PHP talk about what is new in PHP 8.5, I +have been going through various PHP tools and checking to what extent they +support the new features. When the support is lacking, I have sometimes tried to +add it myself; this led me to making my first patch in Rust. Continue reading...

Out-of-Band Signaling

Friday, 29 August 2025

Sometimes, code needs to be able to signal an error has occurred. Perhaps some +invariant was violated. Perhaps an invalid parameter was used. Regardless of the +reason, there are two ways to signal an issue: "in-band signaling", and +"out-of-band signaling". Continue reading...

#[\DelayedTargetValidation] Attribute Explained

Wednesday, 20 August 2025

PHP 8.5 will come with a new attribute, #[\DelayedTargetValidation], that +is intended to help libraries that support running on multiple versions of PHP. +The attribute can be a bit confusing, so as the author I figured I should write +up a (hopefully helpful) guide. Continue reading...

No PHP 8.5.0alpha3

Friday, 01 August 2025

Yesterday, I announced the release of the last alpha version +of PHP 8.5.0, labeled 8.5.0alpha4. This comes two weeks after the previous +version, 8.5.0alpha2, was announced. So, what happened to 8.5.0alpha3? Continue reading...

Pygments Syntax Highlighting for Markdown

Friday, 18 July 2025

This week, I published a new composer package that adds support for syntax +highlighting of code snippets embedded in Markdown documents. The new package +uses the Pygments highlighting library (from Python) and connects it to the +CommonMark PHP library for rendering Markdown. Continue reading...

A Tale of Two RFCs

Sunday, 22 June 2025

A few minutes ago, my third RFC, +adding support for promoted final properties, was implemented +after being approved unanimously. This comes after my second RFC, +adding support for never parameters, was rejected by the +community. Continue reading...

Attributes on constants

Tuesday, 29 April 2025

Earlier today, the attributes-on-constants RFC implementation was finally +merged. This was a project I worked on for months and a significant reason I +became more active as a PHP core developer. The initial motivation was to add a +way for constants to be marked as deprecated, so that MediaWiki could use that +feature to signal deprecated constants instead of relying solely on +documentation. Continue reading...

PHP 8.5 Release Manager

Thursday, 17 April 2025

I'm honored to share that the PHP internals development community has elected +me as one of the two "rookie" release managers for the PHP 8.5 release cycle. +Working alongside my fellow release managers, I hope to help ensure a smooth and +successful release process. Continue reading...

Website launch

Wednesday, 09 April 2025

Today, I launched the initial version of my personal website (where you are most +likely reading this blog post). This marks the cumulation of over a month of +preparation and development, because I wanted to create the website myself, +rather than using a website builder like WordPress. While I do rely on a few +external libraries, the content and styles are all mine. Continue reading...

\ No newline at end of file From b02032c833ed7abe69e39d603f61e0410b9b0125 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 23 Sep 2026 15:30:44 -0700 Subject: [PATCH 3/6] CI PHP version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 777db69..6177b4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: pull_request: env: - PHP_VERSION: 8.3 + PHP_VERSION: 8.5 jobs: lint: From 704f1080976a3283f08e3d4adea4496c0af4f109 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sun, 27 Sep 2026 23:58:35 -0700 Subject: [PATCH 4/6] stash --- src/Blog/posts/20261005-blog-tags.md | 33 ++++++++++++++++++++++++++++ src/Pages/BlogIndexPage.php | 2 +- src/Pages/LandingPage.php | 16 ++++++++++++-- 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/Blog/posts/20261005-blog-tags.md diff --git a/src/Blog/posts/20261005-blog-tags.md b/src/Blog/posts/20261005-blog-tags.md new file mode 100644 index 0000000..7a8b3c6 --- /dev/null +++ b/src/Blog/posts/20261005-blog-tags.md @@ -0,0 +1,33 @@ +--- +title: Blog Tags +extensions: + pygments: true +tags: ["website"] +--- + +# Blog Tags + +Blog posts on my website now have tags to help filter them! You can see the +tags for each post on the bottom of the page, and on the blog index page the +tags are shown in the upper left corner of the post's excerpt. + +In addition to adding some color to the output and making things look more +interesting, the tags can also be used as filters on the list of overall blog +posts. For example, [/Blog?tag=website][blog-tag-filtered] filters for blog +posts that are tagged as being related to my website. The tags shown on the +bottom of each post and in the excerpts are links that apply the applicable +filters. You can hover over the tags to see descriptions of each. + +This is my 31st blog post on this site, so having some method of organization +was starting to feel needed. But, I did not want to go overboard - at the +moment, the filtering is just a binary check of if a post has a tag or not. At +some point I might add support for excluding posts with a given tag, or filtering +for posts that are tagged with one of a number of tags, or something else, but +a simple "Only show posts with tag ---" should be enough for now. + +The filtering is currently implement on the backend before the list of excerpts +is shown, but since I don't have any public examples of single single page +applications that I've written, I might switch to a dynamic version of filtering +at some point, to showcase some ~fancy~ JavaScript. We'll see. + +[blog-tag-filtered]: /Blog?tag=website diff --git a/src/Pages/BlogIndexPage.php b/src/Pages/BlogIndexPage.php index d46b68c..47989eb 100644 --- a/src/Pages/BlogIndexPage.php +++ b/src/Pages/BlogIndexPage.php @@ -123,7 +123,7 @@ protected function build(): void { [ 'class' => 'blog-preview-date' ], $post->date->format( 'l, d F Y' ) ), - $tags ? $tags : [], + $tags ? $tags : '', FluentHTML::make( 'p', [], diff --git a/src/Pages/LandingPage.php b/src/Pages/LandingPage.php index a0a983f..edee72a 100644 --- a/src/Pages/LandingPage.php +++ b/src/Pages/LandingPage.php @@ -7,6 +7,7 @@ use DanielEScherzer\HTMLBuilder\RawHTML; use DanielWebsite\Blog\BlogDisplay; use DanielWebsite\Blog\BlogPostStore; +use DanielWebsite\Blog\BlogTags; use DanielWebsite\SitemapEntry; use League\CommonMark\Extension\CommonMark\Node\Block\Heading; use League\CommonMark\Extension\CommonMark\Node\Inline\Link; @@ -132,7 +133,7 @@ protected function build(): void { // Use `League\CommonMark` library for parsing, since I write all of // the blog posts no need to escape unsecure stuff - $env = BlogDisplay::makeCommonMarkEnv( false ); + $env = BlogDisplay::makeCommonMarkEnv( $latestPost ); $parser = new MarkdownParser( $env ); $renderer = new HtmlRenderer( $env ); @@ -175,10 +176,20 @@ protected function build(): void { ) ); + $tags = $latestPost->getTags(); + $previewClasses = [ 'blog-preview' ]; + if ( $tags ) { + $tags = FluentHTML::make( + 'div', + [ 'class' => 'blog-tags' ], + BlogTags::getListForTags( $tags ), + ); + $previewClasses[] = 'blog-preview--has-tags'; + } $this->contentWrapper->append( FluentHTML::make( 'div', - [ 'class' => 'blog-preview' ], + [ 'class' => $previewClasses ], [ FluentHTML::make( 'h3', [], new RawHTML( $firstHeading ) ), FluentHTML::make( @@ -186,6 +197,7 @@ protected function build(): void { [ 'class' => 'blog-preview-date' ], $latestPost->date->format( 'l, d F Y' ) ), + $tags ? $tags : '', FluentHTML::make( 'p', [], From 6b11a12857b944c17d1e1c32e1fd2d0c90bf3f2d Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 3 Oct 2026 12:01:49 -0700 Subject: [PATCH 5/6] continue --- resources/blog-styles.css | 2 +- sitemap.xml | 3 +++ src/Blog/feed-rss.xml | 7 +++++- src/Blog/posts/20261005-blog-tags.md | 32 ++++++++++++++-------------- tests/data/Home.html | 6 +++--- tests/data/blog-index.html | 4 +++- tests/data/blog-tag-filter.html | 4 +++- tests/data/blog-tag-unknown.html | 4 +++- 8 files changed, 38 insertions(+), 24 deletions(-) diff --git a/resources/blog-styles.css b/resources/blog-styles.css index c0e61a5..9cacadd 100644 --- a/resources/blog-styles.css +++ b/resources/blog-styles.css @@ -182,7 +182,7 @@ &.blog-tags--pill-php, &[class*=" blog-tags--pill-php-"] { - background-color: #4f5b93; + background-color: #474A8A; a { color: #fff; } diff --git a/sitemap.xml b/sitemap.xml index 1919e00..f0137b8 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -111,4 +111,7 @@ https://scherzer.dev/Blog/20260923-no-php86-rc-1 + + https://scherzer.dev/Blog/20261005-blog-tags + diff --git a/src/Blog/feed-rss.xml b/src/Blog/feed-rss.xml index acba096..4e42dc9 100644 --- a/src/Blog/feed-rss.xml +++ b/src/Blog/feed-rss.xml @@ -3,9 +3,14 @@ Daniel Scherzer's Blog Entries from Daniel Scherzer' personal blog - Wed, 23 Sep 2026 00:00:00 +0000 + Mon, 05 Oct 2026 00:00:00 +0000 https://github.com/DanielEScherzer/website-content https://scherzer.dev/Blog + + Blog Tags + Mon, 05 Oct 2026 00:00:00 +0000 + https://scherzer.dev/Blog/20261005-blog-tags + No PHP 8.6.0RC1 Wed, 23 Sep 2026 00:00:00 +0000 diff --git a/src/Blog/posts/20261005-blog-tags.md b/src/Blog/posts/20261005-blog-tags.md index 7a8b3c6..ecd6dd1 100644 --- a/src/Blog/posts/20261005-blog-tags.md +++ b/src/Blog/posts/20261005-blog-tags.md @@ -8,26 +8,26 @@ tags: ["website"] # Blog Tags Blog posts on my website now have tags to help filter them! You can see the -tags for each post on the bottom of the page, and on the blog index page the -tags are shown in the upper left corner of the post's excerpt. +tags for each post at the bottom of the page. On the blog index page, each +post's tags are shown in the upper right corner of the post's excerpt. In addition to adding some color to the output and making things look more -interesting, the tags can also be used as filters on the list of overall blog -posts. For example, [/Blog?tag=website][blog-tag-filtered] filters for blog -posts that are tagged as being related to my website. The tags shown on the -bottom of each post and in the excerpts are links that apply the applicable -filters. You can hover over the tags to see descriptions of each. +interesting, the tags can also be used to filter the list of blog posts. For +example, [/Blog?tag=website][blog-tag-filtered] filters for blog posts that are +tagged as being related to my website. The tags shown at the bottom of each post +and in the excerpts are links that apply the corresponding filters. You can +hover over the tags to see descriptions of each. This is my 31st blog post on this site, so having some method of organization -was starting to feel needed. But, I did not want to go overboard - at the -moment, the filtering is just a binary check of if a post has a tag or not. At -some point I might add support for excluding posts with a given tag, or filtering -for posts that are tagged with one of a number of tags, or something else, but -a simple "Only show posts with tag ---" should be enough for now. +was starting to feel necessary. But I did not want to go overboard - at the +moment, the filtering is just a simple check of whether a post has a tag. At +some point I might add support for excluding posts with a given tag, or +filtering for posts that are tagged with one of a number of tags, or something +else, but a simple "Only show posts with tag ---" should be enough for now. -The filtering is currently implement on the backend before the list of excerpts -is shown, but since I don't have any public examples of single single page -applications that I've written, I might switch to a dynamic version of filtering -at some point, to showcase some ~fancy~ JavaScript. We'll see. +The filtering is currently implemented on the backend before the list of +excerpts is shown, but since I don't have any public examples of a single-page +application that I've written, I might switch to a dynamic version of the +filtering at some point, to showcase some ~fancy~ JavaScript. We'll see. [blog-tag-filtered]: /Blog?tag=website diff --git a/tests/data/Home.html b/tests/data/Home.html index 77d6ee1..251d49c 100644 --- a/tests/data/Home.html +++ b/tests/data/Home.html @@ -7,6 +7,6 @@ a release manager for PHP 8.5 (where I am one of two "rookie" release managers) and for PHP 8.6 (where I am the "veteran" release manager). I also contribute to PHP as a developer, and as the maintainer of the built-in Reflection extension.

See the links in the navigation bar above for more information about my -experience.

Contact

  • LinkedIn
  • Email: daniel.e.scherzer@gmail.com

Blog

I also have a blog. You can see a full index of my posts here. My latest blog post is:

No PHP 8.6.0RC1

Wednesday, 23 September 2026

Tomorrow, the PHP 8.6 release manager team is planning to announce the first -release candidate (RC) of PHP 8.6, PHP 8.6.0RC2. So, what happened to -PHP 8.6.0RC1? Continue reading...

\ No newline at end of file +experience.

Contact

  • LinkedIn
  • Email: daniel.e.scherzer@gmail.com

Blog

I also have a blog. You can see a full index of my posts here. My latest blog post is:

Blog Tags

Monday, 05 October 2026

Blog posts on my website now have tags to help filter them! You can see the +tags for each post at the bottom of the page. On the blog index page, each +post's tags are shown in the upper right corner of the post's excerpt. Continue reading...

\ No newline at end of file diff --git a/tests/data/blog-index.html b/tests/data/blog-index.html index c9150b1..7ea931e 100644 --- a/tests/data/blog-index.html +++ b/tests/data/blog-index.html @@ -1,5 +1,7 @@ -Blog index

Blog index

No PHP 8.6.0RC1

Wednesday, 23 September 2026

Tomorrow, the PHP 8.6 release manager team is planning to announce the first +Blog index

Blog index

Blog Tags

Monday, 05 October 2026

Blog posts on my website now have tags to help filter them! You can see the +tags for each post at the bottom of the page. On the blog index page, each +post's tags are shown in the upper right corner of the post's excerpt. Continue reading...

No PHP 8.6.0RC1

Wednesday, 23 September 2026

Tomorrow, the PHP 8.6 release manager team is planning to announce the first release candidate (RC) of PHP 8.6, PHP 8.6.0RC2. So, what happened to PHP 8.6.0RC1? Continue reading...

Joining the PHP Ecosystem Security Team

Wednesday, 09 September 2026

I'm excited to share that I am joining the PHP Foundation's Ecosystem Security Team to help harden the PHP codebase. Continue reading...

FOSSY 2026

Monday, 10 August 2026

Last week, I was in Vancouver for the FOSSY 2026 conference, where I gave a talk diff --git a/tests/data/blog-tag-filter.html b/tests/data/blog-tag-filter.html index dd2f882..9a7fcba 100644 --- a/tests/data/blog-tag-filter.html +++ b/tests/data/blog-tag-filter.html @@ -1,5 +1,7 @@ -Blog index

Blog posts about my website

(clear filter)

Blog RSS Feed

Sunday, 17 May 2026

Last month, I received a request to add an RSS feed to my blog. I am +Blog index

Blog posts about my website

(clear filter)

Blog Tags

Monday, 05 October 2026

Blog posts on my website now have tags to help filter them! You can see the +tags for each post at the bottom of the page. On the blog index page, each +post's tags are shown in the upper right corner of the post's excerpt. Continue reading...

Blog RSS Feed

Sunday, 17 May 2026

Last month, I received a request to add an RSS feed to my blog. I am happy to share that I have added such a feed, available at /Blog/feed/rss.xml. Continue reading...

Unplanned Downtime, November 2025

Tuesday, 25 November 2025

Last week, I had some unexpected downtime for my website. The original cause was a power issue at the physical server, and out of my control, but an oversight of diff --git a/tests/data/blog-tag-unknown.html b/tests/data/blog-tag-unknown.html index a092fc9..b6f8efe 100644 --- a/tests/data/blog-tag-unknown.html +++ b/tests/data/blog-tag-unknown.html @@ -1,5 +1,7 @@ -Blog index

The requested blog tag 'missing' is not recognized; no filtering is applied

Blog index

No PHP 8.6.0RC1

Wednesday, 23 September 2026

Tomorrow, the PHP 8.6 release manager team is planning to announce the first +Blog index

The requested blog tag 'missing' is not recognized; no filtering is applied

Blog index

Blog Tags

Monday, 05 October 2026

Blog posts on my website now have tags to help filter them! You can see the +tags for each post at the bottom of the page. On the blog index page, each +post's tags are shown in the upper right corner of the post's excerpt. Continue reading...

No PHP 8.6.0RC1

Wednesday, 23 September 2026

Tomorrow, the PHP 8.6 release manager team is planning to announce the first release candidate (RC) of PHP 8.6, PHP 8.6.0RC2. So, what happened to PHP 8.6.0RC1? Continue reading...

Joining the PHP Ecosystem Security Team

Wednesday, 09 September 2026

I'm excited to share that I am joining the PHP Foundation's Ecosystem Security Team to help harden the PHP codebase. Continue reading...

FOSSY 2026

Monday, 10 August 2026

Last week, I was in Vancouver for the FOSSY 2026 conference, where I gave a talk From 4a165696fecbfb407f88d92762ff4a14e26e2d52 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 3 Oct 2026 12:04:10 -0700 Subject: [PATCH 6/6] Update commonmark --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 547d0df..489ed27 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "danielescherzer/commonmark-ext-pygments-highlighter": "0.2.0", "danielescherzer/html-builder": "0.0.2", "nikic/fast-route": "1.3.0", - "league/commonmark": "2.10.1", + "league/commonmark": "2.10.3", "ramsey/pygments": "3.0.0", "symfony/yaml": "7.4.12" },