{"id":112,"date":"2026-11-02T16:00:00","date_gmt":"2026-11-02T15:00:00","guid":{"rendered":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/"},"modified":"2026-10-16T14:00:13","modified_gmt":"2026-10-16T12:00:13","slug":"finding-php-8-fatal-errors-in-legacy-code-before-production","status":"publish","type":"post","link":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/","title":{"rendered":"Finding PHP 8 fatal errors in legacy code before production"},"content":{"rendered":"<p>The short version of the story: the child theme of my old blog passed a comment callback as <code>'callback' =&gt; mytheme_comment<\/code>, without quotes. On PHP 7 that was a warning and the string <code>\"mytheme_comment\"<\/code>. On PHP 8 it is a fatal <code>Error<\/code>, and every blog post with comments died. I told the full story in an earlier post. This one is about the question that came after: which tool would have told me <em>before<\/em> the deploy?<\/p>\n<p>I did not want to answer from memory, so I ran the tools against a minimal example and against the real old <code>comments.php<\/code> from git. Versions: PHP 8.4 CLI, PHPStan 2.3.1, PHP_CodeSniffer 3.13 with PHPCompatibility 9.3.5 and 10.0.0-alpha2.<\/p>\n<h2>The bug in ten lines<\/h2>\n<pre><code class=\"language-php\">&lt;?php\nfunction mytheme_comment($comment, $args, $depth) { echo 'x'; }\n\nfunction render_list(): void {\n    $options = array(\n        'style'    =&gt; 'ol',\n        'callback' =&gt; mytheme_comment,\n    );\n    var_dump($options['callback']);\n}\nrender_list();<\/code><\/pre>\n<p>PHP 7.4 prints <code>Warning: Use of undefined constant mytheme_comment - assumed 'mytheme_comment' (this will throw an Error in a future version of PHP)<\/code> and carries on. PHP 8.4 prints <code>Fatal error: Uncaught Error: Undefined constant \"mytheme_comment\"<\/code>. The future arrived.<\/p>\n<h2>php -l: only syntax, and only for the PHP you run it with<\/h2>\n<pre><code class=\"language-text\">$ php -l src\/comments.php\nNo syntax errors detected in src\/comments.php<\/code><\/pre>\n<p>That is correct: it is valid syntax. A bare word is a constant lookup, and whether that constant exists is only known at runtime. <code>php -l<\/code> on the real old <code>comments.php<\/code> said the same. So the linter is not useless, it just answers a different question.<\/p>\n<p>Where <code>php -l<\/code> does help: syntax that PHP 8 removed. The old string offset with curly braces is a parse error now:<\/p>\n<pre><code class=\"language-text\">$ php -l syntax\/curly.php     # contains: echo $s{0};\nParse error: syntax error, unexpected token \"{\", expecting \",\" or \";\" in syntax\/curly.php on line 3<\/code><\/pre>\n<p>The important detail: run the linter with the <em>target<\/em> PHP version. <code>php -l<\/code> on your old PHP 7.x server will happily accept that file. In DDEV that is easy, set the target version and lint everything once:<\/p>\n<pre><code class=\"language-bash\">ddev exec 'find public\/blog\/wp-content\/themes -name \"*.php\" -print0 | xargs -0 -n1 php -l | grep -v \"^No syntax errors\"'<\/code><\/pre>\n<h2>PHPStan: it finds it, but not at level 0<\/h2>\n<p>PHPStan analyses the code without running it, so it knows which functions and constants exist. On the ten-line example:<\/p>\n<pre><code class=\"language-text\">--- level 0\nsrc\/more.php:5:Deprecated in PHP 8.0: Required parameter $b follows optional parameter $a.\nsrc\/more.php:5:Function each not found.\nsrc\/more.php:6:Function create_function not found.\n--- level 1\nsrc\/comments.php:7:Constant mytheme_comment not found.\n...<\/code><\/pre>\n<p>Look closely: in my run, level 0 did not report the undefined constant, level 1 did. If you introduce PHPStan to a legacy codebase at level 0 to keep the noise down, this class of bug still slips through. Start at level 1 at least.<\/p>\n<p>On the real WordPress theme, the first run is noise: every WordPress function is &#8220;not found&#8221;, because PHPStan does not know WordPress. The fix is the <code>szepeviktor\/phpstan-wordpress<\/code> extension, which brings the WordPress stubs. With it, the old <code>comments.php<\/code> at level 1 gives five lines, and every one of them is worth reading:<\/p>\n<pre><code class=\"language-text\">theme\/comments.php:29:Function twentyfifteen_comment_nav not found.\ntheme\/comments.php:45:Constant mytheme_comment not found.\ntheme\/comments.php:93:Array has 2 duplicate keys with value 'label_submit' ('label_submit', 'label_submit').\ntheme\/comments.php:110:Variable $user_identity might not be defined.\ntheme\/comments.php:133:Array has 2 duplicate keys with value 'title_reply' ('title_reply', 'title_reply').<\/code><\/pre>\n<p>Line 45 is our fatal error. Line 29 is a function from the parent theme (Twenty Fifteen); tell PHPStan about the parent theme folder and it disappears. The duplicate keys and the possibly undefined variable are real sloppiness from 2015, the same family as the undefined <code>$aria_req<\/code> I fixed in that file during the move. The configuration for that run is tiny:<\/p>\n<pre><code class=\"language-yaml\"># phpstan.neon\nparameters:\n  level: 1\n  paths:\n    - public\/blog\/wp-content\/themes\/myblog<\/code><\/pre>\n<p>With <code>phpstan\/extension-installer<\/code> the WordPress extension registers itself, no include needed. Higher levels find more PHP 8.1 problems: at level 5, <code>strlen(null)<\/code> is reported as &#8220;Parameter #1 $string of function strlen expects string, null given&#8221;, which is exactly the PHP 8.1 deprecation for passing null to internal functions.<\/p>\n<h2>PHPCompatibility: good for removed functions, blind to this bug<\/h2>\n<p>PHPCompatibility is a PHP_CodeSniffer standard that knows which functions and syntax changed between PHP versions. It does not know your code&#8217;s constants, so it did not report <code>mytheme_comment<\/code> in either version, and on the real <code>comments.php<\/code> it reported nothing.<\/p>\n<p>It also matters which version you install. The latest stable release, 9.3.5, is from before PHP 8 and told me only half the truth:<\/p>\n<pre><code class=\"language-text\"># 9.3.5, testVersion 8.0-\nmore.php:5:38: warning - Function each() is deprecated since PHP 7.2; Use a foreach loop instead\n\n# 10.0.0-alpha2, testVersion 8.0-\nmore.php:5:17: warning - Declaring an optional parameter before a required parameter is deprecated since PHP 8.0. ...\nmore.php:5:38: error - Function each() is deprecated since PHP 7.2 and removed since PHP 8.0; Use a foreach loop or ArrayIterator instead<\/code><\/pre>\n<p>&#8220;Deprecated&#8221; versus &#8220;removed&#8221; is the difference between a log line and a white page. If you use PHPCompatibility for PHP 8 work, use the 10.x line (still alpha when I wrote this) and run it with <code>--runtime-set testVersion 8.0-<\/code>.<\/p>\n<h2>What I run now<\/h2>\n<ol>\n<li><code>php -l<\/code> with the target PHP version over all files. Cheap, catches removed syntax.<\/li>\n<li>PHPStan at level 1 or higher, with stubs for the framework (WordPress stubs, or the TYPO3 core in <code>vendor\/<\/code> for extensions). Catches unknown constants, functions and classes.<\/li>\n<li>PHPCompatibility 10.x for removed and deprecated functions and syntax across versions.<\/li>\n<li>And then still: open every template type once on the target PHP version, with error logging on. On hoja.in the post pages with comments were the ones that died; a smoke test that only loaded the blog home would have passed.<\/li>\n<\/ol>\n<h2>The PHP 8 changes that bite legacy themes and extensions most<\/h2>\n<ul>\n<li>PHP 8.0: undefined constants throw <code>Error<\/code> (our bug); <code>each()<\/code>, <code>create_function()<\/code> and PHP 4 style constructors are gone; <code>$str{0}<\/code> is a parse error; a required parameter after an optional one is deprecated; <code>0 == \"abc\"<\/code> is now false.<\/li>\n<li>PHP 8.1: passing <code>null<\/code> to non-nullable parameters of internal functions (<code>strlen<\/code>, <code>trim<\/code>, <code>htmlspecialchars<\/code>) is deprecated. Old WordPress themes do this everywhere.<\/li>\n<li>PHP 8.2: dynamic properties are deprecated, and so is <code>\"${var}\"<\/code> string interpolation.<\/li>\n<li>PHP 8.4: implicitly nullable parameter types (<code>Foo $x = null<\/code>) are deprecated.<\/li>\n<\/ul>\n<p>Deprecations do not break the page today, but each one is a fatal error in a future major version. Fix them while the tools point at them.<\/p>\n<p>If this post helped you, I will be really happy. Cheers!<\/p>\n<h2>Further reading<\/h2>\n<ul>\n<li><a href=\"https:\/\/www.php.net\/manual\/en\/migration80.incompatible.php\">PHP 8.0: backward incompatible changes<\/a><\/li>\n<li><a href=\"https:\/\/phpstan.org\/user-guide\/rule-levels\">PHPStan rule levels<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/PHPCompatibility\/PHPCompatibility\">PHPCompatibility on GitHub<\/a><\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/wp_list_comments\/\">WordPress: wp_list_comments()<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>php -l said the old blog theme was fine. PHP 8 disagreed. What php -l, PHPStan and PHPCompatibility really report for legacy themes and extensions, measured on the real file.<\/p>\n","protected":false},"author":1,"featured_media":113,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19],"tags":[32,31,25,42],"class_list":["post-112","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-php","tag-php-8","tag-testing","tag-upgrades","tag-wordpress"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Finding PHP 8 fatal errors in legacy code<\/title>\n<meta name=\"description\" content=\"Which tool catches PHP 8 fatal errors in old themes and extensions? php -l, PHPStan and PHPCompatibility tested on a real undefined constant bug.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Finding PHP 8 fatal errors in legacy code\" \/>\n<meta property=\"og:description\" content=\"Which tool catches PHP 8 fatal errors in old themes and extensions? php -l, PHPStan and PHPCompatibility tested on a real undefined constant bug.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/\" \/>\n<meta property=\"og:site_name\" content=\"Hoja Mustaffa Abdul Latheef \u2013 Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-11-02T15:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/finding-php-8-fatal-errors-in-legacy-code-before-production.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Hoja Mustaffa Abdul Latheef\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@hojaonline\" \/>\n<meta name=\"twitter:site\" content=\"@hojaonline\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Hoja Mustaffa Abdul Latheef\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/\"},\"author\":{\"name\":\"Hoja Mustaffa Abdul Latheef\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"headline\":\"Finding PHP 8 fatal errors in legacy code before production\",\"datePublished\":\"2026-11-02T15:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/\"},\"wordCount\":777,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"image\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/finding-php-8-fatal-errors-in-legacy-code-before-production.jpg\",\"keywords\":[\"PHP 8\",\"Testing\",\"Upgrades\",\"WordPress\"],\"articleSection\":[\"PHP\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/\",\"name\":\"Finding PHP 8 fatal errors in legacy code\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/finding-php-8-fatal-errors-in-legacy-code-before-production.jpg\",\"datePublished\":\"2026-11-02T15:00:00+00:00\",\"description\":\"Which tool catches PHP 8 fatal errors in old themes and extensions? php -l, PHPStan and PHPCompatibility tested on a real undefined constant bug.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/#primaryimage\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/finding-php-8-fatal-errors-in-legacy-code-before-production.jpg\",\"contentUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/finding-php-8-fatal-errors-in-legacy-code-before-production.jpg\",\"width\":1200,\"height\":630,\"caption\":\"Catch it before PHP 8 does\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/finding-php-8-fatal-errors-in-legacy-code-before-production\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/hoja.in\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Finding PHP 8 fatal errors in legacy code before production\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/\",\"name\":\"Hoja Mustaffa Abdul Latheef \u2013 Blog\",\"description\":\"Notes from daily work with TYPO3, PHP and AI-assisted engineering\",\"publisher\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/hoja.in\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\",\"name\":\"Hoja Mustaffa Abdul Latheef\",\"logo\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/image\\\/\"},\"description\":\"Senior TYPO3 Developer at jweiland.net, TYPO3 CMS Certified Developer. I write about TYPO3 upgrades, extension development, PHP and AI-assisted engineering with Claude Code.\",\"sameAs\":[\"https:\\\/\\\/hoja.in\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Finding PHP 8 fatal errors in legacy code","description":"Which tool catches PHP 8 fatal errors in old themes and extensions? php -l, PHPStan and PHPCompatibility tested on a real undefined constant bug.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/","og_locale":"en_US","og_type":"article","og_title":"Finding PHP 8 fatal errors in legacy code","og_description":"Which tool catches PHP 8 fatal errors in old themes and extensions? php -l, PHPStan and PHPCompatibility tested on a real undefined constant bug.","og_url":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/","og_site_name":"Hoja Mustaffa Abdul Latheef \u2013 Blog","article_published_time":"2026-11-02T15:00:00+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/finding-php-8-fatal-errors-in-legacy-code-before-production.jpg","type":"image\/jpeg"}],"author":"Hoja Mustaffa Abdul Latheef","twitter_card":"summary_large_image","twitter_creator":"@hojaonline","twitter_site":"@hojaonline","twitter_misc":{"Written by":"Hoja Mustaffa Abdul Latheef","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/#article","isPartOf":{"@id":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/"},"author":{"name":"Hoja Mustaffa Abdul Latheef","@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"headline":"Finding PHP 8 fatal errors in legacy code before production","datePublished":"2026-11-02T15:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/"},"wordCount":777,"commentCount":0,"publisher":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"image":{"@id":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/#primaryimage"},"thumbnailUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/finding-php-8-fatal-errors-in-legacy-code-before-production.jpg","keywords":["PHP 8","Testing","Upgrades","WordPress"],"articleSection":["PHP"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/","url":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/","name":"Finding PHP 8 fatal errors in legacy code","isPartOf":{"@id":"https:\/\/hoja.in\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/#primaryimage"},"image":{"@id":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/#primaryimage"},"thumbnailUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/finding-php-8-fatal-errors-in-legacy-code-before-production.jpg","datePublished":"2026-11-02T15:00:00+00:00","description":"Which tool catches PHP 8 fatal errors in old themes and extensions? php -l, PHPStan and PHPCompatibility tested on a real undefined constant bug.","breadcrumb":{"@id":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/#primaryimage","url":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/finding-php-8-fatal-errors-in-legacy-code-before-production.jpg","contentUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/finding-php-8-fatal-errors-in-legacy-code-before-production.jpg","width":1200,"height":630,"caption":"Catch it before PHP 8 does"},{"@type":"BreadcrumbList","@id":"https:\/\/hoja.in\/blog\/finding-php-8-fatal-errors-in-legacy-code-before-production\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/hoja.in\/blog\/"},{"@type":"ListItem","position":2,"name":"Finding PHP 8 fatal errors in legacy code before production"}]},{"@type":"WebSite","@id":"https:\/\/hoja.in\/blog\/#website","url":"https:\/\/hoja.in\/blog\/","name":"Hoja Mustaffa Abdul Latheef \u2013 Blog","description":"Notes from daily work with TYPO3, PHP and AI-assisted engineering","publisher":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/hoja.in\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b","name":"Hoja Mustaffa Abdul Latheef","logo":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/image\/"},"description":"Senior TYPO3 Developer at jweiland.net, TYPO3 CMS Certified Developer. I write about TYPO3 upgrades, extension development, PHP and AI-assisted engineering with Claude Code.","sameAs":["https:\/\/hoja.in\/"]}]}},"_links":{"self":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/112","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/comments?post=112"}],"version-history":[{"count":1,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/112\/revisions"}],"predecessor-version":[{"id":179,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/112\/revisions\/179"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/media\/113"}],"wp:attachment":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/media?parent=112"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/categories?post=112"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/tags?post=112"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}