Make WordPress Core

Opened 2 months ago

Closed 5 weeks ago

#65519 closed enhancement (fixed)

Code Modernization: Use array_any() and array_all() in various places

Reported by: Soean Owned by: SergeyBiryukov
Priority: normal Milestone: 7.1
Component: General Version:
Severity: normal Keywords: has-patch
Cc: Focuses: performance, php-compatibility

Description

PHP 8.4 introduced the array_any() and array_all() functions, and core already ships polyfills for both in wp-includes/compat.php (since WordPress 6.8.0, see [59783]). This makes it safe to adopt them on every supported PHP version without raising the minimum requirement.

Several foreach loops in core implement exactly these patterns:

  • "return true on the first matching element" → array_any()
  • "return false on the first failing element" → array_all()

Converting them makes the intent clearer and the code more concise.
These are pure refactors with no behavioral change; the short-circuit and empty-array semantics match the loops they replace.

Change History (20)

This ticket was mentioned in PR #12260 on WordPress/wordpress-develop by @Soean.


2 months ago
#1

  • Keywords has-patch added

Replaces several foreach loops that iterate an array, return false as soon as an element fails a condition, and otherwise fall through to true. This is exactly what PHP 8.4's array_all() expresses in a single, more readable call.

Core already ships an array_all() polyfill in wp-includes/compat.php (since 6.8.0), so the change works on every supported PHP version without raising the minimum requirement.

Trac ticket: https://core.trac.wordpress.org/ticket/65519

This ticket was mentioned in PR #12258 on WordPress/wordpress-develop by @Soean.


2 months ago
#2

Replaces several foreach loops that iterate an array, return true as soon as an element matches a condition, and otherwise fall through to false. This is exactly what PHP 8.4's array_any() expresses in a single, more readable call.

Core already ships an array_any() polyfill in wp-includes/compat.php (since 6.8.0), so the change works on every supported PHP version without raising the minimum requirement.

Trac ticket: https://core.trac.wordpress.org/ticket/65519

#3 @mukesh27
2 months ago

  • Focuses performance added

#4 @SergeyBiryukov
2 months ago

  • Milestone Awaiting Review7.1

#5 @SergeyBiryukov
2 months ago

In 62550:

Code Modernization: Use array_any() where appropriate.

This commit replaces several foreach loops that iterate an array, return true as soon as an element matches a condition, and otherwise fall through to false. That is exactly what PHP 8.4's array_any() expresses in a single, more readable call.

WordPress core includes a polyfill for array_any() on PHP < 8.4 as of WordPress 6.8, so the change works on every supported PHP version without raising the minimum requirement.

Follow-up to [59783].

Props Soean, mukesh27, SergeyBiryukov.
See #65519.

#6 @TobiasBg
2 months ago

I think that the PHP arrow functions this introduces could use the static keyword for a slight performance improvement?

@westonruter commented on PR #12258:


8 weeks ago
#7

Can you address the merge conflicts?

This ticket was mentioned in PR #12300 on WordPress/wordpress-develop by @Soean.


8 weeks ago
#10

It replaces a foreach loop in check_post_type_supports_notes() that iterates the editor supports, returns true as soon as an element has non-empty notes, and otherwise falls through to false.

That is exactly what PHP 8.4's array_any() expresses in a single, more readable call. WordPress core includes a polyfill for array_any() on PHP < 8.4 as of WordPress 6.8, so the change works on every supported PHP version without raising the minimum requirement.

Follow-up to [59783], [62550].

Trac ticket: https://core.trac.wordpress.org/ticket/65519

#11 @SergeyBiryukov
8 weeks ago

In 62553:

Code Modernization: Use array_all() where appropriate.

This commit replaces several foreach loops that iterate an array, return false as soon as an element fails a condition, and otherwise fall through to true. That is exactly what PHP 8.4's array_all() expresses in a single, more readable call.

WordPress core includes a polyfill for array_all() on PHP < 8.4 as of WordPress 6.8, so the change works on every supported PHP version without raising the minimum requirement.

Follow-up to [59783], [62550].

Props Soean, mukesh27, westonruter, SergeyBiryukov.
See #65519.

#12 @SergeyBiryukov
8 weeks ago

  • Owner set to SergeyBiryukov
  • Status newaccepted

#14 @SergeyBiryukov
8 weeks ago

In 62564:

Code Modernization: Use array_any() in WP_REST_Comments_Controller.

This commit replaces a foreach loop in ::check_post_type_supports_notes() that iterates the editor supports, returns true as soon as an element has non-empty notes, and otherwise falls through to false. That is exactly what PHP 8.4's array_any() expresses in a single, more readable call.

WordPress core includes a polyfill for array_any() on PHP < 8.4 as of WordPress 6.8, so the change works on every supported PHP version without raising the minimum requirement.

Follow-up to [59783], [62550], [62553].

Props Soean.
See #65519.

#15 @dmsnell
8 weeks ago

Copied from #62558

I noticed this when seeing [62564] and wondered if we’re keeping a watchful eye on performance of these conversions. PHP still includes some disproportionately high cost for function calls, particularly user-space function calls, and we’re changing working code for styling purposes in ways that introduce new overhead on a substantial base of installed WordPresses.

The foreach form is not only about as efficient as possible, but it works with iterable values and ArrayAccess values while array_any() crashes on non-array inputs.

So these changes are not supposed to bring in behavioral changes, but do, increase the risk of PHP request crashes, and add overhead which isn’t much, but is also unnecessary (and given that it’s being applied to arrays, has a high chance of multiplying insignificant overhead into significant overhead).

I guess I feel like maybe we are jumping too quickly into changing existing code without a warrant that it needs updating, and putting existing working code at risk by assuming the updates are benign. I support having the polyfills, but the refactors like [62564] seem like they verge on change for the sake of change to the detriment of the project.

#16 @dmsnell
8 weeks ago

These are pure refactors with no behavioral change; the short-circuit and empty-array semantics match the loops they replace.

@Soean unfortunately this is only mostly true, but there are behavioral changes when foreach is receiving iterable non-array values, and there are performance implications worth measuring, particularly in cases where we have large arrays and a high probability of the match not existing in the array or existing towards the end.

as @josephscott recently saw, a rather meaningless performance issue in splitting CSS selectors became a problem in theme.json code because it was called around 4,000 times in a single request.

Given that these are inherently array operations, it would be wise for us to be thoughtful in each refactor. Also, for good or bad, the foreach idiom is ubiquitous in PHP, so we probably have less confidence to say that the refactor makes “the intent clearer.” Or maybe another way to phrase this is that, given we have already misunderstood the intent of the foreach in the claims we are making, it would be wise of us to take extra caution in these refactors because our tendency is to over-simplify the role the foreach plays and under-appreciate the behavioral impact of array_any() and the like.

#18 @khokansardar
5 weeks ago

  • Focuses php-compatibility added

This ticket was mentioned in Slack in #core by adrianduffell. View the logs.


5 weeks ago

#20 @adrianduffell
5 weeks ago

  • Resolutionfixed
  • Status acceptedclosed

This was discussed in the bug scrub today. We believe it is completed! Marking it as fixed -- please correct if this is not the case.

Note: See TracTickets for help on using tickets.