Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
Url
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 3
272
0.00% covered (danger)
0.00%
0 / 1
 normalize
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 get_current_url
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
110
 normalize_query_args
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2/**
3 * Implement the URL normalizer.
4 *
5 * @package automattic/jetpack-boost-core
6 */
7
8namespace Automattic\Jetpack\Boost_Core\Lib;
9
10/**
11 * Class Url
12 */
13class Url {
14
15    const PARAMS_TO_EXCLUDE   = array( 'utm_campaign', 'utm_medium', 'utm_source', 'utm_content', 'fbclid', '_ga', 'jb-disable-modules' );
16    const PARAMS_TO_NORMALIZE = array( 's' => '' );
17
18    /**
19     * Normalize a URL - right now, just make sure it's absolute.
20     *
21     * @param string $url The URL.
22     *
23     * @return string
24     */
25    public static function normalize( $url ) {
26        $url = self::normalize_query_args( $url );
27        if ( '/' === $url[0] ) {
28            $url = site_url( $url );
29        }
30
31        return rtrim( $url, '/' );
32    }
33
34    /**
35     * Returns the current URL.
36     *
37     * @return string
38     */
39    public static function get_current_url() {
40        // Fallback to the site URL if we're unable to determine the URL from $_SERVER global.
41        $current_url = site_url();
42
43        if ( isset( $_SERVER ) && is_array( $_SERVER ) ) {
44            // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization happens at the end
45            $scheme = isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ? 'https' : 'http';
46            $host   = ! empty( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : null;
47            $path   = ! empty( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
48
49            // Support for local plugin development and testing using ngrok.
50            if ( ! empty( $_SERVER['HTTP_X_ORIGINAL_HOST'] ) && str_contains( $_SERVER['HTTP_X_ORIGINAL_HOST'], 'ngrok.io' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.
51                $host = wp_unslash( $_SERVER['HTTP_X_ORIGINAL_HOST'] );
52            }
53            // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
54
55            if ( $host ) {
56                $current_url = esc_url_raw( sprintf( '%s://%s%s', $scheme, $host, $path ) );
57            }
58        }
59
60        return apply_filters( 'jetpack_boost_current_url', $current_url );
61    }
62
63    /**
64     * Remove and reorder query parameters.
65     *
66     * @param string $url URL.
67     *
68     * @return string
69     */
70    protected static function normalize_query_args( $url ) {
71        $exclude_parameters = apply_filters(
72            'jetpack_boost_excluded_query_parameters',
73            self::PARAMS_TO_EXCLUDE
74        );
75
76        // Filter out certain parameters that we know don't constitute a different page.
77        $url = remove_query_arg( $exclude_parameters, $url );
78
79        // Extract the query string, sort it and replace it in the current URL.
80        $parsed_url = wp_parse_url( $url );
81
82        if ( ! empty( $parsed_url['query'] ) ) {
83            parse_str( $parsed_url['query'], $args );
84            foreach ( self::PARAMS_TO_NORMALIZE as $key => $value ) {
85                if ( isset( $args[ $key ] ) ) {
86                    $args[ $key ] = $value;
87                }
88            }
89            ksort( $args );
90            $sorted_query = http_build_query( $args );
91            $url          = str_replace( '?' . $parsed_url['query'], '?' . $sorted_query, $url );
92        }
93
94        return $url;
95    }
96}