Make WordPress Core

Opened 5 months ago

Closed 6 days ago

#64087 closed defect (bug) (maybelater)

Deprecated urlencode() passing null warning during load-styles.php because get_stylesheet_directory() returns empty

Reported by: jerclarke's profile jerclarke Owned by:
Milestone: Priority: normal
Severity: minor Version:
Component: Script Loader Keywords: php8 reporter-feedback
Focuses: administration, performance, php-compatibility Cc:

Description

I'm getting the following in my PHP error logs (nginx+php 8.3):

[08-Oct-2025 19:06:54] PHP deprecated: "urlencode(): Passing null to parameter #1 ($string) of type string is deprecated" file: [...]/wp-includes/script-loader.php:1680 url: example.com/wp-admin/load-styles.php?c=0&dir=ltr&load%5Bchunk_0%5D=dashicons,admin-bar,buttons,media-views,common,forms,admin-menu,dashboard,list-tables,edit,revisions,media,themes,about,nav-menu&load%5Bchunk_1%5D=s,wp-pointer,widgets,site-icon,l10n,wp-auth-check,wp-color-picker&ver=6.8.3 
[08-Oct-2025 19:06:54] PHP deprecated: "file_exists(): Passing null to parameter #1 ($filename) of type string is deprecated" file: [...]/wp-includes/global-styles-and-settings.php:429 url: example.com/wp-admin/load-styles.php?c=0&dir=ltr&load%5Bchunk_0%5D=dashicons,admin-bar,buttons,media-views,common,forms,admin-menu,dashboard,list-tables,edit,revisions,media,themes,about,nav-menu&load%5Bchunk_1%5D=s,wp-pointer,widgets,site-icon,l10n,wp-auth-check,wp-color-picker&ver=6.8.3 

They always come together and seem to be related, though this ticket is specifically about the first one because I've narrowed it down to something specific.

First, the deprecation warning is clearly a new thing in PHP 8.x, so it makes sense this was never noticed before.

As you can see in the error, it happens when loading load-styles.php. The line with the error is in wp_theme_has_theme_json():

	$theme_has_support[ $stylesheet ] = file_exists( $path );

Makes sense that if $path is empty, deprecation warning will show.

Using Xdebug I determined that the reason it's empty is that it uses get_stylesheet(), get_stylesheet_directory(), and get_template_directory(), all of which return null in the context of load-styles.php.

If I debug wp_theme_has_theme_json() on a wp-admin screen, those functions return strings and a $path is created, but in load-styles.php they come up empty and the deprecation warning is the result.

I tested this with a totally vanilla copy of WP using twenty-twentyfive and had the same results.

Note that the errors don't show on my local Valet+nginx installation, they only arrive in the logs of my live server for some reason. That said, Xdebug confirms that the problem exists locally as well, albeit silently.

FWIW it doesn't seem like this actually breaks anything, I only noticed it because it fills up my server's error logs (effectively notifying me when a user has logged in with a cold CSS cache). It seems plausible to me that the "failure" of get_stylesheet() etc. is a long-time accepted state of affairs, since it doesn't hurt anything as the theme doesn't affect these scripts.

Still, I think it should be fixed one way or another, whether making those stylesheet functions work in load-styles.php, or just fixing wp_theme_has_theme_json() to not call file_exists() if $path is empty.

Thanks to anyone who can look into this and confirm or deny my conclusion that it is indeed an issue that affects all WP installs.

Change History (12)

#1 @jerclarke
5 months ago

For now my temporary fix will be to use the following in wp-config.php:

define( 'CONCATENATE_SCRIPTS', false );

This silences both of the errors above because it disables the entire load-scripts.php system and thus these two bugs don't surface.

Obviously this isn't a good solution, because I miss out on the intended performance benefits of concatenating and minifying the scripts. At the same time, I tested the load speed in dev tools and the difference was minimal in 2025 Chrome, more or less irrelevant in the random differences between loads. As I read elsewhere where people are struggling with this issue, the whole premise of concatenating scripts is a lot less relevant now than it was a decade ago when systems like this were added.

Really hoping this is something that can be fixed in core, of course. Clean logs are priceless for finding actual bugs, after all.

#2 @westonruter
3 months ago

  • Keywords php8 added; 2nd-opinion removed
  • Milestone changed from Awaiting Review to 7.0

Milestoning this for 7.0 because we may remove concatenation in #57548.

#3 @westonruter
3 months ago

  • Version 6.8.3 deleted

This ticket was mentioned in Slack in #core-performance by westonruter. View the logs.


9 days ago

#5 @westonruter
9 days ago

  • Keywords reporter-feedback added

@jerclarke Hey, it seems there was a duplicate opened of this in #64620. Can you check of if the associated PR fixes the issue for you?

#6 @jerclarke
8 days ago

@westonruter Thanks for asking but the patch doesn't seem to have any effect, applied it to the live site and the log is still full of errors when I visit /wp-admin/load-styles.php

The part of my bug I tracked down is in global-styles-and-settings.php:wp_theme_has_theme_json():

$theme_has_support[ $stylesheet ] = file_exists( $path );

Because $path ends up not empty, but null, on load-styles.php.

If we just wanted to silence the notice on my bug, which is just about passing null rather than string to file_exists(), I think the following would work:

	if ( ! is_string( $path ) {
		$path = "";
	}
	$theme_has_support[ $stylesheet ] = file_exists( $path );

I don't submit that (or file_exists( (string) $path)) as solutions because it strikes me that the function should instead return earlier or something if all the variables are null.

Last edited 8 days ago by jerclarke (previous) (diff)

#7 @westonruter
8 days ago

I'm testing with PHP 8.3 and Nginx with the Twenty Twenty-Five theme active.

I attempt to access /wp-admin/load-styles.php?c=0&dir=ltr&load%5Bchunk_0%5D=dashicons,admin-bar,buttons,media-views,common,forms,admin-menu,dashboard,list-tables,edit,revisions,media,themes,about,nav-menu&load%5Bchunk_1%5D=s,wp-pointer,widgets,site-icon,l10n,wp-auth-check,wp-color-picker&ver=6.8.3

Unfortunately, I'm not seeing anything in the logs. I'm even testing in WP 6.8.3.

For reference:

For the first, when I try logging out $font_family I see "Noto Serif:400,400i,700,700i".
For the second, when I try logging out $path I see: "wp-content/themes/twentytwentyfive/theme.json".

This is when I access load-styles.php. So it is not triggering the same issues as you.

#8 follow-up: @jerclarke
8 days ago

Thanks for testing it! FWIW like I said I can't replicate the log message on my local site, only on the server.

What I can replicate everywhere (custom theme+plugins, no plugins with twentytwentyfive) is the fact that when I visit wp-admin/load-styles.php the $path is empty and so is the $font_family, which explains why the error is thrown on the live server/

I tried both empty wp-admin/load-styles.php and your more realistic one with all the URL variables, same result in terms of the null variables (though of course the long version renders actual styles unlike the plain version).

Also confirmed with 6.9.1.

Very strange. There's no way to view logs in playgrounds, is there? It's hard to have more platforms where I can test this with a debugger, maybe it's something in common between my local mac running Valet and our Ubuntu server, but it's kind of hard to imagine.

I guess if you want to close this you can. I thought it would be easy to reproduce on other systems because it's so reliable across the ones I'm using.

#9 in reply to: ↑ 8 @westonruter
8 days ago

Replying to jerclarke:

Very strange. There's no way to view logs in playgrounds, is there? It's hard to have more platforms where I can test this with a debugger, maybe it's something in common between my local mac running Valet and our Ubuntu server, but it's kind of hard to imagine.

Yes, there is an easy way to view logs in Playground: https://wordpress.github.io/wordpress-playground/blueprints/troubleshoot-and-debug/#log-your-own-error-messages

#10 follow-up: @jerclarke
7 days ago

Thanks for the tip about Playgrounds logging. I managed to see errors in the logs, but it was VERY inconsistent and unreliable, so I ended up using a completely fresh and default LocalWP installation to debug this outside my Varnish/live environment.

I figured out why the errors don't normally show, load-styles.php and load-scripts.php both call error_reporting(0) at the top of the file!

/*
 * The error_reporting() function can be disabled in php.ini. On systems where that is the case,
 * it's best to add a dummy function to the wp-config.php file, but as this call to the function
 * is run prior to wp-config.php loading, it is wrapped in a function_exists() check.
 */
if ( function_exists( 'error_reporting' ) ) {
	/*
	 * Disable error reporting.
	 *
	 * Set this to error_reporting( -1 ) for debugging.
	 */
	error_reporting( 0 );
}

Aside from the fact that that is cringe AF, it explains a lot. When I disable that line I suddenly get both of the errors I reported at the top.

The reason it showed up on the live server is we have a custom error handler that doesn't respect error_reporting(), so I guess it's exposing WP's dirty little secret here.

Am I being unreasonable to say that WP should work, without errors, even if error_reporting is forced on?

I figured out these lines to insert in wp_theme_has_theme_json() before the file_exists() call that I think clarify what's going on:

	error_reporting(E_ALL);
	error_log( "LOADING: " . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
	error_log( 'BEFORE filter $path');
	error_log('$path [gettype] ' . gettype($path) . ' [value] ' .  $path);

	// $theme_has_support[ $stylesheet ] = file_exists( $path );

	/** This filter is documented in wp-includes/link-template.php */
	$path = apply_filters( 'theme_file_path', $path, 'theme.json' );

	error_log('AFTER filtering $path');
	error_log('$path [gettype] ' . gettype($path) . ' [value] ' .  $path);

	$theme_has_support[ $stylesheet ] = file_exists( $path );

Resulting log:

[26-Feb-2026 17:25:22 UTC] LOADING: gv.test/wp-admin/load-styles.php 
[26-Feb-2026 17:25:22 UTC] BEFORE filter $path 
[26-Feb-2026 17:25:22 UTC] $path [gettype] string [value] /theme.json 
[26-Feb-2026 17:25:22 UTC] AFTER filtering $path 
[26-Feb-2026 17:25:22 UTC] $path [gettype] NULL [value]  
[26-Feb-2026 17:25:22 UTC] PHP Deprecated:  file_exists(): Passing null to parameter #1 ($filename) of type string is deprecated in /{site}/wp-includes/global-styles-and-settings.php on line 440 

So it $path starts as /theme.json but ends up as null after the filter, thus triggering an error, IF error reporting is enabled, of course.

I didn't dig into WHY that filter returns null, but now that I know that errors are expected during load-styles.php, I'm disinclined to spend more time on it.

Honestly I'm not sure what to do at this point, it seems like this corner of WordPress is just "errors allowed" and I should assume a resolution of wontfix.

More than anything it makes me think of your earlier comment:

Milestoning this for 7.0 because we may remove concatenation in #57548.

This whole system should probably be burned with fire, TBH. Glad I turned it off on my actual sites 😅

#11 in reply to: ↑ 10 @westonruter
6 days ago

Replying to jerclarke:

I didn't dig into WHY that filter returns null, but now that I know that errors are expected during load-styles.php, I'm disinclined to spend more time on it.

I think it's because load-scripts.php and load-styles.php load noop.php in which apply_filters() is defined as:

<?php
function apply_filters() {}

So that explains why null is returned when applying the filters 😬

Last edited 6 days ago by westonruter (previous) (diff)

#12 @westonruter
6 days ago

  • Milestone 7.0 deleted
  • Resolution set to maybelater
  • Status changed from new to closed

@jerclarke Thank you for your investigation here. I'll close this as maybelater since it's not strictly a duplicate of #57548 but at the same time, we might fix the issue later.

Note: See TracTickets for help on using tickets.