PHP 8.3: Key Features, Improvements, and Examples
PHP 8.3, released on November 23, 2023, continues the tradition of PHP releases, bringing a mix of new features, performance improvements, and deprecations. This release focuses on improved type safety, enhanced developer experience, and further refinement of existing features. Let’s dive into the key aspects of PHP 8.3 with detailed descriptions and code examples.
1. Typed Class Constants
Perhaps the most significant feature in PHP 8.3 is the ability to declare types for class constants. Previously, class constants were implicitly dynamically typed, leading to potential type-related errors at runtime. Now, you can enforce type consistency for constants, just like you can for properties and method parameters/return values.
Before PHP 8.3:
“`php
class MyClass {
const VERSION = “1.0”; // No type hint
}
// No type checking, could accidentally assign a different type:
// MyClass::VERSION = 2; // No error at compile time, but might cause issues later
“`
In PHP 8.3:
“`php
class MyClass {
const string VERSION = “1.0”; // Type declaration for the constant
const int MAX_USERS = 100;
const array SUPPORTED_LANGUAGES = [‘en’, ‘es’, ‘fr’];
const ?float PI = 3.14159; // Nullable type
}
// Attempting to change the type will now result in a Fatal Error:
// MyClass::VERSION = 2; // Fatal error: Cannot use int as value for string constant VERSION of class MyClass
//Accessing the constant:
echo MyClass::VERSION; // Outputs: 1.0
“`
Benefits of Typed Class Constants:
- Improved Type Safety: Ensures that constants hold values of the expected type, preventing unexpected behavior and bugs.
- Better Code Readability and Maintainability: Makes code clearer and easier to understand, as the intended type of the constant is explicitly stated.
- Enhanced Static Analysis: Allows IDEs and static analysis tools to provide more accurate code suggestions and error detection.
- Consistency with other Type-hinted Features: Brings constants in line with the existing type-hinting system for properties, parameters, and return values.
2. json_validate()
Function
PHP 8.3 introduces the json_validate()
function, a much-needed utility for efficiently checking if a string is valid JSON without incurring the overhead of decoding it. Prior to this, you had to use json_decode()
and check for errors, which was less efficient, especially for large JSON strings.
“`php
$jsonString = ‘{“name”: “John Doe”, “age”: 30}’;
$invalidJsonString = ‘{“name”: “John Doe”, “age”: 30, }’; // Trailing comma
if (json_validate($jsonString)) {
echo “Valid JSON\n”;
} else {
echo “Invalid JSON\n”;
echo “JSON Error: ” . json_last_error_msg() . “\n”; // Access error message if needed.
}
if (json_validate($invalidJsonString)) {
echo “Valid JSON\n”;
} else {
echo “Invalid JSON\n”;
echo “JSON Error: ” . json_last_error_msg() . “\n”; // Access error message
}
“`
Output:
Valid JSON
Invalid JSON
JSON Error: Syntax error
Benefits of json_validate()
:
- Performance: Significantly faster than
json_decode()
when you only need to validate the JSON structure, as it avoids the full decoding process. - Resource Efficiency: Uses less memory, especially for large JSON strings, as it doesn’t create PHP data structures from the JSON.
- Readability: More concise and expressive than the older
json_decode()
error-checking approach.
3. Dynamic Class Constant Fetch
PHP 8.3 allows fetching class constants using dynamic expressions. This means you can now use variables or expressions to specify the constant name.
“`php
class Config {
const DEFAULT_THEME = ‘light’;
const DARK_THEME = ‘dark’;
}
$themeType = ‘DEFAULT_THEME’;
echo Config::{$themeType}; // Outputs: light
$themeType = ‘DARK_THEME’;
echo Config::{$themeType}; // Outputs: dark
function getConstant(string $constantName): mixed {
return Config::{$constantName};
}
echo getConstant(‘DEFAULT_THEME’); // Outputs: light
“`
Before PHP 8.3, you would have needed to use constant()
:
php
$themeType = 'DEFAULT_THEME';
echo constant(Config::class . '::' . $themeType);
Benefits:
- Cleaner Syntax: More readable and concise than using the
constant()
function. - Improved Developer Experience: Simplifies code when you need to dynamically access class constants.
4. #[\Override]
Attribute
The #[\Override]
attribute provides a way to explicitly indicate that a method is intended to override a method from a parent class or interface. This helps catch errors early if the method signature doesn’t match the parent method or if the parent method is removed or renamed. This is similar to @Override
in Java.
“`php
class ParentClass {
public function greet(string $name): string {
return “Hello, ” . $name;
}
}
class ChildClass extends ParentClass {
#[\Override]
public function greet(string $name): string { // Correct override
return “Greetings, ” . $name;
}
#[\Override] // Will cause a fatal error at compile-time
public function great(string $name): string { // Typo!
return “Greetings, ” . $name;
}
}
interface MyInterface {
public function doSomething(): void;
}
class MyImplementation implements MyInterface
{
#[\Override]
public function doSomething(): void {
// …implementation
}
}
class IncorrectImplementation implements MyInterface
{
#[\Override] // Fatal error: IncorrectImplementation::doSomethingElse() has #[\Override] attribute, but no matching parent method exists
public function doSomethingElse(): void {
// …
}
}
“`
Benefits of #[\Override]
:
- Early Error Detection: Catches potential errors at compile time, preventing runtime surprises.
- Improved Code Refactoring: Helps ensure that overridden methods remain consistent with their parent methods during code refactoring.
- Clearer Intent: Explicitly communicates that a method is intended to override a parent method.
5. Cloning of Readonly Properties
PHP 8.3 allows cloning of readonly properties within the __clone()
magic method. This provides more control over the cloning process for objects with readonly properties.
“`php
class User {
public readonly string $username;
public function __construct(string $username) {
$this->username = $username;
}
public function __clone(): void
{
//Allowed in 8.3. Will throw in earlier versions.
$this->username = $this->username . '_clone';
}
}
$user1 = new User(“johndoe”);
$user2 = clone $user1;
echo $user1->username; // Outputs: johndoe
echo “\n”;
echo $user2->username; // Outputs: johndoe_clone
“`
Before PHP 8.3, this would result in a fatal error:
Fatal error: Cannot modify readonly property User::$username
Benefits:
- Greater Control over Cloning: Allows modification of readonly properties during cloning, enabling more flexible cloning behavior.
- Removes an unnecessary restriction: Makes the readonly feature behave more consistently.
6. More Appropriate Date/Time Exceptions
PHP 8.3 introduces more specific exception classes for date and time operations, providing better error handling and debugging. Instead of generic Exception
or Error
objects, you now get exceptions like DateMalformedStringException
, DateInvalidTimeZoneException
, etc.
“`php
try {
$date = new DateTime(‘invalid date string’);
} catch (DateMalformedStringException $e) {
echo “Invalid date string: ” . $e->getMessage() . “\n”;
} catch (Exception $e) {
// Handle other potential exceptions
echo “General exception: ” . $e->getMessage() . “\n”;
}
try {
new DateTimeZone(“Invalid/Timezone”);
} catch(DateInvalidTimeZoneException $e) {
echo “Invalid Timezone exception: ” . $e->getMessage() . “\n”;
}
“`
Benefits:
- More Specific Error Handling: Allows you to catch and handle specific date/time errors more precisely.
- Improved Debugging: Provides more informative error messages, making it easier to diagnose and fix date/time related issues.
- Better Code Organization: Encourages better error handling practices by providing dedicated exception classes.
7. Randomizer Additions
The Randomizer
class, introduced in PHP 8.2, receives some useful additions:
-
getBytesFromString(string $string, int $length): string
: Generates a random string of a specified length, using only characters from the provided input string. -
getFloat(float $min, float $max, \Random\IntervalBoundary $boundary = \Random\IntervalBoundary::ClosedOpen): float
: Generates a random floating-point number within a specified range, with control over whether the boundaries are inclusive or exclusive. This allows for fine-grained control, including generating numbers between, for instance, 0 (inclusive) and 1 (exclusive), similar toMath.random()
in JavaScript. -
nextFloat(): float
: Returns a float between 0 (inclusive) and 1 (exclusive).
“`php
$randomizer = new \Random\Randomizer();
// Get a 5-character random string using only lowercase letters:
$randomString = $randomizer->getBytesFromString(‘abcdefghijklmnopqrstuvwxyz’, 5);
echo “Random string: ” . $randomString . “\n”;
// Get a random float between 10 (inclusive) and 20 (exclusive):
$randomFloat = $randomizer->getFloat(10, 20, \Random\IntervalBoundary::ClosedOpen);
echo “Random float: ” . $randomFloat . “\n”;
//Get a random float between 0 (inclusive) and 1 (exclusive):
$randomFloat01 = $randomizer->nextFloat();
echo “Random float between 0 and 1: ” . $randomFloat01 . “\n”;
“`
8. Deprecations
Several features and behaviors are deprecated in PHP 8.3, indicating that they will be removed in future versions. It’s important to address these deprecations to ensure forward compatibility. Here are some key ones:
MT_RAND
constants (with incorrect implementations):MT_RAND_PHP
is deprecated. UseMT_RAND_MT19937
instead.- Calling static methods non-statically: Calling a non-static method statically is already deprecated. Calling a static method non-statically (e.g.,
$obj::staticMethod()
) is now also deprecated. ReflectionClass::newInstanceWithoutConstructor()
andReflectionMethod::invoke()
with by-reference arguments: Reflective calls that interact with by-reference parameters have some limitations and inconsistencies; certain use cases are now deprecated.- Usage of
assert()
with string arguments. Use proper boolean expressions withassert()
. - Passing negative
$widths
tonumber_format()
: The behavior ofnumber_format()
with a negative$width
argument for the number of decimal places is deprecated.
9. Performance Improvements
PHP 8.3 includes various internal optimizations that contribute to improved performance. These improvements are often subtle and depend on the specific code being executed, but generally, you can expect a slight speed boost compared to PHP 8.2. Improvements have been made to the handling of:
- Inheritance
array_push
,array_unshift
unserialize
str_contains
,str_starts_with()
,str_ends_with()
range()
function
Conclusion
PHP 8.3 is a solid release that builds upon the strengths of previous versions. The introduction of typed class constants, json_validate()
, dynamic class constant fetching, the #[\Override]
attribute, and improved date/time exceptions are significant enhancements that improve code quality, maintainability, and developer experience. The additions to the Randomizer
class, along with readonly property cloning enhancements further add to the flexibility of the language. The deprecations also serve as an important reminder to update legacy code for future compatibility. Overall, PHP 8.3 is a recommended upgrade for all PHP developers.