���� JFIF    �� �        "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777��  { �" ��     �� 5    !1AQa"q�2��BR��#b�������  ��  ��   ? ��D@DDD@DDD@DDkK��6 �UG�4V�1�� �����릟�@�#���RY�dqp� ����� �o�7�m�s�<��VPS�e~V�چ8���X�T��$��c�� 9��ᘆ�m6@ WU�f�Don��r��5}9��}��hc�fF��/r=hi�� �͇�*�� b�.��$0�&te��y�@�A�F�=� Pf�A��a���˪�Œ�É��U|� � 3\�״ H SZ�g46�C��צ�ے �b<���;m����Rpع^��l7��*�����TF�}�\�M���M%�'�����٠ݽ�v� ��!-�����?�N!La��A+[`#���M����'�~oR�?��v^)��=��h����A��X�.���˃����^Ə��ܯsO"B�c>; �e�4��5�k��/CB��.  �J?��;�҈�������������������~�<�VZ�ꭼ2/)Í”jC���ע�V�G�!���!�F������\�� Kj�R�oc�h���:Þ I��1"2�q×°8��Р@ז���_C0�ր��A��lQ��@纼�!7��F�� �]�sZ B�62r�v�z~�K�7�c��5�.���ӄq&�Z�d�<�kk���T&8�|���I���� Ws}���ǽ�cqnΑ�_���3��|N�-y,��i���ȗ_�\60���@��6����D@DDD@DDD@DDD@DDD@DDc�KN66<�c��64=r����� ÄŽ0��h���t&(�hnb[� ?��^��\��â|�,�/h�\��R��5�? �0�!צ܉-����G����٬��Q�zA���1�����V��� �:R���`�$��ik��H����D4�����#dk����� h�}����7���w%�������*o8wG�LycuT�.���ܯ7��I��u^���)��/c�,s�Nq�ۺ�;�ך�YH2���.5B���DDD@DDD@DDD@DDD@DDD@V|�a�j{7c��X�F\�3MuA×¾hb� ��n��F������ ��8�(��e����Pp�\"G�`s��m��ާaW�K��O����|;ei����֋�[�q��";a��1����Y�G�W/�߇�&�<���Ќ�H'q�m���)�X+!���=�m�ۚ丷~6a^X�)���,�>#&6G���Y��{����"" """ """ """ """ ""��at\/�a�8 �yp%�lhl�n����)���i�t��B�������������?��modskinlienminh.com - WSOX ENC ‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!url-match.php000066600000002565151733465510007177 0ustar00url = $url; } /** * Get the plain 'matched' URL: * * - Lowercase * - No trailing slashes * * @return string URL */ public function get_url() { // Remove query params, and decode any encoded characters $url = new Red_Url_Path( $this->url ); $path = $url->get_without_trailing_slash(); // URL encode $decode = [ '/', ':', '[', ']', '@', '~', ',', '(', ')', ';', ]; // URL encode everything - this converts any i10n to the proper encoding $path = rawurlencode( $path ); // We also converted things we dont want encoding, such as a /. Change these back foreach ( $decode as $char ) { $path = str_replace( rawurlencode( $char ), $char, $path ); } // Lowercase everything $path = Red_Url_Path::to_lower( $path ); return $path ? $path : '/'; } /** * Get the URL with parameters re-ordered into alphabetical order * * @return string */ public function get_url_with_params() { $query = new Red_Url_Query( $this->url, new Red_Source_Flags( [ Red_Source_Flags::FLAG_CASE => true ] ) ); return $query->get_url_with_query( $this->get_url() ); } } url-encode.php000066600000004404151733465510007332 0ustar00url = $url; $this->is_regex = $is_regex; } /** * URL encode some things, but other things can be passed through * * @return string */ public function get_as_target() { $allowed = [ '%2F' => '/', '%3F' => '?', '%3A' => ':', '%3D' => '=', '%26' => '&', '%25' => '%', '+' => '%20', '%24' => '$', '%23' => '#', ]; $url = rawurlencode( $this->url ); $url = $this->replace_encoding( $url, $allowed ); return $this->encode_regex( $url ); } /** * Encode a URL * * @return string */ public function get_as_source() { $allowed = [ '%2F' => '/', '%3F' => '?', '+' => '%20', '.' => '\\.', ]; $url = $this->replace_encoding( rawurlencode( $this->url ), $allowed ); return $this->encode_regex( $url ); } /** * Replace encoded characters in a URL * * @param string $str Source string. * @param array $allowed Allowed encodings. * @return string */ private function replace_encoding( $str, $allowed ) { foreach ( $allowed as $before => $after ) { $str = str_replace( $before, $after, $str ); } return $str; } /** * Encode a regex URL * * @param string $url URL. * @return string */ private function encode_regex( $url ) { if ( $this->is_regex ) { // No leading slash $url = ltrim( $url, '/' ); // If pattern has a ^ at the start then ensure we don't have a slash immediatley after $url = preg_replace( '@^\^/@', '^', $url ); $url = $this->replace_encoding( $url, [ '%2A' => '*', '%3F' => '?', '%28' => '(', '%29' => ')', '%5B' => '[', '%5C' => ']', '%24' => '$', '%2B' => '+', '%7C' => '|', '\\.' => '.', ] ); } return $url; } }url-path.php000066600000005564151733465510007041 0ustar00path = $this->get_path_component( $path ); } /** * Is the supplied `url` a match for this object? * * @param string $url URL to match against. * @param Red_Source_Flags $flags Source flags to use in match. * @return boolean */ public function is_match( $url, Red_Source_Flags $flags ) { $target = new Red_Url_Path( $url ); $target_path = $target->get(); $source_path = $this->get(); if ( $flags->is_ignore_trailing() ) { // Ignore trailing slashes $source_path = $this->get_without_trailing_slash(); $target_path = $target->get_without_trailing_slash(); } if ( $flags->is_ignore_case() ) { // Case insensitive match $source_path = self::to_lower( $source_path ); $target_path = self::to_lower( $target_path ); } return $target_path === $source_path; } /** * Convert a URL to lowercase * * @param string $url URL. * @return string */ public static function to_lower( $url ) { if ( function_exists( 'mb_strtolower' ) ) { return mb_strtolower( $url, 'UTF-8' ); } return strtolower( $url ); } /** * Get the path value * * @return string */ public function get() { return $this->path; } /** * Get the path value without trailing slash, or `/` if home * * @return string */ public function get_without_trailing_slash() { // Return / or // as-is if ( $this->path === '/' ) { return $this->path; } // Anything else remove the last / return preg_replace( '@/$@', '', $this->get() ); } /** * `parse_url` doesn't handle 'incorrect' URLs, such as those with double slashes * These are often used in redirects, so we fall back to our own parsing * * @param string $url URL. * @return string */ private function get_path_component( $url ) { $path = $url; if ( preg_match( '@^https?://@', $url, $matches ) > 0 ) { $parts = explode( '://', $url ); if ( count( $parts ) > 1 ) { $rest = explode( '/', $parts[1] ); $path = '/' . implode( '/', array_slice( $rest, 1 ) ); } } return urldecode( $this->get_query_before( $path ) ); } /** * Get the path component up to the query string * * @param string $url URL. * @return string */ private function get_query_before( $url ) { $qpos = strpos( $url, '?' ); $qrpos = strpos( $url, '\\?' ); // Have we found an escaped query and it occurs before a normal query? if ( $qrpos !== false && $qrpos < $qpos ) { // Yes, the path is everything up to the escaped query return substr( $url, 0, $qrpos ); } // No query - return everything as path if ( $qpos === false ) { return $url; } // Query found - return everything up to it return substr( $url, 0, $qpos ); } } url-transform.php000066600000004440151733465510010110 0ustar00transform_deprecated( $url ); $url = do_shortcode( $url ); // Restore shortcodes // phpcs:ignore $shortcode_tags = array_merge( [], $shortcode_copy ); return $url; } /** * Peform a shortcode * * @param array $attrs Shortcode attributes. * @param string $content Shortcode content. * @param string $tag Shortcode tag. * @return string */ public function do_shortcode( $attrs, $content, $tag ) { $user = wp_get_current_user(); switch ( $tag ) { case 'userid': return (string) $user->ID; case 'userlogin': return $user->ID > 0 ? $user->user_login : ''; case 'unixtime': return (string) time(); case 'md5': return md5( $content ); case 'upper': return strtoupper( $content ); case 'lower': return strtolower( $content ); case 'dashes': return str_replace( [ '_', ' ' ], '-', $content ); case 'underscores': return str_replace( [ '-', ' ' ], '_', $content ); } return apply_filters( 'redirection_url_transform', '', $tag, $attrs, $content ); } /** * Convert deprecated inline tags to shortcodes. * * @param string $url URL. * @return string */ private function transform_deprecated( $url ) { $url = str_replace( '%userid%', '[userid]', $url ); $url = str_replace( '%userlogin%', '[userlogin]', $url ); $url = str_replace( '%userurl%', '[userurl]', $url ); return $url; } } url-flags.php000066600000011553151733465510007174 0ustar00set_flags( $json ); } } /** * Get list of valid query types as an array * * @return string[] */ private function get_allowed_query() { return [ self::QUERY_IGNORE, self::QUERY_EXACT, self::QUERY_PASS, self::QUERY_EXACT_ORDER, ]; } /** * Parse flag data. * * @param array $json Flag data. * @return void */ public function set_flags( array $json ) { if ( isset( $json[ self::FLAG_QUERY ] ) && in_array( $json[ self::FLAG_QUERY ], $this->get_allowed_query(), true ) ) { $this->flag_query = $json[ self::FLAG_QUERY ]; } if ( isset( $json[ self::FLAG_CASE ] ) && is_bool( $json[ self::FLAG_CASE ] ) ) { $this->flag_case = $json[ self::FLAG_CASE ] ? true : false; } if ( isset( $json[ self::FLAG_TRAILING ] ) && is_bool( $json[ self::FLAG_TRAILING ] ) ) { $this->flag_trailing = $json[ self::FLAG_TRAILING ] ? true : false; } if ( isset( $json[ self::FLAG_REGEX ] ) && is_bool( $json[ self::FLAG_REGEX ] ) ) { $this->flag_regex = $json[ self::FLAG_REGEX ] ? true : false; if ( $this->flag_regex ) { // Regex auto-disables other things $this->flag_query = self::QUERY_EXACT; } } // Keep track of what values have been set, so we know what to override with defaults later $this->values_set = array_intersect( array_keys( $json ), array_keys( $this->get_json() ) ); } /** * Return `true` if ignore trailing slash, `false` otherwise * * @return boolean */ public function is_ignore_trailing() { return $this->flag_trailing; } /** * Return `true` if ignore case, `false` otherwise * * @return boolean */ public function is_ignore_case() { return $this->flag_case; } /** * Return `true` if ignore trailing slash, `false` otherwise * * @return boolean */ public function is_regex() { return $this->flag_regex; } /** * Return `true` if exact query match, `false` otherwise * * @return boolean */ public function is_query_exact() { return $this->flag_query === self::QUERY_EXACT; } /** * Return `true` if exact query match in set order, `false` otherwise * * @return boolean */ public function is_query_exact_order() { return $this->flag_query === self::QUERY_EXACT_ORDER; } /** * Return `true` if ignore query params, `false` otherwise * * @return boolean */ public function is_query_ignore() { return $this->flag_query === self::QUERY_IGNORE; } /** * Return `true` if ignore and pass query params, `false` otherwise * * @return boolean */ public function is_query_pass() { return $this->flag_query === self::QUERY_PASS; } /** * Return the flags as a JSON object * * @return array */ public function get_json() { return [ self::FLAG_QUERY => $this->flag_query, self::FLAG_CASE => $this->is_ignore_case(), self::FLAG_TRAILING => $this->is_ignore_trailing(), self::FLAG_REGEX => $this->is_regex(), ]; } /** * Return flag data, with defaults removed from the data. * * @param array $defaults Defaults to remove. * @return array */ public function get_json_without_defaults( $defaults ) { $json = $this->get_json(); if ( count( $defaults ) > 0 ) { foreach ( $json as $key => $value ) { if ( isset( $defaults[ $key ] ) && $value === $defaults[ $key ] ) { unset( $json[ $key ] ); } } } return $json; } /** * Return flag data, with defaults filling in any gaps not set. * * @return array */ public function get_json_with_defaults() { $settings = red_get_options(); $json = $this->get_json(); $defaults = [ self::FLAG_QUERY => $settings[ self::FLAG_QUERY ], self::FLAG_CASE => $settings[ self::FLAG_CASE ], self::FLAG_TRAILING => $settings[ self::FLAG_TRAILING ], self::FLAG_REGEX => $settings[ self::FLAG_REGEX ], ]; foreach ( $this->values_set as $key ) { if ( ! isset( $json[ $key ] ) ) { $json[ $key ] = $defaults[ $key ]; } } return $json; } } url-query.php000066600000026374151733465510007254 0ustar00original_query = $this->get_url_query( $url ); $this->match_query = $this->original_query; if ( $flags->is_ignore_case() ) { $this->match_query = $this->get_url_query( Red_Url_Path::to_lower( $url ) ); } } /** * Does this object match the URL? * * @param string $url URL to match. * @param Red_Source_Flags $flags Source flags. * @return boolean */ public function is_match( $url, Red_Source_Flags $flags ) { if ( $flags->is_ignore_case() ) { $url = Red_Url_Path::to_lower( $url ); } // If we can't parse the query params then match the params exactly if ( $this->match_exact !== false ) { return $this->is_string_match( $this->get_query_after( $url ), $this->match_exact, $flags->is_ignore_case() ); } $target = $this->get_url_query( $url ); // All params in the source have to exist in the request, but in any order $matched = $this->get_query_same( $this->match_query, $target, $flags->is_ignore_case() ); if ( count( $matched ) !== count( $this->match_query ) ) { // Source params arent matched exactly return false; }; // Get list of whatever is left over $query_diff = $this->get_query_diff( $this->match_query, $target ); $query_diff = array_merge( $query_diff, $this->get_query_diff( $target, $this->match_query ) ); if ( $flags->is_query_ignore() || $flags->is_query_pass() ) { return true; // This ignores all other query params } // In an exact match there shouldn't be any more params return count( $query_diff ) === 0; } /** * Return true if the two strings match, false otherwise. Pays attention to case sensitivity * * @param string $first First string. * @param string $second Second string. * @param boolean $case Case sensitivity. * @return boolean */ private function is_string_match( $first, $second, $case ) { if ( $case ) { return Red_Url_Path::to_lower( $first ) === Red_Url_Path::to_lower( $second ); } return $first === $second; } /** * Pass query params from one URL to another URL, ignoring any params that already exist on the target. * * @param string $target_url The target URL to add params to. * @param string $requested_url The source URL to pass params from. * @param Red_Source_Flags $flags Any URL flags. * @return string URL, modified or not. */ public static function add_to_target( $target_url, $requested_url, Red_Source_Flags $flags ) { if ( $flags->is_query_pass() && $target_url ) { $source_query = new Red_Url_Query( $target_url, $flags ); $request_query = new Red_Url_Query( $requested_url, $flags ); // Now add any remaining params $query_diff = $source_query->get_query_diff( $source_query->original_query, $request_query->original_query ); $request_diff = $request_query->get_query_diff( $request_query->original_query, $source_query->original_query ); foreach ( $request_diff as $key => $value ) { $query_diff[ $key ] = $value; } // Remove any params from $source that are present in $request - we dont allow // predefined params to be overridden foreach ( array_keys( $query_diff ) as $key ) { if ( isset( $source_query->original_query[ $key ] ) ) { unset( $query_diff[ $key ] ); } } return self::build_url( $target_url, $query_diff ); } return $target_url; } /** * Build a URL from a base and query parameters * * @param string $url Base URL. * @param Array $query_array Query parameters. * @return string */ public static function build_url( $url, $query_array ) { $query = http_build_query( array_map( function( $value ) { if ( $value === null ) { return ''; } return $value; }, $query_array ) ); $query = preg_replace( '@%5B\d*%5D@', '[]', $query ); // Make these look like [] foreach ( $query_array as $key => $value ) { if ( $value === null ) { $search = str_replace( '%20', '+', rawurlencode( $key ) . '=' ); $replace = str_replace( '%20', '+', rawurlencode( $key ) ); $query = str_replace( $search, $replace, $query ); } } $query = str_replace( '%252B', '+', $query ); if ( $query ) { // Get any fragment $target_fragment = wp_parse_url( $url, PHP_URL_FRAGMENT ); // If we have a fragment we need to ensure it comes after the query parameters, not before if ( $target_fragment ) { // Remove fragment $url = str_replace( '#' . $target_fragment, '', $url ); // Add to the end of the query $query .= '#' . $target_fragment; } return $url . ( strpos( $url, '?' ) === false ? '?' : '&' ) . $query; } return $url; } /** * Get a URL with the given base and query parameters from this Url_Query * * @param string $url Base URL. * @return string */ public function get_url_with_query( $url ) { return self::build_url( $url, $this->original_query ); } /** * Get the query parameters * * @return array */ public function get() { return $this->original_query; } /** * Does the URL and the query params contain no parameters? * * @param string $url URL. * @param Array $params Query params. * @return boolean */ private function is_exact_match( $url, $params ) { // No parsed query params but we have query params on the URL - some parsing error with wp_parse_str if ( count( $params ) === 0 && $this->has_query_params( $url ) ) { return true; } return false; } /** * Get query parameters from a URL * * @param string $url URL. * @return array */ private function get_url_query( $url ) { $params = []; $query = $this->get_query_after( $url ); $internal = $this->parse_str( $query ); wp_parse_str( $query ? $query : '', $params ); // For exactness and due to the way parse_str works we go through and check any query param without a value foreach ( $params as $key => $value ) { if ( is_string( $value ) && strlen( $value ) === 0 && strpos( $url, $key . '=' ) === false ) { $params[ $key ] = null; } } // A work-around until we replace parse_str with internal function foreach ( $internal as $pos => $internal_param ) { if ( $internal_param['parse_str'] !== $internal_param['name'] ) { foreach ( $params as $key => $value ) { if ( $key === $internal_param['parse_str'] ) { unset( $params[ $key ] ); unset( $internal[ $pos ] ); $params[ $internal_param['name'] ] = $value; } } } } if ( $this->is_exact_match( $url, $params ) ) { $this->match_exact = $query; } return $params; } /** * A replacement for parse_str, which behaves oddly in some situations (spaces and no param value) * * TODO: use this in preference to parse_str * * @param string $query Query. * @return string */ private function parse_str( $query ) { $params = []; if ( strlen( $query ) === 0 ) { return $params; } $parts = explode( '&', $query ? $query : '' ); foreach ( $parts as $part ) { $param = explode( '=', $part ); $parse_str = []; wp_parse_str( $part, $parse_str ); $params[] = [ 'name' => str_replace( [ '[', ']', '%5B', '%5D' ], '', str_replace( '+', ' ', $param[0] ) ), 'value' => isset( $param[1] ) ? str_replace( '+', ' ', $param[1] ) : null, 'parse_str' => implode( '', array_keys( $parse_str ) ), ]; } return $params; } /** * Does the URL contain query parameters? * * @param string $url URL. * @return boolean */ public function has_query_params( $url ) { $qpos = strpos( $url, '?' ); if ( $qpos === false ) { return false; } return true; } /** * Get parameters after the ? * * @param string $url URL. * @return string */ public function get_query_after( $url ) { $qpos = strpos( $url, '?' ); $qrpos = strpos( $url, '\\?' ); // No ? anywhere - no query if ( $qpos === false ) { return ''; } // Found an escaped ? and it comes before the non-escaped ? if ( $qrpos !== false && $qrpos < $qpos ) { return substr( $url, $qrpos + 2 ); } // Standard query param return substr( $url, $qpos + 1 ); } private function get_query_case( array $query ) { $keys = []; foreach ( array_keys( $query ) as $key ) { $keys[ Red_Url_Path::to_lower( $key ) ] = $key; } return $keys; } /** * Get query parameters that are the same in both query arrays * * @param array $source_query Source query params. * @param array $target_query Target query params. * @param bool $is_ignore_case Ignore case. * @param integer $depth Current recursion depth. * @return array */ public function get_query_same( array $source_query, array $target_query, $is_ignore_case, $depth = 0 ) { if ( $depth > self::RECURSION_LIMIT ) { return []; } $source_keys = $this->get_query_case( $source_query ); $target_keys = $this->get_query_case( $target_query ); $same = []; foreach ( $source_keys as $key => $original_key ) { // Does the key exist in the target if ( isset( $target_keys[ $key ] ) ) { // Key exists. Now match the value $source_value = $source_query[ $original_key ]; $target_value = $target_query[ $target_keys[ $key ] ]; $add = false; if ( is_array( $source_value ) && is_array( $target_value ) ) { $add = $this->get_query_same( $source_value, $target_value, $is_ignore_case, $depth + 1 ); if ( count( $add ) !== count( $source_value ) ) { $add = false; } } elseif ( is_string( $source_value ) && is_string( $target_value ) ) { $add = $this->is_string_match( $source_value, $target_value, $is_ignore_case ) ? $source_value : false; } elseif ( $source_value === null && $target_value === null ) { $add = null; } if ( ! empty( $add ) || is_numeric( $add ) || $add === '' || $add === null ) { $same[ $original_key ] = $add; } } } return $same; } /** * Get the difference in query parameters * * @param array $source_query Source query params. * @param array $target_query Target query params. * @param integer $depth Current recursion depth. * @return array */ public function get_query_diff( array $source_query, array $target_query, $depth = 0 ) { if ( $depth > self::RECURSION_LIMIT ) { return []; } $diff = []; foreach ( $source_query as $key => $value ) { if ( array_key_exists( $key, $target_query ) && is_array( $value ) && is_array( $target_query[ $key ] ) ) { $add = $this->get_query_diff( $source_query[ $key ], $target_query[ $key ], $depth + 1 ); if ( ! empty( $add ) ) { $diff[ $key ] = $add; } } elseif ( ! array_key_exists( $key, $target_query ) || ! $this->is_value( $value ) || ! $this->is_value( $target_query[ $key ] ) || $target_query[ $key ] !== $source_query[ $key ] ) { $diff[ $key ] = $value; } } return $diff; } private function is_value( $value ) { return is_string( $value ) || $value === null; } } url.php000066600000002427151733465510006102 0ustar00url = $url; $this->url = str_replace( ' ', '%20', $this->url ); // deprecated } /** * Get the raw URL * * @return string URL */ public function get_url() { return $this->url; } /** * Match a target URL against the current URL, using any match flags * * @param string $requested_url Target URL. * @param Red_Source_Flags $flags Match flags. * @return boolean */ public function is_match( $requested_url, Red_Source_Flags $flags ) { if ( $flags->is_regex() ) { $regex = new Red_Regex( $this->url, $flags->is_ignore_case() ); return $regex->is_match( $requested_url ); } $path = new Red_Url_Path( $this->url ); $query = new Red_Url_Query( $this->url, $flags ); return $path->is_match( $requested_url, $flags ) && $query->is_match( $requested_url, $flags ); } } url-request.php000066600000003771151733465510007573 0ustar00original_url = apply_filters( 'redirection_url_source', $url ); $this->decoded_url = rawurldecode( $this->original_url ); // Replace the decoded query params with the original ones $this->original_url = $this->replace_query_params( $this->original_url, $this->decoded_url ); } /** * Take the decoded path part, but keep the original query params. This ensures any redirects keep the encoding. * * @param string $original_url Original unencoded URL. * @param string $decoded_url Decoded URL. * @return string */ private function replace_query_params( $original_url, $decoded_url ) { $decoded = explode( '?', $decoded_url ); if ( count( $decoded ) > 1 ) { $original = explode( '?', $original_url ); if ( count( $original ) > 1 ) { return $decoded[0] . '?' . $original[1]; } } return $decoded_url; } /** * Get the original URL * * @return string */ public function get_original_url() { return $this->original_url; } /** * Get the decoded URL * * @return string */ public function get_decoded_url() { return $this->decoded_url; } /** * Is this a valid URL? * * @return boolean */ public function is_valid() { return strlen( $this->get_decoded_url() ) > 0; } /** * Protect certain URLs from being redirected. Note we don't need to protect wp-admin, as this code doesn't run there * * @return boolean */ public function is_protected_url() { $rest = wp_parse_url( red_get_rest_api() ); $rest_api = $rest['path'] . ( isset( $rest['query'] ) ? '?' . $rest['query'] : '' ); if ( substr( $this->get_decoded_url(), 0, strlen( $rest_api ) ) === $rest_api ) { // Never redirect the REST API return true; } return false; } }