{"id":70,"date":"2026-10-19T17:00:00","date_gmt":"2026-10-19T15:00:00","guid":{"rendered":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/"},"modified":"2026-10-16T14:00:12","modified_gmt":"2026-10-16T12:00:12","slug":"php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension","status":"publish","type":"post","link":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/","title":{"rendered":"PHP 8.1 to 8.3 features I now use in every TYPO3 extension"},"content":{"rendered":"<p>TYPO3 13 and 14 both require PHP 8.2 or newer. That means a lot of PHP features that were &#8220;nice, but not for extensions that must run on old servers&#8221; are now simply available. I came to TYPO3 when <code>pibase<\/code> and arrays-of-arrays were normal, so some of these still feel like a small luxury.<\/p>\n<p>Here is the short list I reach for in every new extension, each with a TYPO3-flavoured example and the PHP version it needs. Pay attention to the last two: they need PHP 8.3, which is more than the TYPO3 minimum.<\/p>\n<h2>Constructor promotion with dependency injection (PHP 8.0)<\/h2>\n<p>Services in TYPO3 are autowired through <code>Services.yaml<\/code>. With constructor promotion, a service with dependencies needs no property declarations at all:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace MyVendorMyEventsService;\n\nuse TYPO3CMSCoreContextContext;\nuse TYPO3CMSCoreDatabaseConnection;\nuse TYPO3CMSCoreDatabaseConnectionPool;\n\nfinal class EventCounter\n{\n    public function __construct(\n        private readonly ConnectionPool $connectionPool,\n        private readonly Context $context,\n    ) {}\n\n    public function countUpcoming(): int\n    {\n        $now = $this-&gt;context-&gt;getPropertyFromAspect('date', 'timestamp');\n        $queryBuilder = $this-&gt;connectionPool-&gt;getQueryBuilderForTable('tx_myevents_domain_model_event');\n\n        return (int)$queryBuilder\n            -&gt;count('uid')\n            -&gt;from('tx_myevents_domain_model_event')\n            -&gt;where($queryBuilder-&gt;expr()-&gt;gte('start_date', $queryBuilder-&gt;createNamedParameter($now, Connection::PARAM_INT)))\n            -&gt;executeQuery()\n            -&gt;fetchOne();\n    }\n}<\/code><\/pre>\n<p><code>private readonly<\/code> (readonly properties came with PHP 8.1) says what it means: this dependency is set once and never replaced. No more <code>inject*()<\/code> methods for new code.<\/p>\n<h2>Enums for select values (PHP 8.1)<\/h2>\n<p>Every extension has a field with a fixed set of values: a status, a type, a layout. Before, these were integer constants scattered around. Now they are a backed enum:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace MyVendorMyEventsDomainModel;\n\nenum EventStatus: string\n{\n    case Planned = 'planned';\n    case Confirmed = 'confirmed';\n    case Cancelled = 'cancelled';\n\n    public function label(): string\n    {\n        return 'LLL:EXT:my_events\/Resources\/Private\/Language\/locallang_db.xlf:status.' . $this-&gt;value;\n    }\n}<\/code><\/pre>\n<p>The TCA can build its select items from the enum, so the list of allowed values lives in one place:<\/p>\n<pre><code class=\"language-php\">'status' =&gt; [\n    'label' =&gt; 'LLL:EXT:my_events\/Resources\/Private\/Language\/locallang_db.xlf:status',\n    'config' =&gt; [\n        'type' =&gt; 'select',\n        'renderType' =&gt; 'selectSingle',\n        'items' =&gt; array_map(\n            static fn(EventStatus $status): array =&gt; ['label' =&gt; $status-&gt;label(), 'value' =&gt; $status-&gt;value],\n            EventStatus::cases()\n        ),\n        'default' =&gt; EventStatus::Planned-&gt;value,\n    ],\n],<\/code><\/pre>\n<p>And since TYPO3 13.0, Extbase maps backed enums in domain models directly (Feature #101396). The property is simply typed with the enum:<\/p>\n<pre><code class=\"language-php\">protected EventStatus $status = EventStatus::Planned;<\/code><\/pre>\n<p>The old <code>TYPO3CMSCoreTypeEnumeration<\/code> class is deprecated since 13.0, so new code should not extend it.<\/p>\n<h2>First-class callable syntax (PHP 8.1)<\/h2>\n<p>Small, but I use it every day. Instead of <code>[$this, 'mapRow']<\/code> or a string with a function name, you write the call with <code>(...)<\/code>:<\/p>\n<pre><code class=\"language-php\">$rows = $queryBuilder-&gt;select('uid', 'title', 'start_date')\n    -&gt;from('tx_myevents_domain_model_event')\n    -&gt;executeQuery()\n    -&gt;fetchAllAssociative();\n\nreturn array_map($this-&gt;mapRow(...), $rows);<\/code><\/pre>\n<p>Your IDE and PHPStan understand it, and renaming the method updates the call. A string callback is invisible to both. A bare name without quotes is even worse: PHP 8 treats it as an undefined constant and stops with a fatal error. I met exactly that in an old WordPress theme on my own blog.<\/p>\n<h2>Readonly classes for events and DTOs (PHP 8.2)<\/h2>\n<p>PSR-14 events that only carry data, or small value objects, become one short class. Since PHP 8.2 the whole class can be <code>readonly<\/code>:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace MyVendorMyEventsEvent;\n\nuse MyVendorMyEventsDomainModelEventStatus;\n\nfinal readonly class EventStatusChangedEvent\n{\n    public function __construct(\n        public int $eventUid,\n        public EventStatus $oldStatus,\n        public EventStatus $newStatus,\n    ) {}\n}<\/code><\/pre>\n<p>A listener with the <code>#[AsEventListener]<\/code> attribute (TYPO3 13.0 and newer) needs no <code>Services.yaml<\/code> entry:<\/p>\n<pre><code class=\"language-php\">use TYPO3CMSCoreAttributeAsEventListener;\n\n#[AsEventListener(identifier: 'my-events\/notify-on-cancel')]\nfinal readonly class NotifyOnCancel\n{\n    public function __construct(private MailNotifier $notifier) {}\n\n    public function __invoke(EventStatusChangedEvent $event): void\n    {\n        if ($event-&gt;newStatus === EventStatus::Cancelled) {\n            $this-&gt;notifier-&gt;sendCancellation($event-&gt;eventUid);\n        }\n    }\n}<\/code><\/pre>\n<p>Careful: an event that listeners are meant to <em>modify<\/em> must not be readonly. Use readonly only for events that inform. The TYPO3 core itself uses <code>final readonly class<\/code> for several of its newer events, so you are in good company.<\/p>\n<h2>Typed class constants (PHP 8.3)<\/h2>\n<p>Table names and other fixed strings used to be untyped constants. With PHP 8.3 you can give them a type, and a child class cannot change it to something else:<\/p>\n<pre><code class=\"language-php\">final class EventTable\n{\n    public const string NAME = 'tx_myevents_domain_model_event';\n    public const int MAX_TEASER_ITEMS = 3;\n}<\/code><\/pre>\n<h2>The #[Override] attribute (PHP 8.3)<\/h2>\n<p>When you extend a core class, for example a ViewHelper or a form element, mark the methods that should override a parent method:<\/p>\n<pre><code class=\"language-php\">final class PriceViewHelper extends AbstractViewHelper\n{\n    #[Override]\n    public function initializeArguments(): void\n    {\n        $this-&gt;registerArgument('amount', 'int', 'Amount in cent', true);\n    }\n\n    public function render(): string\n    {\n        return number_format($this-&gt;arguments['amount'] \/ 100, 2, ',', '.') . ' \u20ac';\n    }\n}<\/code><\/pre>\n<p>If a future TYPO3 version renames or removes the parent method, PHP stops with an error at compile time instead of your method silently never being called. During major upgrades that is exactly the early warning you want.<\/p>\n<h2>The catch with PHP 8.3 features<\/h2>\n<p>TYPO3 13 and 14 require PHP 8.2, not 8.3. On a PHP 8.2 server, a typed constant is a parse error and the whole file breaks. <code>#[Override]<\/code> is less dramatic: PHP 8.2 does not know the attribute and ignores it, so you simply lose the check. So:<\/p>\n<ul>\n<li>For a project, check the PHP version of production and set <code>config.platform.php<\/code> in <code>composer.json<\/code>. On hoja.in it is pinned to 8.4, so everything here is fine.<\/li>\n<li>For a public extension, require <code>\"php\": \"^8.3\"<\/code> in its <code>composer.json<\/code> if you use them, or stay with the 8.2 features.<\/li>\n<\/ul>\n<h2>How I introduce them in old extensions<\/h2>\n<p>I do not rewrite a working extension just to use new syntax. During an upgrade, I touch the files anyway, so that is the moment. Constructor promotion and <code>readonly<\/code> come first, because Rector can do most of it and the diff is easy to review. Enums come when I have to touch the TCA of a field anyway, because then I can also check that the values in the database really match the cases. For a non-nullable enum property, Extbase maps the value with <code>from()<\/code>, so a database value that is not a case of the enum throws an error when the record is loaded, so run a quick <code>SELECT DISTINCT<\/code> on the column before you switch.<\/p>\n<p>None of these features is spectacular alone. Together, they make extension code shorter, more honest about types, and easier for tools like PHPStan to check. If you are still writing <code>inject*()<\/code> methods and integer constants, try one of these in your next small extension. Feedbacks are the champion&#8217;s breakfast, so tell me which one you use most.<\/p>\n<h2>Further reading<\/h2>\n<ul>\n<li><a href=\"https:\/\/www.php.net\/manual\/en\/language.enumerations.backed.php\">PHP manual: Backed enumerations<\/a><\/li>\n<li><a href=\"https:\/\/www.php.net\/manual\/en\/language.oop5.basic.php#language.oop5.basic.class.readonly\">PHP manual: Readonly classes<\/a><\/li>\n<li><a href=\"https:\/\/www.php.net\/manual\/en\/class.override.php\">PHP manual: The Override attribute<\/a><\/li>\n<li><a href=\"https:\/\/docs.typo3.org\/c\/typo3\/cms-core\/main\/en-us\/Changelog\/13.0\/Feature-101396-LetExtbaseHandleNativeEnums.html\">Feature #101396: Let Extbase handle native enums<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Enums for TCA select values, readonly classes for events, constructor promotion with DI, first-class callables, typed constants and #[Override] in TYPO3 extensions.<\/p>\n","protected":false},"author":1,"featured_media":71,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19],"tags":[30,32,5],"class_list":["post-70","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-php","tag-extension-development","tag-php-8","tag-typo3"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>PHP 8.1\u20138.3 features for TYPO3 extensions<\/title>\n<meta name=\"description\" content=\"Enums, readonly classes, constructor promotion, first-class callables, typed constants and #[Override], each with a TYPO3 example and PHP version.\" \/>\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\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"PHP 8.1\u20138.3 features for TYPO3 extensions\" \/>\n<meta property=\"og:description\" content=\"Enums, readonly classes, constructor promotion, first-class callables, typed constants and #[Override], each with a TYPO3 example and PHP version.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/\" \/>\n<meta property=\"og:site_name\" content=\"Hoja Mustaffa Abdul Latheef \u2013 Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-19T15:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension.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=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/\"},\"author\":{\"name\":\"Hoja Mustaffa Abdul Latheef\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"headline\":\"PHP 8.1 to 8.3 features I now use in every TYPO3 extension\",\"datePublished\":\"2026-10-19T15:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/\"},\"wordCount\":772,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"image\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension.jpg\",\"keywords\":[\"Extension development\",\"PHP 8\",\"TYPO3\"],\"articleSection\":[\"PHP\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/\",\"name\":\"PHP 8.1\u20138.3 features for TYPO3 extensions\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension.jpg\",\"datePublished\":\"2026-10-19T15:00:00+00:00\",\"description\":\"Enums, readonly classes, constructor promotion, first-class callables, typed constants and #[Override], each with a TYPO3 example and PHP version.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/#primaryimage\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension.jpg\",\"contentUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension.jpg\",\"width\":1200,\"height\":630,\"caption\":\"Modern PHP in TYPO3\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/hoja.in\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PHP 8.1 to 8.3 features I now use in every TYPO3 extension\"}]},{\"@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":"PHP 8.1\u20138.3 features for TYPO3 extensions","description":"Enums, readonly classes, constructor promotion, first-class callables, typed constants and #[Override], each with a TYPO3 example and PHP version.","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\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/","og_locale":"en_US","og_type":"article","og_title":"PHP 8.1\u20138.3 features for TYPO3 extensions","og_description":"Enums, readonly classes, constructor promotion, first-class callables, typed constants and #[Override], each with a TYPO3 example and PHP version.","og_url":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/","og_site_name":"Hoja Mustaffa Abdul Latheef \u2013 Blog","article_published_time":"2026-10-19T15:00:00+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension.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":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/#article","isPartOf":{"@id":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/"},"author":{"name":"Hoja Mustaffa Abdul Latheef","@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"headline":"PHP 8.1 to 8.3 features I now use in every TYPO3 extension","datePublished":"2026-10-19T15:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/"},"wordCount":772,"commentCount":0,"publisher":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"image":{"@id":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/#primaryimage"},"thumbnailUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension.jpg","keywords":["Extension development","PHP 8","TYPO3"],"articleSection":["PHP"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/","url":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/","name":"PHP 8.1\u20138.3 features for TYPO3 extensions","isPartOf":{"@id":"https:\/\/hoja.in\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/#primaryimage"},"image":{"@id":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/#primaryimage"},"thumbnailUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension.jpg","datePublished":"2026-10-19T15:00:00+00:00","description":"Enums, readonly classes, constructor promotion, first-class callables, typed constants and #[Override], each with a TYPO3 example and PHP version.","breadcrumb":{"@id":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/#primaryimage","url":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension.jpg","contentUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension.jpg","width":1200,"height":630,"caption":"Modern PHP in TYPO3"},{"@type":"BreadcrumbList","@id":"https:\/\/hoja.in\/blog\/php-8-1-to-8-3-features-i-now-use-in-every-typo3-extension\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/hoja.in\/blog\/"},{"@type":"ListItem","position":2,"name":"PHP 8.1 to 8.3 features I now use in every TYPO3 extension"}]},{"@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\/70","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=70"}],"version-history":[{"count":1,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/70\/revisions"}],"predecessor-version":[{"id":158,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/70\/revisions\/158"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/media\/71"}],"wp:attachment":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/media?parent=70"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/categories?post=70"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/tags?post=70"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}