Make WordPress Core

Opened 7 weeks ago

Last modified 6 weeks ago

#64721 assigned defect (bug)

menu-header.php uses relative file_exists() causing wrong submenu URL when PHP CWD is not wp-admin/

Reported by: paoltaia's profile paoltaia Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 6.9.1
Component: Administration Keywords: has-patch 2nd-opinion close
Focuses: Cc:

Description

What Happens

When a plugin registers a submenu page under a Custom Post Type menu
(e.g. edit.php?post_type=gd_place), clicking that submenu item generates
a broken URL like:

admin.php?page=gd_place-settings

...instead of the correct:

edit.php?post_type=gd_place&page=gd_place-settings

Why It Happens

In wp-admin/menu-header.php, around line 267, WordPress checks:

file_exists( $menu_file )

...where $menu_file is a bare relative path like 'edit.php'.

The file_exists() call resolves against PHP's current working directory (CWD).
On standard Apache/Nginx servers, PHP's CWD is the wp-admin/ folder, so
file_exists('edit.php') returns TRUE and the correct URL is built.

But in WordPress Studio (and any environment where PHP's CWD is the WordPress
root instead of wp-admin/), file_exists('edit.php') returns FALSE, so
WordPress falls back to building admin.php?page=... — dropping the post_type
parameter entirely.

The Fix

The check should use an absolute path:

file_exists( ABSPATH . 'wp-admin/' . $menu_file )

This would work correctly regardless of what the PHP CWD happens to be.

Steps to Reproduce

  1. Install WordPress using WordPress Studio (local dev tool)
  2. Install a plugin that registers a submenu under a CPT menu (e.g. GeoDirectory)
  3. Click the submenu item in wp-admin
  4. Observe the URL: it says admin.php?page=... instead of edit.php?post_type=...&page=...

Expected vs Actual

  • Expected: edit.php?post_type=gd_place&page=gd_place-settings
  • Actual: admin.php?page=gd_place-settings

Environment

  • WordPress version: 6.9.1
  • PHP server: WordPress Studio (bundled server, CWD = WP root)
  • Works fine on: Apache/Nginx hosting, Local by WP Engine
  • Affected file: wp-admin/menu-header.php ~line 267

Change History (5)

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


7 weeks ago
#1

This PR is not tested. I simply applied the suggestion from the reporter. This PR is for testers to validate.

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

## Use of AI Tools

n/a

#2 @westonruter
7 weeks ago

  • Keywords needs-testing 2nd-opinion added

I opened a PR with the suggestion applied: https://github.com/WordPress/wordpress-develop/pull/11054

Nevertheless, would this not be a bug with Studio itself, or rather Playground?

#3 @ozgursar
7 weeks ago

  • Keywords needs-testing removed

Patch Testing Report

Patch Tested: https://github.com/WordPress/wordpress-develop/pull/11054

Environment

  • WordPress: 7.0-beta1-20260225.232239
  • PHP: 8.3.30
  • Server: PHP.wasm
  • Database: WP_SQLite_Driver (Server: 8.0.38 / Client: 3.51.0)
  • Browser: Chrome 145.0.0.0
  • OS: macOS
  • Theme: Twenty Twenty-Five 1.4
  • MU Plugins:
    • GeoDirectory Fast AJAX 1.0.0
  • Plugins:
    • GeoDirectory 2.8.152
    • Test Reports 1.2.1

Steps taken

  1. Install GeoDirectory to latest trunk via WordPress Playground

https://playground.wordpress.net/?php=8.3&wp=trunk&plugin=geodirectory

  1. Check the Places > Settings submenu page URL
  2. Confirm it links to /wp-admin/admin.php?page=gd_place-settings
  3. Apply patch via Playground link below:

https://playground.wordpress.net/?php=8.3&wp=trunk&core-pr=11054&plugin=geodirectory

  1. Check the Places > Settings submenu page URL
  2. Confirm it links to /wp-admin/edit.php?post_type=gd_place&page=gd_place-settings
  3. ✅ Patch is solving the problem

Expected result

  • URL correctly points to edit.php instead of admin.php and retains the post_type query parameter.

Screenshots/Screencast with results

Before patch
https://i.imgur.com/MtDaZOf.png

After patch
https://i.imgur.com/Q4eFQVI.png

Last edited 7 weeks ago by ozgursar (previous) (diff)

#4 @berislav.grgicak
6 weeks ago

Playground defaults to the PHP CLI SAPI behavior, which doesn't change the current working directory. To match native PHP, Playground needs to set the CWD to the script directory for the WEB SAPI and keep the root directory for the CLI SAPI.

I'm working on a fix in Playground.

#5 @westonruter
6 weeks ago

  • Keywords close added

It seems that since the issue will be fixed in Playground, that this doesn't need to be fixed in core.

Note: See TracTickets for help on using tickets.