Query: Match page and post paths that begin with a slash - #12614
Query: Match page and post paths that begin with a slash#12614thisismyurl wants to merge 1 commit into
Conversation
WP_Query::is_page() and WP_Query::is_single() guard their path check with ! strpos( $path, '/' ). strpos() returns 0 when the slash is the first character, so ! strpos() evaluates to true for a path such as '/foo/bar' and the path is skipped without ever being checked. get_page_by_path(), which performs the actual lookup, trims leading and trailing slashes, so a leading-slash path is input it already accepts, and the parameter is documented as accepting a path. The result is that is_page( '/foo/bar' ) returns false on the very page it names. Use str_contains() instead. Behaviour is identical for every other input; only a path whose first character is a slash changes, from silently skipped to correctly checked. Props thisismyurl. See #65671.
|
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. |
There was a problem hiding this comment.
Pull request overview
Fixes a bug in WP_Query::is_page() and WP_Query::is_single() where paths beginning with / (e.g. '/foo/bar') were incorrectly skipped due to ! strpos(...) treating a 0 return value as falsy. The change uses str_contains() (already used elsewhere in class-wp-query.php and polyfilled in core) so leading-slash paths are evaluated correctly, and adds regression tests for both conditionals.
Changes:
- Replace
! strpos( $path, '/' )guards with! str_contains( $path, '/' )inWP_Query::is_page()andWP_Query::is_single(). - Add PHPUnit regression tests confirming leading-slash paths (and the existing slashless variants) match as expected for hierarchical posts and pages.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/wp-includes/class-wp-query.php |
Fixes conditional path handling by switching to str_contains() so leading-slash paths are no longer skipped. |
tests/phpunit/tests/query/conditionals.php |
Adds regression tests ensuring is_single() / is_page() match hierarchical paths with and without a leading slash. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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. |
WP_Query::is_page()andWP_Query::is_single()document their parameter as accepting a path, and guard the path branch with:strpos()returns0when the slash is the first character, so! strpos()istruefor a path like/foo/barand the path is skipped without ever being checked.get_page_by_path(), which does the actual lookup, trims leading and trailing slashes, so that is input it already accepts. The result is thatis_page( '/foo/bar' )returnsfalseon the very page it names, silently.This switches both guards to
str_contains(), which is polyfilled in core for PHP < 8.0 and already used elsewhere inclass-wp-query.php.Behaviour is identical for every other input. The affected set is any path whose first character is a slash, which is worth stating plainly because it is broader than the nested case that prompted the ticket: a single-segment path like
/aboutalso starts matching, sinceget_page_by_path()trims it toaboutand resolves it correctly.aboutfoo/barfoo/bar//foo/bar/aboutThe degenerate inputs
/and//now reachget_page_by_path(), which trims them to an empty string and finds nothing. The result is stillfalse, at the cost of one query that previously short-circuited. I mention it for completeness rather than because it changes anything observable.Two regression tests sit beside the existing
test_is_page_with_parent()andtest_is_single_with_parent()and reuse their fixtures, one per changed line, each asserting both the slashless and leading-slash forms. Both fail on trunk and pass with the change.One disclosure on verification: this box has no Docker/
wp-env, so I could not run the full PHPUnit suite locally. I confirmed the behaviour difference with a standalone harness across the inputs in the table above, checked thatget_page_by_path()trims the leading slash and that nothing upstream normalises it first, and ranphp -lplus PHPCS (WordPress-Core, 0 errors on both changed files, no new warnings on the changed lines). I would appreciate a CI run confirming the tests.Trac ticket: https://core.trac.wordpress.org/ticket/65671
Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 4.8
Used for: Helping locate the faulty guard and cross-check it against
get_page_by_path()and the documented parameter. I reviewed the change and take responsibility for the final code.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.