#58896 closed defect (bug) (fixed)
Fix WP_List_Table magic methods for PHP 8.2 dynamic properties
Reported by: | antonvlasenko | Owned by: | hellofromTonya |
---|---|---|---|
Milestone: | 6.4 | Priority: | normal |
Severity: | normal | Version: | 4.0 |
Component: | Administration | Keywords: | php82 has-unit-tests commit has-patch |
Focuses: | php-compatibility | Cc: |
Description (last modified by )
WP_List_Table
has a list of compatible fields (compat_fields
), which was introduced at the same time as the magic methods. The fields in compat_fields
list are then not dynamic properties, as the magic methods properly handle each. But fields not in that list are dynamic properties.
What happens if a field is not in the list?
__get()
falls through the method.__set()
nothing happens as it falls through the method.__isset()
returnsfalse
, which is correct.__unset()
nothing happens as it falls through the method.
The problem:
When a dynamic property is called on this class, there's no information given to the developer. The notice of _doing_it_wrong()
will provide the alert to developers to modify their code.
Also notice: the compat_fields
are essentially public properties through the magic methods.
Thus, there are 2 ways to fix this class to be compatible with PHP 8.2's dynamic properties:
- Option 1: Fix the magic methods:
- Change 1: Add a notice or
_doing_it_wrong()
to each magic method, i.e. to alert and inform developers when attempting to get/set/isset/unset a dynamic property.
- Change 2: Add
return null
in__get()
when attempting to get a dynamic property that is not in thecompat_fields
.
- Option 2: Add each
compat_fields
as apublic
property and remove the magic methods.
Props to @jrf for identifying the issue.
References:
- These changes were discussed and agreed to in a livestream by @jrf @markjaquith and @hellofromTonya.
- The changes are part of #56034, which is the larger Dynamic Properties proposal and initiative.
Change History (32)
#3
@
17 months ago
More context:
These changes were made for backwards-compatibility (BC).
In #22234 (in WP 4.0.0):
- [28493] added the
__get()
magic method toWP_List_Table
to:Make private properties readable for backwards compatibility
- [28521] added the
__set()
magic method
- [28524] added the
__isset()
and__unset()
magic methods
In #30891 (in WP 4.2.0):
- [31146] added the list of
compat_fields
and the logic in each of the property magic methods.
This ticket was mentioned in PR #4905 on WordPress/wordpress-develop by @antonvlasenko.
17 months ago
#4
- Keywords has-patch has-unit-tests added; needs-patch needs-unit-tests removed
This PR aims to call doint_it_wrong() when trying to use dynamic properties on the WP_List_Table class. Dynamic properties are not compatible with PHP 8.2 and above.
Trac ticket: https://core.trac.wordpress.org/ticket/58896
#5
in reply to:
↑ 1
;
follow-up:
↓ 6
@
17 months ago
Replying to hellofromTonya:
@antonvlasenko can you pull
WP_List_Table
changes out of https://github.com/WordPress/wordpress-develop/pull/3535 and create a PR for this ticket?
I've pulled the changes out of https://github.com/WordPress/wordpress-develop/pull/3535 and submitted them to https://github.com/WordPress/wordpress-develop/pull/4905, @hellofromTonya.
#6
in reply to:
↑ 5
@
17 months ago
Replying to antonvlasenko:
I've pulled the changes out of https://github.com/WordPress/wordpress-develop/pull/3535 and submitted them to https://github.com/WordPress/wordpress-develop/pull/4905
Thank you @antonvlasenko! Appreciate your time porting your original work to this ticket. Good to have option 1 here for history and contributor consideration.
#7
@
17 months ago
Exploring Option 2
Let's look at option 2, which proposes to make the properties public
and remove the magic methods.
Scenario 1: Accessing a defined property
Demo code: https://3v4l.org/jNFmL#v8.1.20
- Get: works the same ✅
isset()
: works the same ✅- Set: works the same ✅
unset()
: works the same ✅- After an
unset()
:- Get: works the same ✅
isset()
: works the same ✅- Set: works the same ✅
Functionality is unaffected
Scenario 2: Accessing an undefined (dynamic) property
Demo code: https://3v4l.org/nCACX#v8.1.20
- Get: ❌
- both return same value of
NULL
✅ - Without magic method: throws
Warning
❌
- both return same value of
isset()
: works the same ✅- Set: ❌
- With magic method: does not set - value remains
NULL
- Without magic method: value sets ❌
- With magic method: does not set - value remains
unset()
: works the same ✅- After an
unset()
- Get: ❌
- both return same value of
NULL
✅ - Without magic method: throws
Warning
❌
- both return same value of
isset()
: works the same ✅- Set: ❌
- With magic method: does not set - value remains
NULL
- Without magic method: value sets ❌
- With magic method: does not set - value remains
- Get: ❌
#8
@
17 months ago
How did the original 3.1 code work?
Before the magic methods were added, how did the original WP_List_Table
work with defined and undefined (dynamic) properties? This test report shows it in action, i.e. to set a baseline for considering how to fix this ticket.
Running on PHP 5.2.17
Demo: https://3v4l.org/GBfpi#v5.2.17 using the properties code from WP 3.1 in [15491] / #14579.
- Get:
- Defined property: returns
NULL
- Dynamic property: returns
NULL
and throwsNotice
- Defined property: returns
isset()
:- Defined property:
false
- Dynamic property:
false
- Defined property:
- Set:
- Defined property: sets it
- Dynamic property: sets it
- After an
unset()
- Get:
- Defined property: returns
NULL
and throws aNotice
- Dynamic property: returns
NULL
and throws aNotice
- Defined property: returns
isset()
:- Defined property: returns
false
- Dynamic property: returns
false
- Defined property: returns
- Set:
- Defined property: sets it
- Dynamic property: sets it
isset()
:- Defined property: returns
true
- Dynamic property: returns
true
- Defined property: returns
- Get:
Running on PHP 8.1.20
Demo: https://3v4l.org/GBfpi#v8.1.20 using the properties code from WP 3.1 in [15491] / #14579.
- Get:
- Defined property:
null
- Dynamic property: returns
NULL
and throwsWarning
- Defined property:
isset()
:- Defined property:
false
- Dynamic property:
false
- Defined property:
- Set:
- Defined property: sets it
- Dynamic property: sets it
- After an
unset()
- Get:
- Defined property: returns
NULL
and throws aWarning
- Dynamic property: returns
NULL
and throws aWarning
- Defined property: returns
isset()
:- Defined property: returns
false
- Dynamic property: returns
false
- Defined property: returns
- Set:
- Defined property: sets it
- Dynamic property: sets it
isset()
:- Defined property: returns
true
- Dynamic property: returns
true
- Defined property: returns
- Get:
#9
@
17 months ago
Comparing the how the code worked in 3.1 to the changes made in 4.0+, the BC break happened when the magic methods were added. Removing the magic methods (option 2) restores the code to how it originally functioned, thus removing the BC break(s) that happened long ago. Hmm interesting.
Let's compare:
Demo: https://3v4l.org/DZnjf#v8.1.20
- Get:
- Defined property:
- WP 3.1: returns
NULL
- Current: returns
NULL
✅ - Option 2: returns
NULL
✅
- WP 3.1: returns
- Dynamic property:
- WP 3.1: returned
NULL
and throwsWarning
- Current: returns
NULL
❌ 👈 BC Break - WP 3.1: returned
NULL
and throwsWarning
✅
- WP 3.1: returned
- Defined property:
isset()
:- Defined property:
- WP 3.1:
false
- Current:
false
✅ - Option 2:
false
✅
- WP 3.1:
- Dynamic property:
- WP 3.1:
false
- Current:
false
✅ - Option 2:
false
✅
- WP 3.1:
- Defined property:
- set:
- Defined property:
- WP 3.1: sets
- Current: sets ✅
- Option 2: sets ✅
- Dynamic property:
- WP 3.1: sets
- Current: does not set ❌ 👈 BC Break
- Option 2: sets ✅
- Defined property:
unset()
:- Get:
- Defined property:
- WP 3.1: returns
NULL
and throwsWarning
- Current: returns
NULL
and throwsWarning
✅ - Option 2: returns
NULL
and throwsWarning
✅
- WP 3.1: returns
- Dynamic property:
- WP 3.1: returns
NULL
and throwsWarning
- Current: returns
NULL
but does not throwWarning
❌ 👈 BC Break - Option 2: returns
NULL
and throwsWarning
✅
- WP 3.1: returns
- Defined property:
isset()
:- Defined property:
- WP 3.1:
false
- Current:
false
✅ - Option 2:
false
✅
- WP 3.1:
- Dynamic property:
- WP 3.1:
false
- Current:
false
✅ - Option 2:
false
✅
- WP 3.1:
- Defined property:
- Set:
- Defined property:
- WP 3.1: sets
- Current: sets ✅
- Option 2: sets ✅
- Dynamic property:
- WP 3.1: sets
- Current: does not set ❌ 👈 BC Break
- Option 2: sets ✅
- Defined property:
- Get:
#10
@
17 months ago
When comparing how the code originally worked in 3.1 to how it worked in 4.0 after adding the magic methods to option 2, here are the findings:
- For defined properties, the code works the same. No impacts.
- For dynamic properties:
- The magic methods added in 4.0 caused a BC break when getting and setting.
- Option 1 retains the current behavior (added in 4.0 with the magic methods).
- Option 2 fixes the BC break and restores how it originally worked in 3.1.
Would Option 2 be considered a BC break for getting or setting dynamic properties?
I'm inclined to say no. Though it will function differently than the current code for dynamic properties, the current code itself has been a BC break since first introduced in 4.0.
On the other hand, one could argue that Option 2 is itself a BC break from the long standing behavior which could impact any extenders who are attempting to get or set a dynamic property. BC break of a BC break to restore the original behavior ;)
Hmm, an interesting situation. Fix the long ago BC break (option 2). Or fix the immediate problem and retain current behavior (option 1).
My mind next goes to knowing the impact by understanding how many plugins might be affected.
Are any plugins using dynamic properties with WP_List_Table
?
There are over 3200 plugins using WP_List_Table
https://wpdirectory.net/search/01H67HCY4CABR39QEEQRNH1DG8. It is possible there are plugins using dynamic properties. And if yes, then users using those plugins would be affected.
#11
@
17 months ago
After thinking about impacts on users and talking with @azaozz, I think Option 1 is the right way forward.
Though the magic methods introduced a BC break, that happened 9 years ago. Fast forward to today, plugins are (and have been) using that code. I think the current code is the yardstick for determining if the new fix is a BC break or not. Using that yardstick, Option 2 would be a BC break to the current code and thus could/would impact users.\
@antonvlasenko I'll review https://github.com/WordPress/wordpress-develop/pull/4905 tomorrow with the goal of committing it this week or next.
#12
@
17 months ago
- Keywords changes-requested added
I think there's an Option 3 which is a combination of Option 1 and 2 with the benefits of both along with the ability to possibly remove the magic methods in the future.
Option 3:
- Properties:
- Change their visibility keyword to
public
. - Remove the
$compat_fields
.
- Change their visibility keyword to
- Magic methods:
- Remove the
$compat_fields
code. - Unknown dynamic properties in the wild: keep the magic methods (for now) but deprecate each of them. The deprecation notice will alert developers to modify their code.
- Remove the
The benefits:
- Performance: provides a micro-optimization when a
public
property is invoked outside of the object, as the magic methods and thein_array()
logic within each will not be invoked. - Maintainability: less code and complexity.
- Back-compat: no BC breaks (no change to the result of the code).
- Sets up the possibility of removing the magic methods in the future.
This ticket was mentioned in PR #4924 on WordPress/wordpress-develop by @hellofromTonya.
17 months ago
#13
Implements Option 3, see explanation in the Trac ticket https://core.trac.wordpress.org/ticket/58896#comment:12.
Fixes the dynamic properties for PHP 8.2 compatibility by:
- Defined property: restores each to
public
. - Removes the
$compat_fields
and logic. - Deprecates each property magic method.
Trac ticket: https://core.trac.wordpress.org/ticket/58896
@hellofromTonya commented on PR #4924:
17 months ago
#14
Pinging @markjaquith and @anton-vlasenko for your review and thoughts on this implementation.
@hellofromTonya commented on PR #4924:
17 months ago
#15
Forced pushed to rebase on trunk
. Navigation Puppeteer e2e tests keep timing out. Rebased to bring in any changes that may have been committed since this PR was created.
@hellofromTonya commented on PR #4924:
17 months ago
#16
Are any plugins extending WP_List_Table
using compat_fields
property? Yes.
Plugins with > 5M installs:
- Jetpack's `Automattic\Jetpack\Assets\Jetpack_Modules_List_Table` adds
all_items
dynamic property to thecompat_fields
property .
Plugins with <= 4k installs:
- APCu Manager
- WPJAM Basic
- DecaLog
- OPcache Manager
- oEmbed Manager
- JP Omnisearch
- MailArchiver
While Jetpack can be fixed, users upgrading to 6.4 without first upgrading the plugin will experience a Undefined property
Warning on PHP 8 or Notice on PHP 7 (likely in the server logs) https://3v4l.org/8djK8.
I think Option 3 is not the best option :(
@hellofromTonya commented on PR #4905:
17 months ago
#17
There are 2 Core commits in this PR:
- Start the tests from a known starting state.
- Tests added for the magic methods.
#18
@
16 months ago
- Focuses php-compatibility added
- Keywords commit added; changes-requested removed
PR 4905 is ready for commit
.
It's Option 1 but deprecates instead of using _doing_it_wrong()
, i.e. to keep parity with the PHP 8.2 deprecation and provide feedback to extenders to define properties on their child class instead of using dynamic properties.
I'll prepare the commit shortly.
Also added php-compatibility
focus to identify this ticket resolves an incompatibility that PHP 8.2 first introduced.
@hellofromTonya commented on PR #4905:
16 months ago
#21
I committed the test leakage fix in https://core.trac.wordpress.org/changeset/56348. Need to rebase and then fix merge conflicts for this PR (i.e. as it also fixed the problem). Doing that now.
@hellofromTonya commented on PR #4905:
16 months ago
#22
There are 2 Core commits in this PR:
- Start the tests from a known starting state https://github.com/WordPress/wordpress-develop/pull/4905/commits/4e6fa3653e43f0323a330b75112f03bd1458c2e8.
- Tests added for the magic methods.
@hellofromTonya commented on PR #4905:
16 months ago
#24
Committed via https://core.trac.wordpress.org/changeset/56349.
#26
@
16 months ago
- Keywords needs-patch added; has-patch removed
- Resolution fixed deleted
- Status changed from closed to reopened
Reopening to improve the deprecation message. I think the word "define" should be changed to "declare".
Why is "declare" better?
It aligns well to the topic of dynamic properties and the act of explicitly listing the variable as a property on the class.
The goal of this message is guide developers to change their code. Changing the term to "declare" hopefully will aid in the understanding of what is being asked of developers when this deprecation is thrown.
For example, if the developer does "dynamic properties" search, they'll likely be lead to these resources:
- The RFC for deprecating dynamic properties, which defines a dynamic property in PHP as:
“Dynamic property” here refers to a property that has not been declared in the class.
It's also worth noting that both terms are used in the PHP manual's OOP Property page.
I'll follow-up with PR and commit shortly.
This ticket was mentioned in PR #4963 on WordPress/wordpress-develop by @hellofromTonya.
16 months ago
#27
- Keywords has-patch added; needs-patch removed
Improves the magic method deprecation message by changing from "define" to "declare".
Reasoning provided here in Trac ticket.
Trac ticket: https://core.trac.wordpress.org/ticket/58896
@hellofromTonya commented on PR #4963:
16 months ago
#29
Committed via https://core.trac.wordpress.org/changeset/56356.
This ticket was mentioned in PR #5169 on WordPress/wordpress-develop by @hellofromTonya.
15 months ago
#30
Replaces trigger_error()
with wp_trigger_error()
for the deprecation notices of dynamic properties in magic methods.
WP_List_Table
added by changeset 56349 / Trac 58896
WP_User_Query
added by changeset 56353 / Trac 58897
WP_Text_Diff_Renderer_Table
added by changeset 56354 / Trac 58898
Trac ticket: https://core.trac.wordpress.org/ticket/57686
@hellofromTonya commented on PR #5169:
15 months ago
#32
Committed via:
WP_List_Table
https://core.trac.wordpress.org/changeset/56542WP_User_Query
https://core.trac.wordpress.org/changeset/56543WP_Text_Diff_Renderer_Table
https://core.trac.wordpress.org/changeset/56544
@antonvlasenko can you pull
WP_List_Table
changes out of https://github.com/WordPress/wordpress-develop/pull/3535 and create a PR for this ticket?