PluginProbe
Redirection / 4.4
Redirection v4.4
5.10.0 5.9.0 5.8.1 5.8.0 3.7.2 3.7.3 4.0 4.0.1 4.1 4.1.1 4.2 4.2.1 4.2.2 4.2.3 4.3 4.3.1 4.3.2 4.3.3 4.4 4.4.1 4.4.2 4.5 4.5.1 4.6.2 4.7.1 All 130 releases
redirection / models / url-query.php

url-query.php in Redirection 4.4, at models/url-query.php

192 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class Red_Url_Query {
4 const RECURSION_LIMIT = 10;
5
6 private $query = [];
7 private $match_exact = false;
8
9 public function __construct( $url, $flags ) {
10 if ( $flags->is_ignore_case() ) {
11 $url = Red_Url_Path::to_lower( $url );
12 }
13
14 $this->query = $this->get_url_query( $url );
15 }
16
17 public function is_match( $url, Red_Source_Flags $flags ) {
18 if ( $flags->is_ignore_case() ) {
19 $url = Red_Url_Path::to_lower( $url );
20 }
21
22 // If we can't parse the query params then match the params exactly
23 if ( $this->match_exact !== false ) {
24 return $this->get_query_after( $url ) === $this->match_exact;
25 }
26
27 $target = $this->get_url_query( $url );
28
29 // All params in the source have to exist in the request, but in any order
30 $matched = $this->get_query_same( $this->query, $target );
31
32 if ( count( $matched ) !== count( $this->query ) ) {
33 // Source params arent matched exactly
34 return false;
35 };
36
37 // Get list of whatever is left over
38 $query_diff = $this->get_query_diff( $this->query, $target );
39 $query_diff = array_merge( $query_diff, $this->get_query_diff( $target, $this->query ) );
40
41 if ( $flags->is_query_ignore() || $flags->is_query_pass() ) {
42 return true; // This ignores all other query params
43 }
44
45 // In an exact match there shouldn't be any more params
46 return count( $query_diff ) === 0;
47 }
48
49 /**
50 * Pass query params from one URL to another URL, ignoring any params that already exist on the target
51 *
52 * @param string $target_url The target URL to add params to
53 * @param string $requested_url The source URL to pass params from
54 * @param Red_Source_Flags $flags Any URL flags
55 * @return string URL, modified or not
56 */
57 public static function add_to_target( $target_url, $requested_url, Red_Source_Flags $flags ) {
58 if ( $flags->is_query_pass() && $target_url ) {
59 $source_query = new Red_Url_Query( $target_url, $flags );
60 $request_query = new Red_Url_Query( $requested_url, $flags );
61
62 // Now add any remaining params
63 $query_diff = $source_query->get_query_diff( $source_query->query, $request_query->query );
64 $request_diff = $request_query->get_query_diff( $request_query->query, $source_query->query );
65
66 foreach ( $request_diff as $key => $value ) {
67 $query_diff[ $key ] = $value;
68 }
69
70 // Remove any params from $source that are present in $request - we dont allow
71 // predefined params to be overridden
72 foreach ( $query_diff as $key => $value ) {
73 if ( isset( $source_query->query[ $key ] ) ) {
74 unset( $query_diff[ $key ] );
75 }
76 }
77
78 $query = http_build_query( $query_diff );
79 $query = preg_replace( '@%5B\d*%5D@', '[]', $query ); // Make these look like []
80
81 if ( $query ) {
82 return $target_url . ( strpos( $target_url, '?' ) === false ? '?' : '&' ) . $query;
83 }
84 }
85
86 return $target_url;
87 }
88
89 public function get() {
90 return $this->query;
91 }
92
93 private function is_exact_match( $url, $params ) {
94 // No parsed query params but we have query params on the URL - some parsing error with wp_parse_str
95 if ( count( $params ) === 0 && $this->has_query_params( $url ) ) {
96 return true;
97 }
98
99 return false;
100 }
101
102 private function get_url_query( $url ) {
103 $params = [];
104 $query = $this->get_query_after( $url );
105
106 wp_parse_str( $query ? $query : '', $params );
107
108 if ( $this->is_exact_match( $url, $params ) ) {
109 $this->match_exact = $query;
110 }
111
112 return $params;
113 }
114
115 public function has_query_params( $url ) {
116 $qpos = strpos( $url, '?' );
117
118 if ( $qpos === false ) {
119 return false;
120 }
121
122 return true;
123 }
124
125 public function get_query_after( $url ) {
126 $qpos = strpos( $url, '?' );
127 $qrpos = strpos( $url, '\\?' );
128
129 if ( $qpos === false ) {
130 return '';
131 }
132
133 if ( $qrpos !== false && $qrpos < $qpos ) {
134 return substr( $url, $qrpos + strlen( $qrpos ) );
135 }
136
137 return substr( $url, $qpos + 1 );
138 }
139
140 public function get_query_same( array $source_query, array $target_query, $depth = 0 ) {
141 if ( $depth > self::RECURSION_LIMIT ) {
142 return [];
143 }
144
145 $same = [];
146 foreach ( $source_query as $key => $value ) {
147 if ( isset( $target_query[ $key ] ) ) {
148 $add = false;
149
150 if ( is_array( $value ) && is_array( $target_query[ $key ] ) ) {
151 $add = $this->get_query_same( $source_query[ $key ], $target_query[ $key ], $depth + 1 );
152
153 if ( count( $add ) !== count( $source_query[ $key ] ) ) {
154 $add = false;
155 }
156 } elseif ( is_string( $value ) && is_string( $target_query[ $key ] ) ) {
157 $add = $value === $target_query[ $key ] ? $value : false;
158 }
159
160 if ( ! empty( $add ) || is_numeric( $add ) || $add === '' ) {
161 $same[ $key ] = $add;
162 }
163 }
164 }
165
166 return $same;
167 }
168
169 public function get_query_diff( array $source_query, array $target_query, $depth = 0 ) {
170 if ( $depth > self::RECURSION_LIMIT ) {
171 return [];
172 }
173
174 $diff = [];
175 foreach ( $source_query as $key => $value ) {
176 $found = false;
177
178 if ( isset( $target_query[ $key ] ) && is_array( $value ) && is_array( $target_query[ $key ] ) ) {
179 $add = $this->get_query_diff( $source_query[ $key ], $target_query[ $key ], $depth + 1 );
180
181 if ( ! empty( $add ) ) {
182 $diff[ $key ] = $add;
183 }
184 } elseif ( ! isset( $target_query[ $key ] ) || ! is_string( $value ) || ! is_string( $target_query[ $key ] ) || $target_query[ $key ] !== $source_query[ $key ] ) {
185 $diff[ $key ] = $value;
186 }
187 }
188
189 return $diff;
190 }
191 }
192