PHP 8.3 Features Worth Using in WordPress Today

WordPress officially supports PHP 8.3, and while most theme and plugin developers are still writing PHP 7.4-compatible code, there’s no reason your custom theme or plugin can’t take advantage of newer features — especially if you control the hosting environment.
Here are the PHP 8.3 features I’ve started using in WordPress projects, with concrete examples.
Typed Class Constants
Before 8.3, class constants had no type enforcement. You could accidentally override a parent’s string constant with an integer in a child class, and PHP wouldn’t complain. Now you can enforce types:
class Base_Widget {
const string WIDGET_ID = 'base_widget';
const int CACHE_TTL = 3600;
}
class Featured_Posts_Widget extends Base_Widget {
const string WIDGET_ID = 'featured_posts';
// This would throw an error:
// const int WIDGET_ID = 42;
}
In WordPress, this is useful for plugin architectures where child classes must override constants with the correct type. I use it for post type registrations, taxonomy identifiers, and cache keys — anywhere a wrong type would cause subtle bugs downstream.
json_validate() — Validate Before Decode
Before 8.3, validating JSON meant calling json_decode() and checking json_last_error(). That’s wasteful if you only need to know whether the JSON is valid, not parse it. json_validate() does exactly that:
// WordPress settings field validation
function validate_json_config( string $input ): string {
if ( ! json_validate( $input ) ) {
add_settings_error(
'my_plugin_config',
'invalid_json',
'The configuration field must contain valid JSON.'
);
return get_option( 'my_plugin_config', '{}' );
}
return sanitize_text_field( $input );
}
add_filter( 'sanitize_option_my_plugin_config', 'validate_json_config' );
This is cleaner and marginally faster than decode-then-check. I use it for any settings field that accepts JSON input, webhook payload validation, and REST API request bodies.
The #[Override] Attribute
This one prevents a sneaky class of bugs. When you override a parent method, #[Override] tells PHP to verify that the parent method actually exists. If someone renames the parent method and forgets to update the child, PHP throws an error instead of silently creating a dead method:
abstract class Base_Post_Type {
abstract protected function get_post_type_args(): array;
public function register(): void {
register_post_type( $this->get_slug(), $this->get_post_type_args() );
}
abstract protected function get_slug(): string;
}
class Project_Post_Type extends Base_Post_Type {
#[Override]
protected function get_slug(): string {
return 'adz_project';
}
#[Override]
protected function get_post_type_args(): array {
return [
'labels' => [ 'name' => 'Projects' ],
'public' => true,
'has_archive' => true,
'show_in_rest' => true,
'supports' => [ 'title', 'editor', 'thumbnail' ],
];
}
}
Without #[Override], if you rename get_slug to get_post_type_slug in the parent, the child’s get_slug method would silently stop being called. With the attribute, PHP catches it immediately.
Readonly Amendments
PHP 8.2 introduced readonly classes, but they were overly restrictive — you couldn’t clone and modify readonly properties. PHP 8.3 fixes this with the ability to reinitialize readonly properties during __clone():
readonly class WP_Query_Config {
public function __construct(
public string $post_type,
public int $posts_per_page,
public string $orderby = 'date',
public string $order = 'DESC',
) {}
public function with_pagination( int $per_page ): self {
$clone = clone $this;
// In 8.3, this works during __clone
// Allows immutable value objects with modification
return $clone;
}
}
This enables immutable value objects — a pattern that’s excellent for query builders and configuration objects in WordPress. You create a base config and derive variations without mutating the original.
Dynamic Class Constant Fetch
You can now use variables to access class constants dynamically. This was only possible with constant() before:
class Post_Status {
const string PUBLISH = 'publish';
const string DRAFT = 'draft';
const string PENDING = 'pending';
}
// Before 8.3
$status = constant( Post_Status::class . '::' . strtoupper( $input ) );
// PHP 8.3
$name = strtoupper( $input );
$status = Post_Status::{$name};
This is cleaner for mapping user input to predefined constants, which comes up often in REST API endpoints and admin interfaces.
Should You Use These Today?
If you’re building a public plugin or theme for the WordPress.org repository, you’re stuck with PHP 7.4+ compatibility for maximum reach. But for client projects, custom themes, or private plugins where you control the server, there’s no reason to hold back.
Check your PHP version with php -v or look at the Site Health screen in WordPress admin under Tools. Most managed WordPress hosts now support PHP 8.3, and many default to it for new installations.
These features won’t revolutionize your code, but they’ll make it safer and more expressive. Typed constants catch bugs. #[Override] catches refactoring mistakes. json_validate() simplifies validation. Small wins that compound over time.
Written by
Adrian Saycon
A developer with a passion for emerging technologies, Adrian Saycon focuses on transforming the latest tech trends into great, functional products.


