From dac3183848bee7852f5cf66dd7751613df4fc6c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 20:20:52 +0000 Subject: [PATCH 1/5] Initial plan From 398777c0452fd0b64e50c747052faf36cf7a8e06 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 20:27:14 +0000 Subject: [PATCH 2/5] Replace URLResolver library with PF_URL_Resolver - Created new PressForward\Libraries\PF_URL_Resolver class using wp_remote_head() - Updated LibrariesProvider to use new resolver - Removed mattwright/urlresolver dependency from composer.json - Updated HTTPTools to use new PF_URL_Resolver class - Updated MetaCheckEndpoint to remove unused url_resolver parameter Co-authored-by: boonebgorges <246627+boonebgorges@users.noreply.github.com> --- Controllers/HTTPTools.php | 12 ++-- Core/API/MetaCheckEndpoint.php | 19 ++---- Core/Providers/LibrariesProvider.php | 4 +- Libraries/PF_URL_Resolver.php | 93 ++++++++++++++++++++++++++++ composer.json | 1 - 5 files changed, 105 insertions(+), 24 deletions(-) create mode 100644 Libraries/PF_URL_Resolver.php diff --git a/Controllers/HTTPTools.php b/Controllers/HTTPTools.php index a6accc4e..72743a07 100644 --- a/Controllers/HTTPTools.php +++ b/Controllers/HTTPTools.php @@ -9,7 +9,7 @@ use Intraxia\Jaxion\Contract\Core\HasActions; use PressForward\Interfaces\System; -use mattwright\URLResolver; +use PressForward\Libraries\PF_URL_Resolver; /** * HTTP utilities. @@ -19,7 +19,7 @@ class HTTPTools implements HasActions { * URLResolver object. * * @access public - * @var \mattwright\URLResolver + * @var \PressForward\Libraries\PF_URL_Resolver */ public $url_resolver; @@ -42,11 +42,11 @@ class HTTPTools implements HasActions { /** * Constructor. * - * @param \mattwright\URLResolver $resolver URLResolver object. - * @param \PressForward\Interfaces\System $system System object. - * @param \PressForward\Controllers\Metas $meta Metas object. + * @param \PressForward\Libraries\PF_URL_Resolver $resolver URLResolver object. + * @param \PressForward\Interfaces\System $system System object. + * @param \PressForward\Controllers\Metas $meta Metas object. */ - public function __construct( URLResolver $resolver, System $system, Metas $meta ) { + public function __construct( PF_URL_Resolver $resolver, System $system, Metas $meta ) { $this->url_resolver = $resolver; $this->system = $system; $this->meta = $meta; diff --git a/Core/API/MetaCheckEndpoint.php b/Core/API/MetaCheckEndpoint.php index 9efa50ca..0cddf79e 100644 --- a/Core/API/MetaCheckEndpoint.php +++ b/Core/API/MetaCheckEndpoint.php @@ -10,7 +10,6 @@ use PressForward\Core\Admin\PFTemplater; use PressForward\Controllers\PF_JWT; use PFOpenGraph; -use mattwright\URLResolver; use WP_Error; @@ -42,29 +41,19 @@ class MetaCheckEndpoint implements \Intraxia\Jaxion\Contract\Core\HasActions { */ public $og; - /** - * URL Resolver object. - * - * @access public - * @var \mattwright\URLResolver - */ - public $url_resolver; - /** * Constructor. * - * @param array $api_base API base data. - * @param \PressForward\Controllers\PF_JWT $jwt PF_JWT object. - * @param PFOpenGraph $og PFOpenGraph object. - * @param \mattwright\URLResolver $url_resolver URLResolver object. + * @param array $api_base API base data. + * @param \PressForward\Controllers\PF_JWT $jwt PF_JWT object. + * @param PFOpenGraph $og PFOpenGraph object. */ - public function __construct( $api_base, PF_JWT $jwt, PFOpenGraph $og, URLResolver $url_resolver ) { + public function __construct( $api_base, PF_JWT $jwt, PFOpenGraph $og ) { $this->api_base = $api_base; $this->api_base['endpoint'] = 'metachecks'; $this->og = $og; $namespace = $this->api_base['base_namespace'] . $this->api_base['version']; $base = $this->api_base['endpoint']; - $this->url_resolver = $url_resolver; $this->jwt = $jwt; } diff --git a/Core/Providers/LibrariesProvider.php b/Core/Providers/LibrariesProvider.php index a932dc4b..bbd63bea 100644 --- a/Core/Providers/LibrariesProvider.php +++ b/Core/Providers/LibrariesProvider.php @@ -10,9 +10,9 @@ use Intraxia\Jaxion\Contract\Core\Container; use PressForward\Libraries\HTMLChecker; +use PressForward\Libraries\PF_URL_Resolver; use PFOpenGraph; use AlertBox\The_Alert_Box; -use mattwright\URLResolver; /** * LibrariesProvider class. @@ -27,7 +27,7 @@ public function register( Container $container ) { $container->define( 'library.url_resolver', function () { - return new URLResolver(); + return new PF_URL_Resolver(); } ); diff --git a/Libraries/PF_URL_Resolver.php b/Libraries/PF_URL_Resolver.php new file mode 100644 index 00000000..b6fa9627 --- /dev/null +++ b/Libraries/PF_URL_Resolver.php @@ -0,0 +1,93 @@ +final_url = $this->resolve( $url ); + return $this; + } + + /** + * Get the resolved URL. + * + * @return string The final resolved URL. + */ + public function getURL() { + return $this->final_url; + } + + /** + * Actually resolve the URL using wp_remote_head. + * + * Uses WordPress HTTP API to follow redirects and find the final URL. + * This is a simplified replacement for the abandoned mattwright/urlresolver + * library that uses wp_remote_head() instead of raw cURL. + * + * @param string $url The URL to resolve. + * @return string The final URL after following redirects. + */ + protected function resolve( $url ) { + // WordPress's wp_remote_head() automatically follows redirects up to + // the limit specified in the 'redirection' parameter. + $response = wp_remote_head( + $url, + array( + 'redirection' => 10, // Follow up to 10 redirects. + 'timeout' => 30, // 30 second timeout. + ) + ); + + // If there was an error, return the original URL. + if ( is_wp_error( $response ) ) { + return $url; + } + + // WordPress's HTTP API already follows redirects for us. The response + // array contains information about the final destination, but the + // actual final URL isn't directly available. However, we can retrieve + // it from the underlying HTTP object if available. + if ( isset( $response['http_response'] ) ) { + $http_response = $response['http_response']; + if ( is_a( $http_response, 'WP_HTTP_Requests_Response' ) ) { + // Get the final URL from the Requests response object. + $final_url = $http_response->get_response_object()->url; + if ( ! empty( $final_url ) ) { + return $final_url; + } + } + } + + // Fallback: if we can't get the final URL from the response object, + // return the original URL. WordPress has already followed all redirects, + // so the original URL is effectively the final URL. + return $url; + } +} diff --git a/composer.json b/composer.json index b9bb8717..4e1729bc 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,6 @@ "firebase/php-jwt": "^6.11", "paragonie/random_compat": "^9.99", "davechild/textstatistics": "^1.0", - "mattwright/urlresolver": "^2.0", "fivefilters/readability.php": "~3.0.0", "psr/log": "^1.0" } From 055c7ee9135a2136abdde85628f6481565e4c50c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 20:31:28 +0000 Subject: [PATCH 3/5] Improve PF_URL_Resolver to manually follow redirects Updated the resolve() method to manually follow redirects instead of relying on WordPress auto-redirect, which allows us to properly track the final URL destination. Co-authored-by: boonebgorges <246627+boonebgorges@users.noreply.github.com> --- Libraries/PF_URL_Resolver.php | 75 ++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/Libraries/PF_URL_Resolver.php b/Libraries/PF_URL_Resolver.php index b6fa9627..9506cea0 100644 --- a/Libraries/PF_URL_Resolver.php +++ b/Libraries/PF_URL_Resolver.php @@ -51,43 +51,62 @@ public function getURL() { * This is a simplified replacement for the abandoned mattwright/urlresolver * library that uses wp_remote_head() instead of raw cURL. * + * Since WordPress's wp_remote_head() doesn't easily expose the final URL + * after following redirects, we manually follow them by checking Location + * headers. + * * @param string $url The URL to resolve. * @return string The final URL after following redirects. */ protected function resolve( $url ) { - // WordPress's wp_remote_head() automatically follows redirects up to - // the limit specified in the 'redirection' parameter. - $response = wp_remote_head( - $url, - array( - 'redirection' => 10, // Follow up to 10 redirects. - 'timeout' => 30, // 30 second timeout. - ) - ); + $max_redirects = 10; + $current_url = $url; - // If there was an error, return the original URL. - if ( is_wp_error( $response ) ) { - return $url; - } + for ( $i = 0; $i < $max_redirects; $i++ ) { + // Make a HEAD request but don't auto-follow redirects. + // We want to manually follow them so we can track the final URL. + $response = wp_remote_head( + $current_url, + array( + 'redirection' => 0, // Don't auto-follow redirects. + 'timeout' => 30, // 30 second timeout. + ) + ); + + // If there was an error, return the current URL. + if ( is_wp_error( $response ) ) { + return $current_url; + } + + $response_code = wp_remote_retrieve_response_code( $response ); - // WordPress's HTTP API already follows redirects for us. The response - // array contains information about the final destination, but the - // actual final URL isn't directly available. However, we can retrieve - // it from the underlying HTTP object if available. - if ( isset( $response['http_response'] ) ) { - $http_response = $response['http_response']; - if ( is_a( $http_response, 'WP_HTTP_Requests_Response' ) ) { - // Get the final URL from the Requests response object. - $final_url = $http_response->get_response_object()->url; - if ( ! empty( $final_url ) ) { - return $final_url; + // If we got a redirect status code (301, 302, 303, 307, 308), + // follow the Location header. + if ( in_array( $response_code, array( 301, 302, 303, 307, 308 ), true ) ) { + $location = wp_remote_retrieve_header( $response, 'location' ); + + if ( empty( $location ) ) { + // Redirect without Location header - return current URL. + return $current_url; + } + + // Handle relative URLs. + if ( 0 === strpos( $location, '/' ) ) { + $parsed_url = wp_parse_url( $current_url ); + $scheme = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] : 'http'; + $host = isset( $parsed_url['host'] ) ? $parsed_url['host'] : ''; + $location = $scheme . '://' . $host . $location; } + + // Update current URL and continue loop. + $current_url = $location; + } else { + // Not a redirect - we've reached the final URL. + return $current_url; } } - // Fallback: if we can't get the final URL from the response object, - // return the original URL. WordPress has already followed all redirects, - // so the original URL is effectively the final URL. - return $url; + // If we've exhausted max redirects, return the current URL. + return $current_url; } } From 7921190887ee5e8566b186633660ef66432686e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 20:32:26 +0000 Subject: [PATCH 4/5] Use null coalescing operator in PF_URL_Resolver Replaced isset() ternary with ?? operator for cleaner code. Co-authored-by: boonebgorges <246627+boonebgorges@users.noreply.github.com> --- Libraries/PF_URL_Resolver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Libraries/PF_URL_Resolver.php b/Libraries/PF_URL_Resolver.php index 9506cea0..90e97195 100644 --- a/Libraries/PF_URL_Resolver.php +++ b/Libraries/PF_URL_Resolver.php @@ -93,8 +93,8 @@ protected function resolve( $url ) { // Handle relative URLs. if ( 0 === strpos( $location, '/' ) ) { $parsed_url = wp_parse_url( $current_url ); - $scheme = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] : 'http'; - $host = isset( $parsed_url['host'] ) ? $parsed_url['host'] : ''; + $scheme = $parsed_url['scheme'] ?? 'http'; + $host = $parsed_url['host'] ?? ''; $location = $scheme . '://' . $host . $location; } From 47017c533a9a5de0a32478339490c9e3dd0b6689 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 20:33:31 +0000 Subject: [PATCH 5/5] Remove unnecessary curl extension check in HTTPTools Since we're now using WordPress's HTTP API (wp_remote_head) instead of raw cURL, the curl extension check is no longer necessary. WordPress's HTTP API can use multiple transports (curl, streams, fsockopen). Co-authored-by: boonebgorges <246627+boonebgorges@users.noreply.github.com> --- Controllers/HTTPTools.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Controllers/HTTPTools.php b/Controllers/HTTPTools.php index 72743a07..baadc973 100644 --- a/Controllers/HTTPTools.php +++ b/Controllers/HTTPTools.php @@ -122,7 +122,7 @@ public function resolve_a_url( $url ) { return $url; } else { $check = $this->url_is_aggregation_service( $url ); - if ( $check && in_array( 'curl', get_loaded_extensions(), true ) ) { + if ( $check ) { $url = $this->url_resolver->resolveURL( $url )->getURL(); } }