Skip to content

Media, Editor, Menus: Use isset() instead of in_array() over array_keys() - #66

Closed
mukeshpanchal27 wants to merge 2 commits into
trunkfrom
perf/in-array-array-keys
Closed

Media, Editor, Menus: Use isset() instead of in_array() over array_keys()#66
mukeshpanchal27 wants to merge 2 commits into
trunkfrom
perf/in-array-array-keys

Conversation

@mukeshpanchal27

@mukeshpanchal27 mukeshpanchal27 commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Summary

Nine call sites in core answer the question "does this array have this key?" the expensive way:

in_array( $needle, array_keys( $array ), true )

That is O(n) in time and O(n) in memoryarray_keys() allocates a brand new packed array of every key on each call, purely so in_array() can walk it and throw it away. PHP already indexes arrays by key in a hash table, so the same question is answerable in O(1):

isset( $array[ $needle ] )

Changes

7 files, 9 call sites:

File Array Notes
wp-includes/media.php $_wp_additional_image_sizes image_constrain_size_for_editor() — runs per image, per size, on every editor/media request. Hottest of the nine.
wp-includes/block-editor.php $image_size_names Block editor settings assembly.
wp-includes/sitemaps.php $provider->get_object_subtypes() get_sitemap_url().
wp-includes/link-template.php $blogs get_dashboard_url() — every site a user belongs to, unbounded on large networks.
wp-admin/includes/dashboard.php get_blogs_of_user() Quick Draft widget; same unbounded network array.
wp-admin/includes/class-theme-installer-skin.php $all_themes Parent-theme validation.
wp-admin/nav-menus.php (×3) $dbids_to_orders Inside the menu-item loop, so the linear scan is multiplied by the number of menu items — effectively O(n²) on large menus.

In media.php the accompanying ! empty( $_wp_additional_image_sizes ) && guard is dropped as redundant — isset() on a key of an empty or undefined array is already false:

-	} elseif ( ! empty( $_wp_additional_image_sizes ) && in_array( $size, array_keys( $_wp_additional_image_sizes ), true ) ) {
+	} elseif ( isset( $_wp_additional_image_sizes[ $size ] ) ) {

Measurement

PHP 8.3.2, 200,000 iterations per case, key present at the last position (worst case for in_array) and absent:

Array size in_array(array_keys()) hit isset() hit in_array(array_keys()) miss isset() miss Speed-up
5 21.45 ms 7.61 ms 17.39 ms 6.88 ms 2.8×
30 59.07 ms 7.64 ms 41.47 ms 7.81 ms 7.7×
200 226.34 ms 7.23 ms 194.67 ms 6.84 ms 31×

isset() is flat at ~7 ms regardless of array size; the current form grows linearly. The per-call allocation is avoided entirely, which also takes pressure off the GC.

Behaviour notes

Two differences between the old and new expressions, neither of which is a regression at any of these call sites:

  1. isset() is false for a null value. Every array here stores arrays, objects, strings or ints as values — never null. (array_key_exists() would be the literal equivalent, but it is measurably slower than isset() and unnecessary here.)
  2. Numeric-string keys. PHP casts the integer-like key '2' to int 2 on insertion, so in_array( '2', array_keys( $a ), true ) returns false for a size registered as '2' while isset( $a['2'] ) correctly returns true. The new code is more correct; the old strict comparison was silently failing to match such keys. In nav-menus.php the needle is already explicitly cast with (int), so both forms agree.

Testing instructions

  1. vendor/bin/phpcs src/wp-includes/media.php src/wp-includes/block-editor.php src/wp-includes/sitemaps.php src/wp-includes/link-template.php src/wp-admin/includes/dashboard.php src/wp-admin/includes/class-theme-installer-skin.php src/wp-admin/nav-menus.php
  2. Confirm image sizes still resolve: insert an image in the editor at a size registered via add_image_size() and check the constrained dimensions are unchanged.
  3. On multisite, confirm get_dashboard_url() still returns the site dashboard for a user who belongs to the current site, and the user dashboard for one who does not.
  4. Save a nav menu with nested items and confirm parent/child relationships are preserved.

Verification done

  • php -l clean on all 7 files.
  • vendor/bin/phpcs: 0 errors. The 2 warnings emitted are pre-existing WordPress.DB.PreparedSQL.NotPrepared notices on unrelated lines (link-template.php:2030, media.php:5716).

@mukeshpanchal27 mukeshpanchal27 self-assigned this Jul 30, 2026
@mukeshpanchal27 mukeshpanchal27 changed the title Use isset() instead of in_array() over array_key() Media, Editor, Menus: Use isset() instead of in_array() over array_keys() Jul 30, 2026
@mukeshpanchal27
mukeshpanchal27 force-pushed the perf/in-array-array-keys branch from 977cc2a to ff06256 Compare August 7, 2026 05:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant