PHP 8.4: What Actually Changed and When to Upgrade
PHP 8.4 introduced property hooks and asymmetric visibility as its flagship features, building on a pattern established since PHP 8.0 of steady improvements in developer experience and runtime performance. Understanding what these changes mean in practice helps you decide whether upgrading now makes sense for your application and your development workflow.
Each minor release in the PHP 8 series has brought measurable performance gains alongside language improvements. The practical question is not whether to upgrade eventually, but when the upgrade cost is worth paying and what each new feature actually does for code you maintain or write.
Property Hooks: Cleaner Validation Logic on Properties
Property hooks are the most significant addition in PHP 8.4. They allow you to define getter and setter logic directly on class properties, removing the boilerplate of separate methods while keeping validation and transformation logic tied to the property it protects.
Before PHP 8.4, if you wanted a property that enforced a specific constraint, you would write a private property with public getter and setter methods. The logic lived in separate methods from the property definition. With hooks, the behaviour attaches directly to the property declaration.
class OrderLine
{
public float $quantity {
get => $this->quantity;
set {
if ($value <= 0) {
throw new \InvalidArgumentException('Quantity must be positive');
}
$this->quantity = $value;
}
}
}
The practical benefit is less code and clearer ownership. When the property is renamed, the hook follows it. When the validation logic needs to change, there is one place to change it rather than searching through paired getter and setter methods. This makes refactoring simpler and reduces the risk that validation logic drifts away from the property it protects.
Hooks support read and write operations independently. A property can have a getter that formats the stored value for external access and a setter that validates or transforms the incoming value before storage.
class Product
{
public string $name {
get => strtoupper(trim($this->name));
set => trim($value);
}
}
Property hooks are backward compatible. Code that does not use hooks is unaffected. There is no performance penalty compared to using separate methods for most use cases. The feature is syntactic sugar that makes code cleaner and harder to misuse accidentally.
Asymmetric Visibility: Simpler Encapsulation for Read-Mostly Data
Asymmetric visibility allows a property to have different visibility for reading versus writing. A property can be publicly readable but privately writable. This pattern was achievable before PHP 8.4 with a public getter and a private property, but required separate method names and did not prevent direct access to the private property from within the class itself.
The practical use cases include configuration objects where properties can be read by any code but only written during initialisation, value objects where the internal state should not be modified after construction, and DTOs where the object should be immutable once created. The pattern was possible before PHP 8.4, but required more code to enforce and was easy to accidentally bypass.
class Config
{
public string $dbHost {
private get;
public set;
}
public string $dbName {
private get;
public set;
}
}
In this example, $dbHost and $dbName can be read from outside the class, but can only be set from within the class or its subclasses. This is useful for configuration objects where the values are set during initialisation and should not change after the object is constructed.
Asymmetric visibility also works with readonly properties. A property can be publicly readable and privately writable but never changed after initialisation, combining the benefits of readonly with the flexibility of asymmetric visibility.
class User
{
public readonly string $email {
private set;
}
public function __construct(string $email)
{
$this->email = $email;
}
}
This pattern makes immutability guarantees clearer in code that deals with sensitive data. Once constructed, the email cannot be modified from outside the class, but the constructor can set it normally.
PHP 8.3 Deprecations That Become Errors in PHP 8.4
PHP 8.3 introduced deprecations that become hard errors in PHP 8.4. If your application runs without deprecation warnings on PHP 8.3, it is more likely to work cleanly on PHP 8.4. If your application produces deprecation warnings on PHP 8.3, those deprecations are likely to break when you upgrade.
The most significant changes involve older features that were deprecated in PHP 8.3 and removed in PHP 8.4. These include some long-deprecated extensions, certain behaviours that were maintained for backward compatibility, and some syntax kept for compatibility with older PHP versions.
Running your application under PHP 8.3 with deprecation warnings enabled before upgrading reveals exactly what needs fixing. The warnings give you a targeted list rather than unexpected failures after the upgrade.
php -d error_reporting=E_ALL -d display_errors=On your-app.php
If you use a framework, check its documentation for PHP 8.4 compatibility notes. Most frameworks publish a compatibility guide for each PHP version. Understanding the differences between PHP 8.3 and PHP 8.4 helps you anticipate what might need attention during the upgrade process.
Performance Improvements in the PHP 8 Series
The PHP 8 series has delivered consistent performance improvements with each minor release. Moving from PHP 8.0 to PHP 8.4 typically shows measurable throughput improvement for CPU-bound applications. The gains come from JIT compiler optimisations, opcode cache improvements, and runtime interpreter enhancements.
The improvements are most noticeable in applications that do significant computation, process large volumes of data, or have heavy use of array operations. Applications that are primarily I/O bound, such as those waiting on database queries or external API calls, see smaller improvements because the bottleneck is the I/O rather than the PHP runtime.
The JIT compiler introduced in PHP 8.0 was a major step. PHP 8.4 improved the JIT's ability to optimise hot paths in code that runs repeatedly, such as framework routing, template rendering, and data transformation loops. If your application does repetitive processing, the performance gains may be noticeable in production.
When to Upgrade to PHP 8.4
The upgrade decision depends on your current PHP version, your application's dependency constraints, and the risk tolerance of your deployment process. Each situation has a different recommended approach.
If you are running PHP 8.0 or below, the upgrade to PHP 8.4 should be a planned priority. Each minor version in the PHP 8 series has extended the performance gap between older PHP versions and the current one. PHP 8.0 reached end-of-life in December 2024. Running outdated PHP is a security liability, particularly for applications handling sensitive data or accessible from the internet.
If you are running PHP 8.1 or 8.2, the upgrade path is straightforward if your application has no deprecated feature usage. Run on PHP 8.4 in a staging environment first, check for deprecation warnings, fix them, then deploy. The risk is low for well-maintained applications with good test coverage.
If you are running PHP 8.3, upgrade to 8.4 once your dependencies confirm compatibility. Most major frameworks and libraries support PHP 8.4 at or shortly after release. Check your dependencies before upgrading to avoid surprises in production.
Checking Application Compatibility Before Upgrading
The most reliable way to check compatibility is to run the application on PHP 8.4 in a staging environment. If you use a containerised deployment, change the base image to a PHP 8.4 image and run the full test suite. Any errors or failures surface immediately rather than in production.
If you do not have a full test suite, verify the core functionality manually. Log in, create a record, retrieve a record, update a record, delete a record. Check the admin interface if there is one. Verify that the most critical user journeys work before deploying.
For Composer-based projects, check dependency compatibility without making changes first:
composer update --dry-run --ignore-platform-reqs
This shows what would change without actually changing anything. Then check with platform requirements set to PHP 8.4:
composer update --ignore-platform-reqs --platform=php:8.4
This shows whether the dependencies themselves are compatible with PHP 8.4. If a critical library does not support PHP 8.4, you cannot upgrade until it does.
The Cost of Staying on Unsupported PHP Versions
Running PHP versions past their security support date carries known risks. PHP 8.0 end-of-life was December 2024. PHP 8.1 and 8.2 will reach end-of-life in the same pattern, roughly two years after their initial release. End-of-life means no security patches, no bug fixes, and increasing risk as vulnerabilities discovered in supported versions are not backported.
For applications handling sensitive data or accessible from the internet, running an unsupported PHP version creates both a security risk and a difficult explanation if something goes wrong. If your application is compromised because of a known PHP vulnerability that has a patch in a supported version but not in the version you are running, the question of why the patch was not applied becomes hard to answer.
Upgrading proactively is typically less costly than recovering from an exploited vulnerability on an outdated system. Planning the upgrade during a maintenance window is far preferable to emergency deployment after a security incident.
Writing New Code for PHP 8.4
If you are writing new code that will run on PHP 8.4 only, property hooks and asymmetric visibility are worth using where they fit naturally. Property hooks make validation and formatting logic easier to read and maintain. Asymmetric visibility makes immutability guarantees clearer and harder to accidentally violate.
For existing codebases, adding hooks to new classes you write is a reasonable approach. You do not need to retrofit hooks into existing properties, but using them in new code means the new code is cleaner and the patterns are consistent. This incremental adoption keeps new code current without requiring a full rewrite of working systems.
The no-stdin-opening option for implicit enum values fromBackedTypes is a smaller change that simplifies enum construction. If you work with backed enums, this removes boilerplate where the enum previously needed explicit from() method calls to convert from a backed type.
Maintaining good technical documentation practices helps your team adopt new PHP features consistently. When introducing property hooks or asymmetric visibility, documenting the decisions behind their use makes future maintenance easier for anyone working with the codebase.
Next Steps for Your Upgrade
The PHP 8.4 upgrade path is well-trodden by now. Running PHP 8.3 with deprecation warnings enabled gives you a clear list of what needs fixing. Checking your dependencies for PHP 8.4 support tells you whether you are blocked by a library that has not yet updated. The upgrade itself, for most applications, follows a straightforward test-then-deploy pattern.
If you need help reviewing your current setup, preparing a short note with your current PHP version, application framework, and any deprecation warnings you have seen helps frame the work needed. You can get in touch with details of your setup and what you want to achieve from the upgrade.