ERROR · 27 · VERIFIED 2026-10allwpcare.com/errors/deprecated-creation-of-dynamic-property/
Errors / deprecated-creation-of-dynamic-property

Deprecated: Creation of Dynamic Property

A plugin or theme sets a property its class never declared, and PHP 8.2 flags it. The site works. Update the plugin and log errors, not show them.

The message you see
1−Deprecated: Creation of dynamic property MyPlugin::$items is deprecated in /var/www/html/wp-content/plugins/my-plugin/my-plugin.php on line 7
Last verified Oct 2026Tested with WordPress 7.1Applies to WordPress 7.1 (tested) · PHP 8.2+

Why it happens

  • A plugin or theme assigns a value to a property its class never declared, such as $this->items = array(); in a class with no public $items;. PHP 8.2 made that deprecated.
  • The host moved the site from PHP 8.1 or older to PHP 8.2 or newer. The code did not change, but PHP now reports it.
  • The author never updated the plugin or theme for PHP 8.2. The message names the file, so the path shows whose code it is.
  • WP_DEBUG is on and WordPress shows errors on the page (WP_DEBUG_DISPLAY is on, which is the default), so a message that is normally hidden reaches visitors.

How to fix it

Try the fixes in this order. Each one is independent: stop as soon as the error is gone.

FIX 01

Read the path to find who owns the code

  1. Look at the folder after wp-content/. plugins/my-plugin/ is a plugin called my-plugin, themes/name/ is a theme, and mu-plugins/ holds must-use plugins that the Plugins screen cannot deactivate. A path in wp-includes/ or wp-admin/ would be WordPress core. I loaded 15 front-end and admin screens on a clean WordPress 7.1.4 with PHP 8.3 and WP_DEBUG on, and none showed this message.
  2. The name before ::$ is the class and the name after it is the property. The line number is for the author. You do not need to open the file.
FIX 02

Update, or replace, the plugin or theme

  1. Go to Dashboard → Updates and update the plugin or theme named in the path. Authors fix this by declaring the property, or by adding one line above the class.
  2. If there is no update, send the author the exact message from your log. A report with the class, the property and the line is easy to act on.
  3. If the plugin is no longer maintained, pick a replacement; how to update plugins safely covers switching without breaking the site. In PHP 9 this deprecation is planned to become an error, so an abandoned plugin will stop working in time.
  4. To check that the plugin is the source, deactivate it (Plugins → Installed Plugins → Deactivate, or wp plugin deactivate my-plugin). The message went away on my test site as soon as I did.
FIX 03

Hide it from visitors: log, do not display

  1. In wp-config.php, above the line "That's all, stop editing", set define( 'WP_DEBUG', true );, define( 'WP_DEBUG_DISPLAY', false ); and define( 'WP_DEBUG_LOG', true );. The message then goes to wp-content/debug.log and not onto the page. I tested it: the page was clean and the log held one line per page load.
  2. Or turn WP_DEBUG off. WordPress then tells PHP not to report deprecations at all. I tested this too, including with the server's display_errors switched on: nothing reached the page.
  3. Both hide the message and leave the code as it is. When you use the log, keep debug.log private (the WordPress site check tests this) and turn debugging off when you are done. See the WordPress debug log guide.
FIX 04

If you wrote the code: declare the property

  1. Add the property to the class: public $items; (or private $items;). This is what PHP's manual recommends. On my test site this removed the message and the page still printed "Items: 3".
  2. If the class really needs free-form properties, put #[AllowDynamicProperties] on the line above class. I tested this too. PHP 8.2 and newer read it. PHP 8.1 and older see a comment (the # starts one), so the same file runs on every PHP version. Child classes inherit the permission.
  3. Do not use @ to hide the line, and do not use a catch-all error_reporting call to hide every deprecation. Both leave the bug in place.

The message means that some code gave a value to a property its class never declared. PHP 8.2 started reporting that as a deprecation. It is not a warning and not a fatal error: the code runs, the property is created, and the page loads. The path in the message names the plugin or theme that wrote the code, and the fix belongs to its author. Update or replace it. You can keep visitors from seeing the message by writing errors to a log instead of the page.

What you see#

The message is plain text with no styling. PHP prints it where the code runs, so it can land at the bottom of a page, above the admin menu, or in the middle of a layout.

The bottom of a WordPress page. The site title and menu, then the footer line with the theme name and the words Designed with WordPress. Below the footer, in plain unstyled text: Deprecated: Creation of dynamic property MyPlugin::$items is deprecated in /var/www/html/wp-content/plugins/my-plugin/my-plugin.php on line 7 Items: 3.
The bottom of the home page on WordPress 7.1.4 and PHP 8.3 with WP_DEBUG on. The message sits below the footer. The plugin's own output (Items: 3) follows it on the same line, and the status code is 200.

I made it with a one-file plugin:

wp-content/plugins/my-plugin/my-plugin.php
<?php/*Plugin Name: My Plugin*/class MyPlugin {	public function __construct() {		$this->items = array( 1, 2, 3 );	}}add_action( 'wp_footer', function () {	$p = new MyPlugin();	echo 'Items: ' . count( $p->items );} );

The class has no $items declared. PHP creates it on the spot, and PHP 8.2 and newer say so.

How it works#

Before PHP 8.2, you could assign to any property name on any object, and PHP made it. A typo in a name created a new property and hid the bug. PHP 8.2 made this deprecated, unless the class opts in. PHP's RFC says PHP 9.0 will throw an Error instead, so the message is a notice that the code will break later.

Seven stacked cards. 1: the plugin code assigns $this->items without declaring it. 2A: PHP 8.1 and older create it and say nothing. 2B: PHP 8.2 and newer create it and raise Deprecated. 3: WordPress decides where the message goes. With WP_DEBUG off it is dropped. With WP_DEBUG and display on it is printed in the page. With WP_DEBUG on, display off and log on, it is one line in wp-content/debug.log.
PHP raises the message. WordPress decides whether anyone sees it.

I ran the same file on four PHP versions. The only version difference was 8.2:

PHPResult for an undeclared property
7.4.33No message
8.1.34No message
8.2.35Deprecated: Creation of dynamic property Plain::$items is deprecated
8.3.36The same message

PHP reports it when the property is created, which is once per object and not once per assignment. In my test, two objects of the same class gave two messages, and assigning the property a second time on one object gave none.

What PHP does not flag#

I tried each case below on PHP 8.3. Only the first is flagged.

CodeMessage on PHP 8.3
Class with no property declared, then $this->items = ...Deprecated
Property declared: public $items;None
Class marked #[AllowDynamicProperties]None
Child class of a marked classNone
Class with __set() and __get()None
stdClass, or (object) array()None

Reading a property that was never created is a different message: Warning: Undefined property: Declared::$nope. This page is about creating one. For the array version of the same idea, see the undefined array key page.

WordPress core marks its own classes the same way. WordPress 7.1.4 carries #[AllowDynamicProperties] 131 times, in 130 files, such as WP_Comment and WP_List_Table. That is why a clean install is silent and why the message almost always names a plugin or theme.

Why you may see it only sometimes#

PHP raises the message. Whether it reaches the page depends on WordPress. In wp_debug_mode(), WP_DEBUG off sets error_reporting to a list that leaves out E_DEPRECATED. So on most live sites the message is dropped before it is shown or logged. It shows up when WP_DEBUG is on. I tested each setting on the lab site:

WP_DEBUGWP_DEBUG_DISPLAYWP_DEBUG_LOGResult
offanyanyNothing on the page, even with the server's display_errors on
onon (default)anyOn the page
onoffonNot on the page. One line per page load in wp-content/debug.log
onoffoffNot on the page
wp-content/debug.log
[30-Oct-2026 07:23:53 UTC] PHP Deprecated:  Creation of dynamic property MyPlugin::$items is deprecated in /var/www/html/wp-content/plugins/my-plugin/my-plugin.php on line 7

If you see this message, someone turned debugging on: you, a plugin that offers a debug mode, or a host's staging setup. Turning it off, or switching to the log, ends it.

When the message does real damage#

The message itself is harmless. Where it prints is not. Output that starts before WordPress sends its headers stops redirects from working. I put a four-line must-use plugin in wp-content/mu-plugins/early.php. It creates a property on an undeclared class in the admin_init hook, which runs before every admin screen:

wp-content/mu-plugins/early.php
<?phpclass EarlyThing {}add_action( 'admin_init', function () {	$t = new EarlyThing();	$t->cache = 1;} );

With display on, I saved Settings → General. The setting saved, but the page stayed on a block of text and never redirected. The screenshot below is the Dashboard, which shows the same text at the top:

The top of the WordPress dashboard. Above the Dashboard heading, a block of plain text starts with Deprecated: Creation of dynamic property EarlyThing::$cache is deprecated, followed by several Warning: Cannot modify header information lines.
One dynamic property in the admin area, followed by a chain of headers-already-sent warnings. The admin menu is folded because the screenshot is 900 px wide. This is the Dashboard, not the Settings screen.

The save returned 200 with this text in front of the page. With WP_DEBUG_DISPLAY off, the same save returned 302 to options-general.php?settings-updated=true. I re-ran both on the day of writing. The extra warnings are explained on the headers already sent page. Fix the first message and the rest go away.

Edge cases I tested#

  • The attribute on old PHP. #[AllowDynamicProperties] ran without an error on PHP 7.4, because a line that starts with # is a comment there. Plugin authors can add it without raising their minimum PHP version.
  • The attribute does not change behaviour. It only turns the message off. A class marked with it can still create typo properties.
  • A global switch. Putting error_reporting( error_reporting() & ~E_DEPRECATED ); in a must-use plugin removed the message from the page on my test site. It hides every deprecation, including the ones that warn you about code that will stop working. Prefer the log.
  • Not a reason to downgrade PHP. Going back to PHP 8.1 silences it, as the table above shows, but it only postpones the problem. Fix or replace the plugin instead.

If the page goes blank#

A deprecation never stops a page by itself. A blank page, or "There has been a critical error on this website", is a different problem: see the white screen of death and the critical error page.

Sources

  1. PHP manual: Deprecated features in PHP 8.2 · Dynamic properties are deprecated unless the class opts in. The page lists declaring the property, the attribute, and WeakMap.
  2. PHP RFC: Deprecate dynamic properties · Deprecated in PHP 8.2, planned to throw an Error in PHP 9.0. stdClass and __get/__set are exempt. Passed 52 to 25 (vote closed 2021-11-26).
  3. PHP manual: AllowDynamicProperties · The attribute, and that child classes inherit the permission.
  4. Debugging in WordPress · WP_DEBUG, WP_DEBUG_DISPLAY and WP_DEBUG_LOG, and the log-not-display setup for live sites.
  5. WordPress core: wp_debug_mode() in wp-includes/load.php · With WP_DEBUG off, error_reporting leaves out E_DEPRECATED. With it on, error_reporting is E_ALL.
  6. WordPress core: WP_Comment in wp-includes/class-wp-comment.php · One of the core classes marked #[AllowDynamicProperties].
Newsletter

One email when something breaks or changes.

New plugins and re-tested guides, about twice a month. Never a sales pitch.