Why PHP 8.3 Matters for Your Applications
PHP 8.3 was released in November 2023 as the latest minor version in the PHP 8 series. It does not introduce the kind of structural changes that PHP 8.0 brought with named arguments and union types, but it adds quality-of-life features that make everyday coding more precise, more readable, and less error-prone. For applications running on PHP 8.2, upgrading to 8.3 carries low risk with a meaningful return in developer experience and runtime performance.
PHP 8.3 also continues the pattern set by PHP 8.2, which introduced the Randomizer class and readonly amendments that refined how immutability works in PHP. Understanding these connections helps when planning an upgrade strategy for existing projects.
Typed Class Constants
PHP 8.3 introduces the ability to declare types on class constants. Before 8.3, constants were effectively untyped — any value could be assigned to a constant regardless of the context. This created subtle bugs when a constant was used in a type-constrained context and given a value that did not match what the rest of the code expected.
With typed constants, you declare the expected type as part of the constant declaration:
class PaymentStatus {
public const string PENDING = 'pending';
public const string COMPLETED = 'completed';
public const string FAILED = 'failed';
}
class HttpStatus {
public const int OK = 200;
public const int NOT_FOUND = 404;
public const int INTERNAL_ERROR = 500;
}
class CacheTTL {
public const int SHORT = 60;
public const int MEDIUM = 300;
public const int LONG = 3600;
}
Attempting to assign a value that does not match the declared type now produces a compile-time error rather than a runtime failure. This catches bugs at development time rather than in production, particularly in large codebases where constants are defined in one file and used across many others.
The practical benefit appears most clearly in enums, which are constants under the hood. When a backed enum case receives a value, the type declaration on the enum ensures the value is what you expect before the enum case is even created. If you are working with PHP 8.1 enums and readonly properties, typed class constants extend that same safety principle to your constant definitions.
json_validate() Function
PHP has handled JSON data since PHP 5.2, but the existing approach to validating JSON before processing has always been awkward. You call json_decode with null as the second argument, check if the return value is not null, then check json_last_error. This is verbose and easy to forget to do correctly.
PHP 8.3 adds json_validate(), a dedicated function that checks whether a string is valid JSON without parsing it to a PHP value:
$data = file_get_contents('user_input.json');
if (json_validate($data)) {
$decoded = json_decode($data, true);
// Safe to process
} else {
throw new RuntimeException('Invalid JSON in user_input.json');
}
The function accepts a flags parameter for future extensibility but currently returns true for valid JSON and false for invalid JSON. The performance improvement over json_decode plus error checking is meaningful in high-throughput API handlers where JSON validation happens on every request.
This matters for business applications that accept user input through APIs. Validating JSON early and cheaply before attempting to decode it reduces the risk of processing malformed data and simplifies your error handling logic. When building web applications that handle external data sources, proper input validation forms part of a defence-in-depth approach to application security.
get_resource_id() Function
Resources are PHP's oldest handle type — file handles, cURL handles, database connections, image contexts. They are opaque objects that you pass to functions, but there has never been a reliable way to compare two resources for identity or use them as array keys.
get_resource_id() extracts an integer identifier from a resource handle. This allows resources to be used as array keys, compared for equality, and stored in structures that require a scalar key:
$fileHandle = fopen('/tmp/data.txt', 'r');
$connection = mysqli_connect('localhost', 'user', 'pass', 'db');
$fileId = get_resource_id($fileHandle);
$connId = get_resource_id($connection);
// Use as array keys
$resourceTracker = [];
$resourceTracker[$fileId] = 'data.txt';
$resourceTracker[$connId] = 'mysql-production';
// Compare resources directly
if ($fileId === get_resource_id($otherFileHandle)) {
echo 'Same file handle';
}
This is primarily useful in debugging, monitoring, and profiling tools that need to track resource lifecycle. In normal application code, it is not a feature you reach for often, but when you need it, not having it creates real friction. Connection pooling libraries, logging systems that attach resource metadata, and test harnesses that verify resource cleanup all benefit from this addition.
Anonymous Class Improvements
Anonymous classes in PHP allow you to create disposable, single-use class instances inline without defining a named class. They are useful for quick interface implementations, test doubles, and one-off processors. PHP 8.3 adds support for named constructor and const declarations inside anonymous classes, which was previously a fatal error.
Before 8.3, this would produce a fatal error:
$processor = new class(100) {
public const DEFAULT_LIMIT = 100;
private int $limit;
public function __construct(int $limit) {
$this->limit = $limit;
}
public function process(array $data): array {
return array_slice($data, 0, $this->limit);
}
};
In PHP 8.3, this is valid and allows anonymous classes to follow the same patterns as named classes for constructor logic and constant definitions. This reduces the need to refactor an anonymous class into a named class simply because you needed to encapsulate initialisation logic or define a constant that belongs conceptually with that implementation.
Randomizer Additions
The Randomizer class was introduced in PHP 8.2 as a structured way to handle random number generation. If you are not familiar with the class, it is worth reviewing the PHP 8.2 features that laid the groundwork before exploring what PHP 8.3 adds. PHP 8.3 extends it with methods that make it more practical for real-world use.
getBytesFromString allows you to generate random bytes selected only from a specific character set. This is useful for generating short random tokens like coupon codes, referral codes, or one-time passwords:
$randomizer = new Randomizer();
// Generate a 10-character alphanumeric coupon code
$couponCode = $randomizer->getBytesFromString(
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
10
);
// Example output: KZM4T7BQJN
// Generate a secure random referral code
$referralCode = strtoupper($randomizer->getBytesFromString(
'abcdefghijklmnopqrstuvwxyz0123456789',
8
));
// Example output: R4KZ8E1F
getNextFloat returns a random float between 0 and 1 with better distribution than the old mt_rand approach. This is useful for probability calculations, weighted random selection, and sampling algorithms.
Deprecated Features to Address
Several features were deprecated in PHP 8.3 with removal planned for PHP 9.0. These are not removed in 8.3 but will generate warnings when used. Addressing these deprecations now prevents breaking changes when PHP 9.0 arrives.
String Interpolation Syntax
The ${ } string interpolation syntax is deprecated. This was the older style of interpolating variables into strings: "Hello, ${name}". The modern style "Hello, {$name}" was already preferred and is the only valid syntax in PHP 8.3. Migrating now is straightforward — a find-and-replace handles most cases.
mbstring ASCII Fallback Functions
The mbstring functions that treated non-ASCII characters as ASCII — mb_strtolower, mb_strtoupper, and their ASCII variants — are deprecated. These functions silently produced incorrect results for non-Latin character sets. The correct approach is to use the regular mb_strtolower and mb_strtoupper with explicit encoding parameters.
Error Suppression on Internal Functions
The @ operator for error suppression is deprecated when applied to internal functions. Using @ before functions like fopen, file_get_contents, or mysqli_connect now generates a deprecation warning. These warnings will become errors in PHP 9.0. Review any code that uses @ and replace it with proper error handling using try-catch blocks or conditional checks.
Performance Improvements
PHP 8.3 includes a JIT compiler improvement that reduces overhead for certain patterns in the continuous loop category. For web applications that are not CPU-bound, this does not produce a measurable change in request latency. For CLI scripts, long-running daemons, and data processing pipelines, the improvement can be in the range of 5 to 15 percent for relevant workloads.
The performance improvements apply most visibly to code that uses match expressions extensively, array manipulation in tight loops, and string operations in nested loops. If your application has a hot path that does significant string manipulation, upgrading to PHP 8.3 may show a measurable improvement without any code changes.
Upgrading from PHP 8.2
The upgrade path from PHP 8.2 to 8.3 is straightforward for most applications. The major breaking changes that existed in the PHP 8.0 and 8.1 migrations have been resolved, and 8.3 does not introduce incompatible changes to existing working code.
Before upgrading, run your test suite on the current PHP version to establish a baseline. Then install PHP 8.3 alongside the existing version, run the test suite against 8.3, fix any deprecation warnings, then deploy the updated code. Always set up PHP 8.3 in a staging environment before touching production.
# Check current PHP version
php -v
# Check for deprecation warnings in your code
php -d error_reporting=E_ALL -d display_errors=On your_script.php
# Run your test suite under PHP 8.3
./vendor/bin/phpunit --php=/usr/bin/php8.3
The key deprecations to address immediately are the @ operator on internal functions, the ${ } string interpolation syntax, and any use of the deprecated mbstring functions. These will produce warnings visible in logs and will break in PHP 9.0.
When to Upgrade
Upgrade to PHP 8.3 when you are on PHP 8.0 or later and the upgrade does not require moving through intermediate versions. If your application uses a framework with 8.3 support — Laravel 10.34 and later, Symfony 6.4 and later, or CakePHP 5.0 and later — you have a test suite covering the critical paths of your application, and you have the operational capacity to deploy and monitor the upgrade in staging before production.
Delay upgrading when you are on PHP 7.4 or earlier and the upgrade requires a multi-version jump. Also delay when your application uses a library that does not yet support PHP 8.3, or when your infrastructure does not have a staging environment where the upgrade can be validated before production. If you are currently on PHP 7.4, the path to PHP 8.3 involves upgrading through PHP 8.0 first, which carries its own migration effort. It may be worth reviewing your options for upgrading from PHP 7.4 to understand the full scope of changes involved.