whoami
Lucas Jenß
cat /etc/motd
The Coding Journal ツ — Notes taken on an epic coding journey. Technical solutions, debugging notes, and practical guides from the trenches of software development.
ls -la ~/languages/
- drwxr-xr-x
- ▶ PHP
- ▶ Ruby
- ▶ Scala
- ▶ C#
- ▶ JavaScript
- ▶ Objective-C
- ▶ Shell Scripting
ls -la ~/toolchain/
- drwxr-xr-x
- ▶ Typo3
- ▶ Akka
- ▶ Capistrano
- ▶ Git
- ▶ MAMP
- ▶ Adobe Illustrator
- ▶ NSTrackingArea (Cocoa)
uname -a
- drwxr-xr-x
- ▶ Mac OS X
- ▶ Unix
Patching a TYPO3 Extension With a Custom XClass in PHP 8.1
A TYPO3 extension can be perfectly serviceable until a vendor class needs one small behavioural change. Perhaps a record is filtered incorrectly, a backend label needs adjusting, or a method must handle a PHP 8.1 type more carefully. Editing the package directly may solve the immediate problem, but the change will disappear during the next Composer update.
A custom XClass provides a focused way to replace selected behaviour while leaving the original extension intact. The technique is especially useful on projects where a complete fork would create unnecessary maintenance work. It also leaves a clear record of the local modification inside the project’s own codebase.
This approach suits the practical, troubleshooting-oriented style documented in Mike Patt's site, particularly when a production installation needs a controlled workaround rather than a broad rewrite. The examples below assume Composer-based TYPO3 development, namespaces, and PHP 8.1.
When An XClass Is The Right Tool
An XClass is a TYPO3 extension mechanism that substitutes one PHP class for another at runtime. Your replacement normally extends the original class and overrides only the method that needs different behaviour. Existing callers continue to request the original class, while TYPO3 instantiates your replacement instead.
This is useful for a narrowly scoped change in a controller, repository, utility, or other replaceable class. It is less suitable when the original class is declared final, when the required method is private, or when the desired behaviour is better expressed through a PSR-14 event listener, a configuration option, or a service decoration.
The distinction matters for long-term maintenance. An XClass creates a strong dependency on the vendor class’s method signatures and internal behaviour. A minor TYPO3 or extension update can change a constructor, return type, property visibility, or call path. Treat the XClass as a deliberate compatibility layer, not as a general-purpose override system.
Creating The Replacement Class
Start by creating a class in your site package or custom extension. For example, if the original class is Vendor\Example\Domain\Repository\ItemRepository, create a replacement at:
Classes/Xclass/ItemRepository.php
The namespace must match the PSR-4 mapping in your extension’s composer.json. A simple PHP 8.1-compatible replacement might look like this:
<?php
declare(strict_types=1);
namespace MyVendor\SitePackage\Xclass;
use Vendor\Example\Domain\Repository\ItemRepository as OriginalItemRepository;
class ItemRepository extends OriginalItemRepository
{
public function findVisibleItems(): array
{
$items = parent::findVisibleItems();
return array_values(array_filter(
$items,
static fn (object $item): bool => $item->isVisibleInAustralia()
));
}
}
The exact method signature must match the parent method. PHP 8.1 enforces compatibility more strictly than older PHP versions, so a missing return type or an incompatible parameter type can cause a fatal error when the class is loaded. Check the installed vendor source rather than relying on an older online example.
Avoid adding state unless it is necessary. If the parent class has constructor dependencies, let TYPO3’s object creation process handle them where possible. If you define your own constructor, preserve the parent constructor’s required arguments and call parent::__construct(...). A mismatch here is one of the most common causes of a successful-looking registration that fails during the first request.
Registering The XClass In TYPO3
Register the substitution in your extension’s Configuration/ bootstrap file, commonly ext_localconf.php. The configuration uses the fully qualified original class as the key and your replacement class as the className:
<?php
declare(strict_types=1);
defined('TYPO3') or die();
use MyVendor\SitePackage\Xclass\ItemRepository;
use Vendor\Example\Domain\Repository\ItemRepository as OriginalItemRepository;
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][OriginalItemRepository::class] = [
'className' => ItemRepository::class,
];
The registration must be loaded after the original extension is available. In a Composer installation, confirm that your extension is installed and active, then clear TYPO3 caches after changing the configuration. TYPO3 compiles and caches several pieces of runtime information, so a correct PHP file may appear ineffective until the configuration cache has been flushed.
For older TYPO3 versions, you may encounter string-based class names instead of ::class references. Both styles can be valid for the relevant release, but using class constants reduces spelling mistakes and works well with IDE refactoring. Check the TYPO3 version’s documentation and the project’s existing conventions before mixing registration formats.
A useful diagnostic is to add a temporary, unmistakable change to the overridden method and inspect the response or a controlled log entry. Do not leave noisy debugging output in a public Australian production site, especially where a staging server is available through a local hosting provider or a Sydney-based agency environment.
Making PHP 8.1 Compatibility Explicit
PHP 8.1 brings language improvements that can expose assumptions in older TYPO3 extensions. Constructor property promotion, enums, readonly properties, and stricter type handling are helpful, but they do not automatically make a legacy class compatible. Your XClass must obey the parent contract exactly.
Pay attention to nullable parameters, union types, declared return values, and the difference between mixed and a concrete type. If the parent method returns ?array, returning array is generally compatible, while returning string is not. Likewise, do not reduce the visibility of an inherited method or change a required parameter into an incompatible form.
Dependency injection deserves special care. A replacement repository or service may appear to work when TYPO3 constructs it through the object manager, then fail in a command, scheduler task, cache warm-up, or test that creates it differently. Keep the constructor aligned with the vendor class and run the same execution paths used by the application.
Review the vendor package after every update. A Composer update can replace the parent implementation, remove the method you override, or introduce a new event that makes the XClass unnecessary. Keeping a short comment with the affected package version and the reason for the workaround can save time during the end-of-financial-year release rush.
Testing The Override In A Real Project
Test the replacement at several levels. A unit test can verify the altered method in isolation, while a functional test confirms that TYPO3 actually resolves the original class to your XClass. The latter catches registration and cache problems that a direct new ItemRepository() test will miss.
Use Composer’s platform settings to ensure the test environment really runs PHP 8.1. A local Docker container, a staging machine on an Australian NBN connection, or CI configured with the production PHP version can reveal differences in autoloading and extensions. Test with realistic records, including empty results, missing relations, invalid dates, and data created before the patch existed.
The following checks keep the workaround small and reviewable.
Before deployment
- Confirm the original class and method still exist
- Match PHP 8.1 parameter and return types exactly
- Run unit, functional, and TYPO3 cache checks
- Record the vendor version that required the patch
After deployment
- Clear TYPO3 and application caches
- Inspect frontend and backend logs
- Test scheduler and CLI paths where relevant
- Watch response behaviour across AEST business hours
Choosing Between An XClass And Other Options
An XClass is often the fastest safe answer when a vendor extension has no suitable hook and the required adjustment affects one method. It can also be preferable to maintaining a fork when the change is small and likely to be temporary. The implementation remains in your own extension, so it can be reviewed, tested, and removed independently.
A PSR-14 event listener is usually a better option when the extension dispatches an event at the right point. Events reduce coupling to a concrete class and often survive upgrades more gracefully. Configuration overrides, TypoScript, a site set, or a custom data processor may be sufficient when the change concerns presentation or settings rather than core domain behaviour.
| Approach | Best fit | Upgrade risk | Typical maintenance |
|---|---|---|---|
| Custom XClass | One targeted method needs replacement | Medium to high | Review parent class after updates |
| PSR-14 event listener | The extension exposes a suitable event | Low to medium | Maintain listener and event tests |
| Composer patch | A small vendor bug needs a direct fix | Medium | Reapply or remove patch on updates |
| Forked extension | Several coordinated changes are required | High | Merge upstream changes regularly |
| Local configuration | Behaviour is already configurable | Low | Document project-specific settings |
For a client project serving customers in Melbourne, Brisbane, or Perth, release timing can influence the choice. A narrowly tested XClass may be safer than a large fork before a campaign launch, while a permanent product integration should move towards an official patch or event-based solution. Record the workaround in the project notes, such as the coding journal notes that accompany troubleshooting work, and raise it with the extension maintainer when the behaviour represents a genuine defect.
A custom XClass should leave the codebase in a better state than a direct vendor edit. Keep the class small, preserve PHP 8.1 contracts, test the actual TYPO3 bootstrap, and revisit the override whenever Composer updates the dependency. When the upstream project releases a fix, remove the registration and replacement class promptly, then run the full regression suite before deploying through the normal staging and production process.
Apply the patch in a disposable branch first, verify it against the project’s real TYPO3 version, and commit the registration, replacement class, tests, and cache-clearing notes together. That gives the next developer—or future you—a clear, reversible path from workaround to supported implementation.
cat ~/interests.json
| Key | Value |
|---|---|
| editor | Terminal-first workflow |
| os | Mac OS X / Unix |
| vcs | Git, distributed version control |
| deploy | Capistrano, cron automation |
| graphics | SVG, Adobe Illustrator troubleshooting |
| networking | IP validation, SSH, VPN |
git log --oneline --reverse
Solving SVG import issues in Adobe Illustrator CS6 and CC
When importing an SVG into Illustrator, the operation fails with an unknown error [CANT]. A workaround for this Adobe-side bug.
Solving NDK build issues on OS X
Troubleshooting native development kit compilation problems on Mac OS X.
Programmatically adding PHP generated TypoScript to the backend configuration
Integrating dynamically generated TypoScript into Typo3 backend setups using PHP.
ArgumentError: Could not parse PKey: no start line
Debugging an SSH key parsing error encountered during deployment.
Validating IP-Addresses in PHP
Using PHP filter functions with flags like FILTER_FLAG_IPV4 and FILTER_FLAG_IPV6, and understanding how filter_var handles reserved IP addresses.
Cocoa: Using NSTrackingArea
A short tutorial on using Cocoa's NSTrackingArea to capture mouseEntered and mouseExited events.
cat ~/contact.txt