Repository navigation
Customize: Keep stored custom CSS post ID when the lookup fails. - #14225
wpexplorer wants to merge 1 commit 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. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
|
Great to see this being picked up. The improvement to my original suggested fix in Trac ticket #54443 looks like exactly what's required. Would it be worth adding unit tests to cover the three scenarios: an existing custom CSS post being found, a lookup failure with a previously stored post ID, and a site with no custom CSS post? Looking forward to seeing this make it into WordPress Core! |
rejaulalomkhan
left a comment
There was a problem hiding this comment.
Thanks for working on this, @wpexplorer, and for writing up the production case on the ticket! Keeping a stored ID is a small, focused change, and it leaves the -1 cache in place for sites that never saved custom CSS.
I checked it in the wordpress-develop Docker env (PHP 8.3, MySQL 9.7) by forcing every query on wp_posts to fail during wp_get_custom_css_post():
| Scenario | Trunk | PR |
|---|---|---|
| Lookup fails with a stored ID | custom_css_post_id reset to -1 |
Stored ID kept ✅ |
| No custom CSS post, no stored ID | -1 cached |
-1 cached ✅ |
| Post deleted out of band (the trade-off from comment:9) | Self-heals to -1, then 1 query per call |
ID stays stale, 3 queries per call until CSS is saved again |
(The query counts are without a persistent object cache; I flushed the cache between calls.)
I left two notes inline: tests, and a question on the trade-off.
AI Review: Claude Opus 5.5
| * @todo This should get cleared if a custom_css post is added/removed. | ||
| */ | ||
| set_theme_mod( 'custom_css_post_id', $post ? $post->ID : -1 ); | ||
| if ( $post || empty( $post_id ) ) { |
There was a problem hiding this comment.
Should fix: This changes behaviour but has no test yet, as @webd-uk also asked. A DB failure is easy to simulate with the query filter. The test below fails on trunk (Failed asserting that -1 is identical to <ID>) and passes with this PR. I ran it locally, so it could go into tests/phpunit/tests/customize/custom-css-setting.php or a new tests/phpunit/tests/theme/wpGetCustomCssPost.php:
/**
* @ticket 54443
*
* @covers ::wp_get_custom_css_post
*/
public function test_stored_post_id_is_kept_when_lookup_fails() {
global $wpdb;
$post = wp_update_custom_css_post( 'body { color: red; }' );
$suppress = $wpdb->suppress_errors( true );
$break = static function ( $query ) use ( $wpdb ) {
return str_contains( $query, $wpdb->posts ) ? 'SELECT broken FROM missing_table' : $query;
};
add_filter( 'query', $break );
clean_post_cache( $post->ID );
$this->assertNull( wp_get_custom_css_post(), 'The lookup should fail.' );
remove_filter( 'query', $break );
$wpdb->suppress_errors( $suppress );
$this->assertSame( $post->ID, get_theme_mod( 'custom_css_post_id' ), 'The stored post ID should be kept.' );
}A second test for a site with no custom CSS post caching -1 would cover the other branch.
| */ | ||
| set_theme_mod( 'custom_css_post_id', $post ? $post->ID : -1 ); | ||
| if ( $post || empty( $post_id ) ) { | ||
| set_theme_mod( 'custom_css_post_id', $post ? $post->ID : -1 ); |
There was a problem hiding this comment.
Question: Would checking $wpdb->last_error right after the WP_Query work here instead? In the failure simulation above, last_error is set at that point. Skipping set_theme_mod() only when the query errored would protect the stored ID and still let a really deleted post fall back to -1, so there are no repeated lookups. I know @dlh found diagnosing DB errors here a bit out of scope in comment:3, so this is only an option for whoever commits it. The current approach is fine for the case in the ticket. Roughly (with global $wpdb; added to the function):
if ( ! $post && $wpdb->last_error ) {
// The lookup failed; keep the stored ID rather than caching "no post".
} else {
set_theme_mod( 'custom_css_post_id', $post ? $post->ID : -1 );
}There was a problem hiding this comment.
Just to add some real-world context to this trade-off.
I've encountered orphaned custom CSS posts dozens of times over the years across both my own and clients' WordPress installations. I can't 100% say every instance was caused by this particular bug, but it's certainly a recurring problem.
On the other hand, I've never encountered anyone manually deleting a custom CSS post directly from the database. Doing so bypasses WordPress's normal housekeeping anyway, so some resulting inconsistencies are to be expected.
Personally, I'd favour keeping the fix simple rather than introducing additional complexity to accommodate an edge case that, in my experience at least, is considerably less likely than the problem we're trying to solve.
There was a problem hiding this comment.
On the manually deleted case, I agree with @webd-uk. Deleting the custom_css post directly in the database skips WordPress's own housekeeping, so it's already "doing it wrong". Once the CSS is saved again it fixes itself, until then the cost is a few extra queries per request. I think that's a better trade-off than a site losing its CSS completely.
I originally thought about checking $wpdb->last_error first and decided against it for the following reasons:
- @dlh pointed out in comment:3 that diagnosing database errors in this function is out of scope, and his comment:6 points to the approach this PR takes instead.
- It doesn't catch every failure. If the connection drops and reconnecting fails after template_redirect (custom CSS loads on wp_head), wpdb::query() returns false without setting last_error. -1 would still be cached in that case.
- last_error only describes the most recent query. A plugin running its own query from posts_results or the_posts would overwrite it before the check.
Keeps a stored
custom_css_post_idwhen the custom CSS post lookup fails, instead of replacing it with-1. A failed lookup (e.g. during a database error) no longer hides an existingcustom_csspost or causes a duplicate to be created on the next save. Sites without custom CSS still cache-1.Trac ticket: https://core.trac.wordpress.org/ticket/54443
Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5.5
Used for: Investigating the issue on a production site and drafting the change; the final change was reviewed and edited by me.
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.