PHP 8.3 introduced typed class constants, the json_validate() function, readonly property amendments in child classes, the Override attribute, and improvements to array_sum() and array_product(). These changes affect how modern WordPress plugins should be written.
Problem: A WordPress plugin is being upgraded to require PHP 8.3, and it is unclear which new language features are safe to adopt versus which deprecated patterns need to be removed to avoid warnings in production.
Solution: PHP 8.3 brings readonly class improvements, typed class constants, json_validate(), the #[\Override] attribute, and mb_str_split(). Remove deprecated dynamic property creation on classes not marked #[AllowDynamicProperties]. Run vendor/bin/phpstan analyse --php-version=80300 to detect compatibility issues before deployment.
The examples below demonstrate typed class constants for WordPress option schemas, json_validate() as a replacement for decode-and-check patterns, and the Override attribute for safer method overriding in plugin class hierarchies.
<?php
// PHP 8.3: Typed class constants
// Before 8.3, class constants had no enforced type — any constant value was valid.
// In 8.3, you can declare the type:
class MyPlugin {
const string VERSION = '1.0.0';
const int CACHE_TTL = 3600;
const bool DEBUG_MODE = false;
const float TAX_RATE = 0.20;
// Typed constants in interfaces — all implementing classes must honour the type
}
interface HasVersion {
const string MIN_WP_VERSION = '6.0';
}
// Constants in child classes must also satisfy the parent type:
class MyPluginPro extends MyPlugin {
const string VERSION = '2.0.0'; // OK — still a string
// const int VERSION = 2; // Fatal error: type mismatch
}
// PHP 8.3: Override attribute — catch stale method overrides at compile time
class Base {
public function process(): void {}
}
class Child extends Base {
#[Override] // PHP will fatal-error if Base::process() is renamed/removed
public function process(): void {
parent::process();
// extra logic
}
// #[Override]
// public function processs(): void {} // Fatal: Base::processs() doesn't exist
}
json_validate() and readonly property improvements:
<?php
// PHP 8.3: json_validate() — faster than json_decode() just for validation
// BAD (PHP < 8.3 pattern — decodes the whole string just to check validity):
$raw = get_option( 'my_settings', '' );
$decoded = json_decode( $raw );
if ( json_last_error() !== JSON_ERROR_NONE ) {
// invalid JSON
}
// GOOD (PHP 8.3):
$raw = get_option( 'my_settings', '' );
if ( ! json_validate( $raw ) ) {
// invalid JSON — no memory allocated for full decode
return;
}
$settings = json_decode( $raw, true ); // decode only when valid
// json_validate() also accepts depth and flags parameters:
json_validate( $raw, depth: 5, flags: JSON_INVALID_UTF8_IGNORE );
// PHP 8.3: Readonly properties can be initialized in child classes
// (Previously, readonly properties could only be set in the declaring class)
class PostMeta {
public function __construct(
public readonly int $post_id,
public readonly string $meta_key,
) {}
}
class ProductMeta extends PostMeta {
public readonly string $product_type; // new readonly property in child
public function __construct( int $post_id, string $meta_key, string $product_type ) {
parent::__construct( $post_id, $meta_key );
$this->product_type = $product_type; // set once in constructor — then immutable
}
}
NOTE: Run vendor/bin/phpstan analyse --php-version=80300 to detect PHP 8.3 compatibility issues in your plugin before upgrading the server's PHP version. Most WordPress plugins require only minor changes — the biggest source of breakage is static analysis tools that don't yet recognise typed constants.