PHP 8.3: Key Changes Every Developer Should Know

14 min read 2,754 words
PHP 8.3: The Changes Worth Knowing About Before You Upgrade featured image

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. If you are considering a broader version migration, a structured PHP version upgrade guide can help you map out the steps involved.

Typed Class Constants in PHP 8.3

One of the most practical new features in PHP 8.3 is the ability to declare types on class constants. Before this release, constants were effectively untyped — any value could be assigned regardless of the context. This created subtle bugs when a constant was used in a type-constrained environment 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. The compiler catches mismatches at development time rather than allowing them to surface in production:

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 produces a compile-time error. This matters most in large codebases where constants are defined in one location and used across many different files and contexts. Without type safety on constants, a developer could change a value in one place and break type contracts elsewhere without any warning until runtime.

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 matches expectations 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, making your code more predictable as it scales.

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. The old method requires calling json_decode with null as the second argument, checking if the return value is not null, then checking json_last_error. This is verbose and easy to get wrong, especially when validation logic gets copied across multiple files.

PHP 8.3 adds json_validate(), a dedicated function that checks whether a string contains 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 incoming 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. A well-designed API design guide for business web applications covers input validation as a foundational principle, not an afterthought.

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 without workarounds.

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 every day, 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. It removes the need for wrapper classes or associative array workarounds that developers previously had to build just to track resource identity.

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 PHP 8.3, trying to use const or a named constructor in an anonymous class would halt execution immediately:

$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. The result is cleaner code for temporary processors, adapters, and test doubles that do not need to exist beyond a single use case.

Randomizer Additions in PHP 8.3

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, reviewing the PHP 8.2 features that laid the groundwork helps before exploring what PHP 8.3 adds. PHP 8.3 extends the Randomizer 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 older mt_rand approach. This is useful for probability calculations, weighted random selection, and sampling algorithms where predictable distribution matters. When you need a random percentage for A/B testing logic or probabilistic caching decisions, getNextFloat gives you a clean interface that was previously only available through external libraries or manual calculation.

Deprecated Features to Address Before PHP 9.0

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. Ignoring deprecation warnings in your logs is a pattern that leads to painful migrations later.

String Interpolation Syntax Deprecation

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 now the only valid syntax in PHP 8.3. Migrating now is straightforward — a find-and-replace handles most cases without affecting the behaviour of your code.

Run this pattern across your codebase to find occurrences:

# Find files using deprecated interpolation
grep -r '\${' --include="*.php" ./src

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, which is a subtle correctness issue that may not surface in testing if your application primarily handles English text. The correct approach is to use the standard 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.

// Deprecated: suppresses error but generates warning
$data = @file_get_contents($path);

// Recommended: explicit error handling
if (!file_exists($path)) {
    throw new RuntimeException("File not found: {$path}");
}
$data = file_get_contents($path);

Performance Improvements in PHP 8.3

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.

For most web applications built on frameworks like Laravel or Symfony, the visible performance gain comes from framework and library updates that coincide with the PHP version upgrade rather than the PHP runtime improvement alone. Keeping your application on the latest PHP version ensures you benefit from these incremental optimisations as they accumulate across releases.

Upgrading from PHP 8.2 to PHP 8.3

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. The migration effort is primarily about addressing deprecation warnings rather than rewriting logic.

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. If you do not have a staging environment, setting one up as part of the upgrade process is worth the effort.

# 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. Tackling them now means one less thing to worry about when major version upgrades arrive.

When to Upgrade to PHP 8.3

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, the upgrade is straightforward.

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. Reviewing the full scope of changes involved in moving from PHP 7.4 helps you plan the upgrade in manageable stages rather than one large effort.

Preparing Your Application for PHP 8.3

Taking a methodical approach to the PHP 8.3 upgrade reduces risk and gives you confidence in the result. Start by auditing your current PHP version across all environments — development, staging, and production. Inconsistencies between environments are a common source of problems that are easier to fix before the upgrade than during it.

Next, review your composer.json file for the php constraint. Most projects specify a minimum PHP version that the application supports. Update this to include 8.3 after confirming your dependencies are compatible, then run composer update to pull in updated packages that may have PHP 8.3-specific optimisations.

Enable deprecation warnings in your development environment and run your test suite. Any deprecation warnings related to the features listed above indicate code that needs updating before the upgrade. Address these systematically, starting with the @ operator fixes, then the string interpolation syntax, then the mbstring function changes.

Document any third-party libraries that do not yet support PHP 8.3. Check the library's GitHub repository or Packagist page for compatibility status. If a critical library is not compatible, you may need to wait for an update, find an alternative, or temporarily exclude that library from the PHP 8.3 upgrade while planning a longer-term solution.

If you need help reviewing your current PHP setup or planning an upgrade path, prepare details of your current PHP version, framework version, key dependencies, and any specific errors or warnings you have encountered. This gives a clear starting point for a practical assessment of what the upgrade involves.

Frequently Asked Questions

Is PHP 8.3 compatible with Laravel?
Yes. Laravel 10.34 and later support PHP 8.3. Earlier versions of Laravel 10 may require a minor update to be fully compatible. Run composer update when upgrading Laravel to the latest version of the 10.x branch to ensure compatibility. Always test on a staging environment before deploying to production. Laravel's official documentation lists the minimum PHP version required for each release.
Does PHP 8.3 break existing code?
No, not in the way PHP 8.0 did with its many breaking changes. PHP 8.3 maintains backward compatibility for code that follows documented behaviour. The main incompatibilities come from the deprecations: code that uses the deprecated ${ } string interpolation or the @ operator on internal functions will generate warnings. Fixing these before they become errors in PHP 9.0 is the main migration effort involved. Most applications will run without any changes beyond addressing deprecation warnings.
What is the performance difference between PHP 8.2 and 8.3?
For most web applications, the difference is in the range of 2 to 8 percent in request throughput, measured in requests per second under a load test. For CPU-intensive scripts, the difference can be 5 to 15 percent. The improvement is real but not dramatic. PHP 8.3 is an incremental improvement rather than a leap like PHP 8.0 was from PHP 7.4. The performance gains from keeping your application updated accumulate over multiple versions.
Should I upgrade to PHP 8.3 now?
If your application runs on PHP 8.0, 8.1, or 8.2, the upgrade is low risk and should be done as part of your regular maintenance cycle. If you are on PHP 7.4, the upgrade to 8.3 requires going through PHP 8.0 first, which involves a larger migration effort. Prioritise the upgrade for any application that processes user input, handles authentication, or communicates with a database — the typed class constants and json_validate() improvements catch bugs earlier in those contexts where correctness matters most.
How do I check which PHP version my application is using?
Run php -v in your terminal to see the currently active PHP version. For web applications, create a file containing <?php phpinfo(); ?> and access it through your web server. The output shows the PHP version loaded by your web server, which may differ from the CLI version. Check both to ensure your development and production environments are aligned. Mismatches between CLI and web server PHP versions are a common source of confusion during upgrades.
What happens if I skip PHP 8.3 and wait for PHP 9.0?
Skipping 8.3 means missing the deprecation warnings that tell you exactly what needs fixing before 9.0. The deprecations in 8.3 become errors in 9.0, so waiting increases the amount of changes to handle at once. Staying current with minor versions is less disruptive than waiting for a major version jump. Regular updates as they release is easier to manage than large migrations that accumulate across multiple version jumps.