#65441 closed defect (bug) (duplicate)
Formatting: esc_url() triggers PHP 8.1 deprecation when passed null
| Reported by: | dervishov | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | |
| Component: | Formatting | Version: | |
| Severity: | normal | Keywords: | has-patch has-unit-tests php81 |
| Cc: | Focuses: |
Description (last modified by )
esc_url() triggers a PHP 8.1+ deprecation notice when null is passed as the $url parameter:
Deprecated: ltrim(): Passing null to parameter #1 ($string) of type string is deprecated in wp-includes/formatting.php on line 4545
Problem
In esc_url(), the function checks for an empty string on line 4541:
if ( '' === $url ) { return $url; }
However, this does not account for null. When null passes through, it reaches line 4545:
$url = str_replace( ' ', '%20', ltrim( $url ) );
Since PHP 8.1, passing null to ltrim() (which expects a string parameter) triggers an E_DEPRECATED notice. On PHP 8.2+, this will become a TypeError fatal error, breaking sites entirely.
Why this matters
esc_url() is one of the most heavily used escaping functions in WordPress. It is called thousands of times across core, themes, and plugins. Multiple core functions that are commonly passed to esc_url() can return null:
get_edit_post_link()— returnsnullwhen the post type has no editing UI, the post doesn't exist, or the user lacksedit_postcapability. Used in:wp-admin/includes/meta-boxes.php:275wp-admin/edit-form-comment.php:186wp-admin/comment.php:207wp-admin/edit.php:464wp-admin/async-upload.php:68
get_edit_comment_link()— returnsnullwhen the comment doesn't exist or user lacksedit_commentcapabilityget_edit_bookmark_link()— returnsnullwhen user lacksmanage_linkscapabilityget_previous_posts_page_link()— returnsnullon the first page (fixed individually in [62034], but the root cause inesc_url()was not addressed)
Beyond core, thousands of third-party plugins and themes call esc_url() with values that may be null. Hardening esc_url() itself provides defense in depth for the entire ecosystem.
Proposed fix
Add null === $url to the existing empty-value check:
if ( '' === $url || null === $url ) { return ''; }
This:
- Matches the pre-PHP 8.1 behavior exactly (where
ltrim(null)silently returned'', andesc_url(null)returned'') - Introduces no behavioral change for any other input type
- Protects all current and future callers without requiring individual null guards at each call site
- Follows the same defensive pattern applied in [62034] for
previous_posts()
Tests included
Two new unit tests added to tests/phpunit/tests/formatting/escUrl.php:
test_null_input_returns_empty_string— verifiesesc_url( null )returns''test_sanitize_url_null_input_returns_empty_string— verifiessanitize_url( null )returns''(sincesanitize_url()callsesc_url()internally)
Full PHPUnit suite passes: 29,457 tests, 0 failures.
PHPCS passes with no violations.
References
- Follow-up to [62034] which fixed the same deprecation pattern in
previous_posts() - PHP 8.1 migration guide: https://www.php.net/manual/en/migration81.deprecated.php (Internal functions non-nullable parameters)
- GitHub PR: https://github.com/WordPress/wordpress-develop/pull/12132
Change History (3)
This ticket was mentioned in PR #12132 on WordPress/wordpress-develop by @dervishov.
2 months ago
#2
## Summary
Fix a PHP 8.1+ deprecation notice in esc_url() when null is passed as the $url parameter.
On PHP 8.1+, passing null to esc_url() triggers:
Deprecated: ltrim(): Passing null to parameter #1 ($string) of type string is deprecated
This occurs because esc_url() calls ltrim( $url ) on line 4545 without first checking for null. Multiple core functions that return string|null (such as get_edit_post_link(), get_edit_comment_link(), get_edit_bookmark_link()) have their return values passed directly to esc_url() throughout the codebase.
## Fix
Add null === $url to the existing empty check, returning an empty string. This matches the pre-PHP 8.1 behavior where ltrim(null) silently returned ''.
Follow-up to [r62034] which fixed the same pattern in previous_posts().
## Tests
test_null_input_returns_empty_string— verifiesesc_url( null )returns''test_sanitize_url_null_input_returns_empty_string— verifiessanitize_url( null )returns''
#3
@
2 months ago
- Description modified (diff)
- Keywords close removed
- Milestone Awaiting Review
- Resolution → duplicate
- Status new → closed
- Version trunk
This is already reported on #61268.
The esc_url() function expects a string, and developers who misuse functions should be notified. When esc_url() returns an empty string, the markup could have an empty href="" instead of a usable target URL. #61268 also has an example of plugins that use an echoing function inside esc_url(), which then prints the output without escaping anything.
On the other hand, passing a null value was already silenced, in older PHP versions and/or before [47808] used strict comparison.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
Since null is not a string, passing null should give a warning. Removing that is not hardening anything, it's the opposite - it's hiding developer errors.