Theme Directory: Guard against repopackage posts missing _status meta#705
Theme Directory: Guard against repopackage posts missing _status meta#705obenland wants to merge 4 commits into
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
A hard failure between post creation and meta writes in import() can leave a repopackage post without _status/_ticket_id meta. WP_Post::__get() returns an empty string for missing meta, which fatals on PHP 8 where array_keys() and string offset access reject non-array values. Guards populate_post_with_meta(), create_or_update_trac_ticket(), and the themes API versions field so such posts no longer fatal the upload page or the API. No behavior change when the meta is a valid array. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RuMZkdkeAiLcsXjCXjVxpZ
62541b7 to
e127784
Compare
There was a problem hiding this comment.
Pull request overview
This PR hardens the Theme Directory upload and API code paths against repopackage posts that are missing the _status post meta (e.g., orphaned posts created during a failed import), preventing PHP 8 fatals and allowing the data to self-heal on subsequent uploads.
Changes:
- Guard
populate_post_with_meta()against missing post meta keys and missing/non-array_statuswhen derivingmax_version. - Make ticket lookup tolerant of missing
_ticket_idby null-coalescing to0before calling Trac. - Prevent the Themes API
versionsloop from callingarray_keys()on non-array_status.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| wordpress.org/public_html/wp-content/plugins/theme-directory/class-wporg-themes-upload.php | Adds defensive handling around missing meta keys and missing ticket IDs during upload/Ticket creation flow. |
| wordpress.org/public_html/wp-content/plugins/theme-directory/class-themes-api.php | Skips versions enumeration when _status meta is not an array to avoid PHP 8 fatals in API responses. |
Comments suppressed due to low confidence (1)
wordpress.org/public_html/wp-content/plugins/theme-directory/class-wporg-themes-upload.php:1303
create_or_update_trac_ticket()still directly indexes$this->theme_post->_status[ $this->theme_post->max_version ]without guarding that_statusis an array. If_statusmeta is missing,WP_Post::__get()returns an empty string and this line can still fatal on PHP 8 (Cannot access offset of type string on string), which undermines the stated goal of handling orphaned posts missing_status.
/*
* Trac reporter is always the authenticated user, unless it's not-auth'd in which case it's the Theme Author.
*
* This allows for Committers to upload Default themes under 'WordPress.org' but it still be to noted on Trac who uploaded it.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Adds a PHPUnit setup for the theme-directory plugin, mirroring the plugin-directory one: a wp-env test environment, a WordPress-backed test bootstrap, and a CI matrix entry. The initial tests cover populate_post_with_meta() and the themes API versions field for repopackage posts missing the _status post meta. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RuMZkdkeAiLcsXjCXjVxpZ
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RuMZkdkeAiLcsXjCXjVxpZ
When Trac ticket creation fails for a new upload, the freshly created repopackage post is deleted again to allow a clean re-upload. That deletion was caught by the fail-safe preventing repopackage deletions, which wp_die()s before the post is removed — leaving an orphaned post without versioned meta behind. Extracts the deletion into a delete_theme_post() method that detaches the fail-safe for the duration of the deletion and restores it after. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RuMZkdkeAiLcsXjCXjVxpZ
Problem
A
repopackagepost can exist without_statuspost meta:import()inclass-wporg-themes-upload.phpcreates the post but only writes_statusafter Trac ticket creation — a hard failure in between leaves an orphan. A live example exists: post 132199 (block-magazine, draft) has versioned meta but no_statusand no_ticket_id.For missing meta,
WP_Post::__get()returns''(empty string). Three places assume an array and fatal on PHP 8:populate_post_with_meta()—array_keys( $theme->_status )throwsTypeError: array_keys(): Argument #1 ($array) must be of type array, string given. This currently fatals the entire upload page for any theme with an orphaned post.create_or_update_trac_ticket()—$this->theme_post->_ticket_id[ $this->theme_post->max_version ]throwsTypeError: Cannot access offset of type string on stringwhen the meta is missing.class-themes-api.php—array_keys( get_post_meta( $theme->ID, '_status', true ) )would fatal the themes API for a published theme missing the meta.Changes
populate_post_with_meta(): castget_post_custom_keys()with(array)(it returns null for posts with no meta at all), and only callarray_keys()on_statuswhen it is an array. Note(array)casting the meta value itself would be wrong —(array) ''yieldsarray( '' ), creating a bogus version key.end()on the empty array yieldsfalseformax_version; downstream code already copes (! empty()guards,?? '', and thesvn importfallback inadd_to_svn()).create_or_update_trac_ticket(): null-coalesce the ticket ID lookup to0, soticket_get( 0 )falls into the existing branch that creates a fresh ticket.class-themes-api.php: skip theversionsloop when_statusis not an array;versionsremains an empty array in the response.No behavior change when
_statusis a valid array. Post 132199 is left as-is; it will self-heal on the author's next upload.Verification
php -lpasses on both files.php -rsanity checks on PHP 8.4:is_array( '' )guard yieldsarray(),end( array() )yieldsfalse,'' ['1.0.0'] ?? 0yields0without throwing (??uses isset semantics, which return false for illegal string offsets).🤖 Generated with Claude Code
https://claude.ai/code/session_01RuMZkdkeAiLcsXjCXjVxpZ