PluginProbe ʕ •ᴥ•ʔ
Starter Sites & Templates by Neve / 1.4.1
Starter Sites & Templates by Neve v1.4.1
1.4.1 1.4.0 1.3.0 1.2.29 1.2.28 1.2.6 1.2.7 1.2.8 1.2.9 trunk 1.0.10 1.0.11 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.20 1.1.21 1.1.22 1.1.23 1.1.24 1.1.25 1.1.26 1.1.27 1.1.28 1.1.29 1.1.3 1.1.30 1.1.31 1.1.32 1.1.33 1.1.34 1.1.35 1.1.36 1.1.37 1.1.38 1.1.39 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.3 1.2.4 1.2.5
templates-patterns-collection / includes / Importers / Helpers / Slug_Mapping.php
templates-patterns-collection / includes / Importers / Helpers Last commit date
Helper.php 3 months ago Importer_Alterator.php 3 months ago Quiet_Skin.php 5 years ago Quiet_Skin_Legacy.php 5 years ago Slug_Mapping.php 3 months ago
Slug_Mapping.php
458 lines
1 <?php
2 /**
3 * Slug mapping helper.
4 *
5 * Keeps the old/new slug map and source URLs for the current import and
6 * provides URL rewrite helpers that use this state.
7 *
8 * @package templates-patterns-collection
9 */
10
11 namespace TIOB\Importers\Helpers;
12
13 /**
14 * Class Slug_Mapping
15 */
16 class Slug_Mapping {
17
18 /**
19 * Transient key used for the import slug mapping state.
20 */
21 const TRANSIENT_KEY = 'ti_tpc_import_slug_mapping';
22
23 /**
24 * Map array key.
25 */
26 const MAP_KEY = 'slug_map';
27
28 /**
29 * Source URLs array key.
30 */
31 const SOURCES_KEY = 'source_urls';
32
33 /**
34 * Persisted state expiration.
35 */
36 const EXPIRATION = 12 * HOUR_IN_SECONDS;
37
38 /**
39 * Clear stored state.
40 *
41 * @return void
42 */
43 public static function clear() {
44 delete_transient( self::TRANSIENT_KEY );
45 }
46
47 /**
48 * Set slug map.
49 *
50 * @param array $slug_map old slug => new slug map.
51 *
52 * @return void
53 */
54 public static function set_slug_map( $slug_map ) {
55 if ( ! is_array( $slug_map ) ) {
56 return;
57 }
58
59 $state = self::get_state();
60 $map = array();
61
62 foreach ( $slug_map as $old_slug => $new_slug ) {
63 if ( ! is_string( $old_slug ) || ! is_string( $new_slug ) ) {
64 continue;
65 }
66
67 $old_slug = trim( $old_slug );
68 $new_slug = trim( $new_slug );
69 if ( $old_slug === '' || $new_slug === '' ) {
70 continue;
71 }
72
73 $map[ $old_slug ] = $new_slug;
74 }
75
76 $state[ self::MAP_KEY ] = $map;
77 self::save_state( $state );
78 }
79
80 /**
81 * Return the stored slug map.
82 *
83 * @return array
84 */
85 public static function get_slug_map() {
86 $state = self::get_state();
87 return is_array( $state[ self::MAP_KEY ] ) ? $state[ self::MAP_KEY ] : array();
88 }
89
90 /**
91 * Resolve a slug through the map.
92 *
93 * @param string $slug old slug.
94 *
95 * @return string
96 */
97 public static function resolve_slug( $slug ) {
98 $map = self::get_slug_map();
99 if ( isset( $map[ $slug ] ) ) {
100 return $map[ $slug ];
101 }
102
103 return $slug;
104 }
105
106 /**
107 * Register an import source URL.
108 *
109 * @param string $source_url source URL.
110 *
111 * @return void
112 */
113 public static function register_source_url( $source_url ) {
114 if ( ! is_string( $source_url ) || empty( $source_url ) ) {
115 return;
116 }
117
118 $source_url = esc_url_raw( trim( $source_url ) );
119 if ( empty( $source_url ) ) {
120 return;
121 }
122
123 $state = self::get_state();
124 $source_urls = isset( $state[ self::SOURCES_KEY ] ) && is_array( $state[ self::SOURCES_KEY ] ) ? $state[ self::SOURCES_KEY ] : array();
125
126 if ( ! in_array( $source_url, $source_urls, true ) ) {
127 $source_urls[] = $source_url;
128 }
129
130 $state[ self::SOURCES_KEY ] = $source_urls;
131 self::save_state( $state );
132 }
133
134 /**
135 * Get all registered source URLs.
136 *
137 * @return array
138 */
139 public static function get_source_urls() {
140 $state = self::get_state();
141 return isset( $state[ self::SOURCES_KEY ] ) && is_array( $state[ self::SOURCES_KEY ] ) ? $state[ self::SOURCES_KEY ] : array();
142 }
143
144 /**
145 * Recursively rewrite internal links in a value.
146 *
147 * @param mixed $value value to rewrite.
148 *
149 * @return mixed
150 */
151 public static function rewrite_value( $value ) {
152 if ( is_array( $value ) ) {
153 foreach ( $value as $key => $item ) {
154 $value[ $key ] = self::rewrite_value( $item );
155 }
156
157 return $value;
158 }
159
160 if ( is_string( $value ) ) {
161 return self::rewrite_string_value( $value );
162 }
163
164 return $value;
165 }
166
167 /**
168 * Rewrite a single URL if it belongs to an import source.
169 *
170 * @param string $url URL.
171 *
172 * @return string
173 */
174 public static function rewrite_url( $url ) {
175 if ( ! is_string( $url ) || empty( $url ) ) {
176 return $url;
177 }
178
179 $source_url = self::get_matching_source_url( $url );
180 if ( empty( $source_url ) ) {
181 return $url;
182 }
183
184 $url_parts = wp_parse_url( $url );
185 $source_parts = wp_parse_url( $source_url );
186 $home_parts = wp_parse_url( get_home_url() );
187
188 if ( ! is_array( $url_parts ) || ! is_array( $source_parts ) || ! is_array( $home_parts ) ) {
189 return $url;
190 }
191
192 $target_path = isset( $url_parts['path'] ) ? $url_parts['path'] : '/';
193 $source_path = isset( $source_parts['path'] ) ? rtrim( $source_parts['path'], '/' ) : '';
194 $home_path = isset( $home_parts['path'] ) ? rtrim( $home_parts['path'], '/' ) : '';
195 $relative = $target_path;
196
197 if ( ! empty( $source_path ) && $source_path !== '/' ) {
198 if ( $target_path === $source_path ) {
199 $relative = '';
200 } elseif ( strpos( $target_path, $source_path . '/' ) === 0 ) {
201 $relative = substr( $target_path, strlen( $source_path ) );
202 } else {
203 return $url;
204 }
205 }
206
207 $rewritten_relative = $relative;
208 if ( $rewritten_relative !== '' ) {
209 $rewritten_relative = self::replace_path_slugs( $rewritten_relative );
210 }
211
212 $new_path = $home_path . $rewritten_relative;
213 if ( $new_path === '' ) {
214 $new_path = '/';
215 }
216 if ( strpos( $new_path, '/' ) !== 0 ) {
217 $new_path = '/' . $new_path;
218 }
219
220 $url_parts['scheme'] = isset( $home_parts['scheme'] ) ? $home_parts['scheme'] : ( isset( $url_parts['scheme'] ) ? $url_parts['scheme'] : 'http' );
221 $url_parts['host'] = isset( $home_parts['host'] ) ? $home_parts['host'] : $url_parts['host'];
222 $url_parts['path'] = $new_path;
223
224 if ( isset( $home_parts['port'] ) ) {
225 $url_parts['port'] = $home_parts['port'];
226 } else {
227 unset( $url_parts['port'] );
228 }
229
230 if ( isset( $home_parts['user'] ) ) {
231 $url_parts['user'] = $home_parts['user'];
232 } else {
233 unset( $url_parts['user'] );
234 }
235
236 if ( isset( $home_parts['pass'] ) ) {
237 $url_parts['pass'] = $home_parts['pass'];
238 } else {
239 unset( $url_parts['pass'] );
240 }
241
242 return self::build_url( $url_parts );
243 }
244
245 /**
246 * Rewrite string values and support serialized content.
247 *
248 * @param string $value string value.
249 *
250 * @return string
251 */
252 private static function rewrite_string_value( $value ) {
253 if ( $value === '' ) {
254 return $value;
255 }
256
257 if ( is_serialized( $value ) ) {
258 $unserialized = maybe_unserialize( $value );
259 $rewritten = self::rewrite_value( $unserialized );
260
261 return maybe_serialize( $rewritten );
262 }
263
264 $value = self::rewrite_plain_urls_in_string( $value );
265 $value = self::rewrite_escaped_urls_in_string( $value );
266
267 return $value;
268 }
269
270 /**
271 * Rewrite plain URLs found in string.
272 *
273 * @param string $value value.
274 *
275 * @return string
276 */
277 private static function rewrite_plain_urls_in_string( $value ) {
278 return preg_replace_callback(
279 '#https?://[^\s"\'<>()]+#i',
280 function ( $matches ) {
281 return self::rewrite_url( $matches[0] );
282 },
283 $value
284 );
285 }
286
287 /**
288 * Rewrite escaped URLs found in string.
289 *
290 * @param string $value value.
291 *
292 * @return string
293 */
294 private static function rewrite_escaped_urls_in_string( $value ) {
295 return preg_replace_callback(
296 '#https?:\\\\/\\\\/[^\s"\'<>()]+#i',
297 function ( $matches ) {
298 $escaped_url = $matches[0];
299 $decoded_url = str_replace( '\\/', '/', $escaped_url );
300 $rewritten = self::rewrite_url( $decoded_url );
301 $rewritten = str_replace( '/', '\\/', $rewritten );
302
303 return $rewritten;
304 },
305 $value
306 );
307 }
308
309 /**
310 * Replace path segments using slug map.
311 *
312 * @param string $path path.
313 *
314 * @return string
315 */
316 private static function replace_path_slugs( $path ) {
317 $map = self::get_slug_map();
318 if ( empty( $map ) || ! is_string( $path ) || $path === '' ) {
319 return $path;
320 }
321
322 if ( $path === '/' ) {
323 return $path;
324 }
325
326 $starts_with_slash = strpos( $path, '/' ) === 0;
327 $ends_with_slash = substr( $path, -1 ) === '/';
328 $segments = explode( '/', trim( $path, '/' ) );
329
330 foreach ( $segments as $index => $segment ) {
331 $decoded = rawurldecode( $segment );
332 if ( isset( $map[ $decoded ] ) ) {
333 $segments[ $index ] = rawurlencode( $map[ $decoded ] );
334 }
335 }
336
337 $new_path = implode( '/', $segments );
338 if ( $starts_with_slash ) {
339 $new_path = '/' . $new_path;
340 }
341 if ( $ends_with_slash && $new_path !== '/' ) {
342 $new_path .= '/';
343 }
344 if ( $new_path === '' ) {
345 $new_path = '/';
346 }
347
348 return $new_path;
349 }
350
351 /**
352 * Return the matching source URL for a URL.
353 *
354 * @param string $url URL.
355 *
356 * @return string
357 */
358 private static function get_matching_source_url( $url ) {
359 $url_parts = wp_parse_url( $url );
360 if ( ! is_array( $url_parts ) || ! isset( $url_parts['host'] ) ) {
361 return '';
362 }
363
364 $target_host = strtolower( $url_parts['host'] );
365 $target_path = isset( $url_parts['path'] ) ? $url_parts['path'] : '/';
366 $best_match = '';
367 $best_length = -1;
368
369 foreach ( self::get_source_urls() as $source_url ) {
370 $source_parts = wp_parse_url( $source_url );
371 if ( ! is_array( $source_parts ) || ! isset( $source_parts['host'] ) ) {
372 continue;
373 }
374
375 if ( strtolower( $source_parts['host'] ) !== $target_host ) {
376 continue;
377 }
378
379 if ( isset( $source_parts['port'], $url_parts['port'] ) && (int) $source_parts['port'] !== (int) $url_parts['port'] ) {
380 continue;
381 }
382
383 $source_path = isset( $source_parts['path'] ) ? rtrim( $source_parts['path'], '/' ) : '';
384 if ( empty( $source_path ) || $source_path === '/' ) {
385 $path_length = 0;
386 if ( $path_length > $best_length ) {
387 $best_match = $source_url;
388 $best_length = $path_length;
389 }
390 continue;
391 }
392
393 if ( $target_path === $source_path || strpos( $target_path, $source_path . '/' ) === 0 ) {
394 $path_length = strlen( $source_path );
395 if ( $path_length > $best_length ) {
396 $best_match = $source_url;
397 $best_length = $path_length;
398 }
399 }
400 }
401
402 return $best_match;
403 }
404
405 /**
406 * Build URL from parsed URL parts.
407 *
408 * @param array $parts parsed URL parts.
409 *
410 * @return string
411 */
412 private static function build_url( $parts ) {
413 $scheme = isset( $parts['scheme'] ) ? $parts['scheme'] . '://' : '';
414 $user = isset( $parts['user'] ) ? $parts['user'] : '';
415 $pass = isset( $parts['pass'] ) ? ':' . $parts['pass'] : '';
416 $auth = ( $user || $pass ) ? "{$user}{$pass}@" : '';
417 $host = isset( $parts['host'] ) ? $parts['host'] : '';
418 $port = isset( $parts['port'] ) ? ':' . $parts['port'] : '';
419 $path = isset( $parts['path'] ) ? $parts['path'] : '';
420 $query = isset( $parts['query'] ) ? '?' . $parts['query'] : '';
421 $fragment = isset( $parts['fragment'] ) ? '#' . $parts['fragment'] : '';
422
423 return "{$scheme}{$auth}{$host}{$port}{$path}{$query}{$fragment}";
424 }
425
426 /**
427 * Get state from transient.
428 *
429 * @return array
430 */
431 private static function get_state() {
432 $state = get_transient( self::TRANSIENT_KEY );
433 if ( ! is_array( $state ) ) {
434 $state = array();
435 }
436
437 if ( ! isset( $state[ self::MAP_KEY ] ) || ! is_array( $state[ self::MAP_KEY ] ) ) {
438 $state[ self::MAP_KEY ] = array();
439 }
440 if ( ! isset( $state[ self::SOURCES_KEY ] ) || ! is_array( $state[ self::SOURCES_KEY ] ) ) {
441 $state[ self::SOURCES_KEY ] = array();
442 }
443
444 return $state;
445 }
446
447 /**
448 * Save state.
449 *
450 * @param array $state state.
451 *
452 * @return void
453 */
454 private static function save_state( $state ) {
455 set_transient( self::TRANSIENT_KEY, $state, self::EXPIRATION );
456 }
457 }
458