From 957e644aead3e75860901907996292137dd94d00 Mon Sep 17 00:00:00 2001 From: Torsten Landsiedel Date: Wed, 17 Jun 2026 14:43:53 +0200 Subject: [PATCH 1/4] Refactor wp_parse_url to use WHATWG URL parser --- src/wp-includes/http.php | 64 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/http.php b/src/wp-includes/http.php index fba9dd3bbaded..8b94103243ab5 100644 --- a/src/wp-includes/http.php +++ b/src/wp-includes/http.php @@ -670,20 +670,74 @@ function wp_parse_url( $url, $component = -1 ) { $to_unset = array(); $url = (string) $url; - if ( '//' === substr( $url, 0, 2 ) ) { + if ( str_starts_with( $url, '//' ) ) { $to_unset[] = 'scheme'; $url = 'placeholder:' . $url; - } elseif ( '/' === substr( $url, 0, 1 ) ) { + } elseif ( str_starts_with( $url, '/' ) ) { $to_unset[] = 'scheme'; $to_unset[] = 'host'; $url = 'placeholder://placeholder' . $url; } - $parts = parse_url( $url ); + /* + * PHP 8.5+: Prefer the WHATWG URL parser. + */ + if ( class_exists( 'Uri\\WhatWg\\Url' ) ) { + $parsed = \Uri\WhatWg\Url::parse( $url ); - if ( false === $parts ) { // Parsing failure. - return $parts; + if ( null === $parsed ) { + return false; + } + + $parts = array(); + + $scheme = $parsed->getScheme(); + if ( null !== $scheme ) { + $parts['scheme'] = $scheme; + } + + $host = $parsed->getAsciiHost(); + if ( null !== $host ) { + $parts['host'] = $host; + } + + $port = $parsed->getPort(); + if ( null !== $port ) { + $parts['port'] = $port; + } + + $user = $parsed->getUsername(); + if ( null !== $user ) { + $parts['user'] = $user; + } + + $pass = $parsed->getPassword(); + if ( null !== $pass ) { + $parts['pass'] = $pass; + } + + $path = $parsed->getPath(); + if ( '' !== $path ) { + $parts['path'] = $path; + } + + $query = $parsed->getQuery(); + if ( null !== $query ) { + $parts['query'] = $query; + } + + $fragment = $parsed->getFragment(); + if ( null !== $fragment ) { + $parts['fragment'] = $fragment; + } + } else { + $parts = parse_url( $url ); + + // Parsing failure. + if ( false === $parts ) { + return false; + } } // Remove the placeholder values. From a8f108d66f00f0a38a30701315d4e7dc1cbac1d4 Mon Sep 17 00:00:00 2001 From: Torsten Landsiedel Date: Wed, 17 Jun 2026 15:12:02 +0200 Subject: [PATCH 2/4] Refactor URL parsing to use WHATWG parser --- src/wp-includes/http.php | 61 ++++++++++++---------------------------- 1 file changed, 18 insertions(+), 43 deletions(-) diff --git a/src/wp-includes/http.php b/src/wp-includes/http.php index 8b94103243ab5..61c7033b4b29b 100644 --- a/src/wp-includes/http.php +++ b/src/wp-includes/http.php @@ -679,10 +679,7 @@ function wp_parse_url( $url, $component = -1 ) { $url = 'placeholder://placeholder' . $url; } - /* - * PHP 8.5+: Prefer the WHATWG URL parser. - */ - if ( class_exists( 'Uri\\WhatWg\\Url' ) ) { + if ( WP_PARSE_URL_USE_WHATWG ) { $parsed = \Uri\WhatWg\Url::parse( $url ); // Parsing failure. @@ -692,48 +689,26 @@ function wp_parse_url( $url, $component = -1 ) { $parts = array(); - $scheme = $parsed->getScheme(); - if ( null !== $scheme ) { - $parts['scheme'] = $scheme; - } - - $host = $parsed->getAsciiHost(); - if ( null !== $host ) { - $parts['host'] = $host; - } - - $port = $parsed->getPort(); - if ( null !== $port ) { - $parts['port'] = $port; - } - - $user = $parsed->getUsername(); - if ( null !== $user ) { - $parts['user'] = $user; - } - - $pass = $parsed->getPassword(); - if ( null !== $pass ) { - $parts['pass'] = $pass; - } - - $path = $parsed->getPath(); - if ( '' !== $path ) { - $parts['path'] = $path; - } - - $query = $parsed->getQuery(); - if ( null !== $query ) { - $parts['query'] = $query; - } - - $fragment = $parsed->getFragment(); - if ( null !== $fragment ) { - $parts['fragment'] = $fragment; + foreach ( + array( + 'scheme' => $parsed->getScheme(), + 'host' => $parsed->getAsciiHost(), + 'port' => $parsed->getPort(), + 'user' => $parsed->getUsername(), + 'pass' => $parsed->getPassword(), + 'path' => $parsed->getPath(), + 'query' => $parsed->getQuery(), + 'fragment' => $parsed->getFragment(), + ) + as $key => $value + ) { + if ( null !== $value && '' !== $value ) { + $parts[ $key ] = $value; + } } } else { $parts = parse_url( $url ); - + // Parsing failure. if ( false === $parts ) { return false; From 72d052fe45b99bf6470be8e8cdd389c62ffb7ac3 Mon Sep 17 00:00:00 2001 From: Torsten Landsiedel Date: Wed, 17 Jun 2026 15:14:09 +0200 Subject: [PATCH 3/4] Add WP_PARSE_URL_USE_WHATWG constant definition Define WP_PARSE_URL_USE_WHATWG constant based on PHP version. --- src/wp-includes/compat.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/wp-includes/compat.php b/src/wp-includes/compat.php index b95275455ceae..489732f07e24c 100644 --- a/src/wp-includes/compat.php +++ b/src/wp-includes/compat.php @@ -384,3 +384,10 @@ function is_iterable( $var ) { if ( ! defined( 'IMG_WEBP' ) ) { define( 'IMG_WEBP', IMAGETYPE_WEBP ); // phpcs:ignore PHPCompatibility.Constants.NewConstants.imagetype_webpFound } + +// Set WP_PARSE_URL_USE_WHATWG constant is available in PHP 8.5 or later. +if ( class_exists( 'Uri\\WhatWg\\Url', false ) ) { + define( 'WP_PARSE_URL_USE_WHATWG', true ); +} else { + define( 'WP_PARSE_URL_USE_WHATWG', false ); +} From 40b7086e2599b16ef538a8540897f92091b653b9 Mon Sep 17 00:00:00 2001 From: Torsten Landsiedel Date: Wed, 17 Jun 2026 22:28:49 +0200 Subject: [PATCH 4/4] Alternative implementation --- src/wp-includes/compat.php | 7 ---- src/wp-includes/http.php | 71 ++++++++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/wp-includes/compat.php b/src/wp-includes/compat.php index 489732f07e24c..b95275455ceae 100644 --- a/src/wp-includes/compat.php +++ b/src/wp-includes/compat.php @@ -384,10 +384,3 @@ function is_iterable( $var ) { if ( ! defined( 'IMG_WEBP' ) ) { define( 'IMG_WEBP', IMAGETYPE_WEBP ); // phpcs:ignore PHPCompatibility.Constants.NewConstants.imagetype_webpFound } - -// Set WP_PARSE_URL_USE_WHATWG constant is available in PHP 8.5 or later. -if ( class_exists( 'Uri\\WhatWg\\Url', false ) ) { - define( 'WP_PARSE_URL_USE_WHATWG', true ); -} else { - define( 'WP_PARSE_URL_USE_WHATWG', false ); -} diff --git a/src/wp-includes/http.php b/src/wp-includes/http.php index 61c7033b4b29b..d607d3c354bae 100644 --- a/src/wp-includes/http.php +++ b/src/wp-includes/http.php @@ -666,20 +666,23 @@ function ms_allowed_http_request_hosts( $is_external, $host ) { * doesn't exist in the given URL; a string or - in the case of * PHP_URL_PORT - integer when it does. See parse_url()'s return values. */ -function wp_parse_url( $url, $component = -1 ) { - $to_unset = array(); - $url = (string) $url; +if ( class_exists( 'Uri\\WhatWg\\Url', false ) ) { - if ( str_starts_with( $url, '//' ) ) { - $to_unset[] = 'scheme'; - $url = 'placeholder:' . $url; - } elseif ( str_starts_with( $url, '/' ) ) { - $to_unset[] = 'scheme'; - $to_unset[] = 'host'; - $url = 'placeholder://placeholder' . $url; - } + // WHATWG implementation (PHP 8.5+). + function wp_parse_url( $url, $component = -1 ) { + + $to_unset = array(); + $url = (string) $url; + + if ( str_starts_with( $url, '//' ) ) { + $to_unset[] = 'scheme'; + $url = 'placeholder:' . $url; + } elseif ( str_starts_with( $url, '/' ) ) { + $to_unset[] = 'scheme'; + $to_unset[] = 'host'; + $url = 'placeholder://placeholder' . $url; + } - if ( WP_PARSE_URL_USE_WHATWG ) { $parsed = \Uri\WhatWg\Url::parse( $url ); // Parsing failure. @@ -706,21 +709,45 @@ function wp_parse_url( $url, $component = -1 ) { $parts[ $key ] = $value; } } - } else { + + // Remove the placeholder values. + foreach ( $to_unset as $key ) { + unset( $parts[ $key ] ); + } + + return _get_component_from_parsed_url_array( $parts, $component ); + } +} else { + + // Legacy parse_url implementation. + function wp_parse_url( $url, $component = -1 ) { + + $to_unset = array(); + $url = (string) $url; + + if ( str_starts_with( $url, '//' ) ) { + $to_unset[] = 'scheme'; + $url = 'placeholder:' . $url; + } elseif ( str_starts_with( $url, '/' ) ) { + $to_unset[] = 'scheme'; + $to_unset[] = 'host'; + $url = 'placeholder://placeholder' . $url; + } + $parts = parse_url( $url ); - - // Parsing failure. + if ( false === $parts ) { - return false; + // Parsing failure. + return $parts; } - } - // Remove the placeholder values. - foreach ( $to_unset as $key ) { - unset( $parts[ $key ] ); - } + // Remove the placeholder values. + foreach ( $to_unset as $key ) { + unset( $parts[ $key ] ); + } - return _get_component_from_parsed_url_array( $parts, $component ); + return _get_component_from_parsed_url_array( $parts, $component ); + } } /**