PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / libraries / adapter / application / route.php

route.php in VikBooking Hotel Booking Engine & PMS trunk, at libraries/adapter/application/route.php

475 lines 12.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikWP - Libraries
4 * @subpackage adapter.application
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * Route adapter class.
16 *
17 * @since 10.0
18 */
19 class JRoute
20 {
21 /**
22 * A list of component shortcodes.
23 *
24 * @var array
25 */
26 protected static $shortcodes = array();
27
28 /**
29 * Translates an internal URL to a human-readable URL.
30 *
31 * @param string $url Absolute or Relative URI.
32 * @param boolean $xhtml Replace & by &amp; for XML compliance.
33 * @param integer $ssl Secure state for the resolved URI.
34 * 0: (default) No change, use the protocol currently used in the request.
35 * 1: Make URI secure using global secure site URI.
36 * 2: Make URI unsecure using the global unsecure site URI.
37 *
38 * @return string The translated humanly readable URL.
39 *
40 * @uses getPermalink()
41 * @uses replace()
42 */
43 public static function _($url, $xhtml = true, $ssl = null)
44 {
45 // parse URL to obtain query args (true: as array)
46 $args = JUri::getInstance($url)->getQuery(true);
47
48 // prepend current URI if starts with 'index.php'
49 if (strpos($url, 'index.php') === 0)
50 {
51 $url = static::getPermalink();
52 $url .= (strpos($url, '?') !== false ? '&' : '?') . http_build_query($args);
53 }
54
55 // if 'option' and 'view' arguments are not empty, try to route URLs
56 if (!empty($args['option']) && (!empty($args['view']) || !empty($args['Itemid'])))
57 {
58 $url = static::replace($args, $url);
59 }
60
61 /**
62 * Prepend base URL in case the system
63 * was not able to find a permalink.
64 *
65 * @since 10.1.29
66 */
67 if (preg_match("/^\?/", $url))
68 {
69 $url = JUri::root() . $url;
70 }
71
72 // replace & with &amp; for XML compliance
73 if ($xhtml)
74 {
75 $url = str_replace('&', '&amp;', $url);
76 }
77
78 if ($ssl == 1)
79 {
80 // force HTTPS
81 $url = str_replace('http://', 'https://', $url);
82 }
83 else if ($ssl == 2)
84 {
85 // force HTTP
86 $url = str_replace('https://', 'http://', $url);
87 }
88
89 return $url;
90 }
91
92 /**
93 * Proxy for underscore method.
94 * Needed to bypass the issue reported here:
95 * @link https://meta.trac.wordpress.org/ticket/3601
96 *
97 * @since 10.1.32 Renamed from `u`.
98 */
99 public static function rewrite($url, $xhtml = true, $ssl = null)
100 {
101 return static::_($url, $xhtml, $ssl);
102 }
103
104 /**
105 * Replaces the plain URL with the rewritten one.
106 *
107 * @param array $args The URL parts.
108 * @param string $url The URL to rewrite.
109 *
110 * @return string The rewritten URL, if possible.
111 *
112 * @uses getPermalink()
113 * @uses matchShortcode()
114 * @uses withQueryString()
115 */
116 protected static function replace(array $args, $url)
117 {
118 // make safe by replacing initial 'com_' (if any)
119 $option = preg_replace("/^com_/", "", $args['option']);
120 unset($args['option']);
121
122 // check if the shortcodes have been already cached
123 if (!isset(static::$shortcodes[$option]))
124 {
125 // get model to access shortcodes
126 $model = JModel::getInstance($option, 'shortcodes', 'admin');
127
128 /**
129 * Make sure the model exists for the requested option.
130 *
131 * @since 10.1.19
132 */
133 if ($model)
134 {
135 // cache the component shortcodes
136 static::$shortcodes[$option] = $model->all();
137 }
138 else
139 {
140 // create an empty list
141 static::$shortcodes[$option] = array();
142 }
143 }
144
145 $post_id = false;
146
147 if (!empty($args['view']))
148 {
149 // try to rewrite the URL with a matching shortcode
150 $post_id = static::matchShortcode(static::$shortcodes[$option], $args);
151 }
152
153 if (!$post_id && !empty($args['Itemid']))
154 {
155 // if the Itemid is set, get the POST permalink directly
156 $post_id = (int) $args['Itemid'];
157 }
158
159 if ($post_id)
160 {
161 $url = static::getPermalink($post_id);
162 $url = static::withQueryString($url, $post_id, static::$shortcodes[$option], $args);
163 }
164
165 /**
166 * Try to instantiate an external router.
167 *
168 * @since 10.1.19
169 */
170 $router = JFactory::getApplication()->getRouter($option);
171
172 if ($router)
173 {
174 // route URL using the specified arguments
175 $url = $router->build($url);
176 }
177
178 return $url;
179 }
180
181 /**
182 * Wrapper method to extend the function used to obtain
183 * the current permalink or the post link.
184 *
185 * @param mixed $post_id The post ID. Null to obtain the current link.
186 *
187 * @return string The permalink.
188 */
189 protected static function getPermalink($post_id = null)
190 {
191 // if the post ID is set, get the permalink directly
192 if ($post_id)
193 {
194 /**
195 * Added support to WPML, since it is unable to properly obtain the
196 * correct permalink depending the requested language tag.
197 *
198 * @since 10.1.55
199 */
200 $wpml_post_lang = apply_filters('wpml_post_language_details', null, $post_id);
201
202 /**
203 * Make sure we haven't received an error object from WPML.
204 *
205 * @since 10.1.60
206 */
207 if ($wpml_post_lang && !is_wp_error($wpml_post_lang))
208 {
209 // WPML supported, go ahead
210 return apply_filters('wpml_permalink', get_permalink($post_id), $wpml_post_lang['language_code']);
211 }
212
213 // WPML not supported, fallback to the default behavior
214 return get_permalink($post_id);
215 }
216
217 // otherwise get the current permalink
218 $link = get_permalink();
219
220 // return the permalink only if it is not empty
221 if ($link)
222 {
223 return $link;
224 }
225
226 // obtain the post ID from the current URL
227 $post_id = url_to_postid(JUri::current());
228
229 // if the current URL doesn't match any post, return an empty string
230 if (!$post_id)
231 {
232 return '';
233 }
234
235 // recursive call to obtain the permalink of the post found
236 return static::getPermalink($post_id);
237 }
238
239 /**
240 * Returns a URL containing the specified query string.
241 *
242 * @param string $url The URL to which append the query string.
243 * @param integer $post_id The post ID.
244 * @param array $shortcodes The shortcodes list.
245 * @param array $args The args to build the query string.
246 *
247 * @return string The URL with the query string.
248 */
249 protected static function withQueryString($url, $post_id, array $shortcodes, array $args = array())
250 {
251 // unset always the Item ID
252 unset($args['Itemid']);
253
254 // iterate the shortcodes
255 foreach ($shortcodes as $shortcode)
256 {
257 // process only if the given post ID matches the one assigned to the shortcode
258 if ($post_id == $shortcode->post_id)
259 {
260 // get shortcodes parameters, view and language
261 $json = (array) json_decode($shortcode->json, true);
262 $json['view'] = $shortcode->type;
263 $json['lang'] = $shortcode->lang;
264
265 // get the keys that the shortcode doesn't contain
266 $diff = self::diff($args, $json);
267
268 // if there are differences between the args, append them to the URL
269 if (count($diff))
270 {
271 $url .= (strpos($url, '?') !== false ? '&' : '?') . http_build_query($diff);
272 }
273
274 // return the URL with the query string (do not proceed)
275 return $url;
276 }
277 }
278
279 if (count($args))
280 {
281 /**
282 * if no shortcodes found, attach the whole query string
283 *
284 * @since 10.1.8
285 */
286 $url .= (strpos($url, '?') !== false ? '&' : '?') . http_build_query($args);
287 }
288
289 // nothing found, return plain URL
290 return $url;
291 }
292
293 /**
294 * Checks if there is a shortcode object that matches the given URL parts.
295 * In addition, the shortcode MUST own an existing post ID.
296 *
297 * @param array $shortcodes The shortcode objects list.
298 * @param array $args The URL query parts.
299 *
300 * @return mixed The post ID found, false on failure.
301 */
302 protected static function matchShortcode($shortcodes, array $args)
303 {
304 $post_id = false;
305 $last_count = 0;
306
307 if (empty($args['lang']))
308 {
309 // inject current langtag within URL arguments, only if not specified
310 // by query string
311 $args['lang'] = JFactory::getLanguage()->getTag();
312 }
313
314 foreach ($shortcodes as $shortcode)
315 {
316 $count = 0;
317
318 // decode JSON data
319 $json = json_decode($shortcode->json, true);
320 // inject VIEW name
321 $json['view'] = $shortcode->type;
322 // inject shortcode LANGTAG
323 $json['lang'] = $shortcode->lang;
324
325 // proceed only if the view is the same
326 if ($json['view'] == $args['view'])
327 {
328 // iterate the URL arguments
329 foreach ($args as $k => $v)
330 {
331 // the property should be matched only if the shortcode
332 // owns a parameter with the same name (defined in xml view)
333 // and value.
334 if (isset($json[$k]))
335 {
336 /**
337 * Improved the comparison of the language tag, which might be
338 * handled in different ways from different sections of WP.
339 *
340 * @since 10.1.38
341 */
342 if ($k === 'lang')
343 {
344 // check whether at least a language code doesn't mention the country code
345 if (!preg_match("/[_\-]/", $json[$k]) || !preg_match("/[_\-]/", $v))
346 {
347 // remove country code from locale assigned to the shortcode
348 $json[$k] = preg_split("/[_\-]/", $json[$k]);
349 $json[$k] = array_shift($json[$k]);
350
351 // remove country code from URL locale
352 $v = preg_split("/[_\-]/", $v);
353 $v = array_shift($v);
354 }
355
356 // normalize locale separators
357 $json[$k] = str_replace('-', '_', $json[$k]);
358 $v = str_replace('-', '_', $v);
359 }
360
361 // make sure both the values are scalar
362 if (is_scalar($json[$k]) && is_scalar($v))
363 {
364 if (!strcmp($json[$k], $v))
365 {
366 // if the shortcode owns the parameter and it is equals
367 // to the one specified in the URL, increase the counter
368 $count++;
369 }
370 }
371 /**
372 * Added support for arrays in query string, such as cid[].
373 *
374 * @since 10.1.27
375 */
376 else
377 {
378 // treat both the arguments as arrays
379 $json[$k] = (array) $json[$k];
380 $v = (array) $v;
381
382 // sort them to maximize the possibility of having matching URLs
383 sort($json[$k]);
384 sort($v);
385
386 if ($json[$k] == $v)
387 {
388 // increase the counter as we have a matching parameter
389 $count++;
390 }
391 }
392 }
393 }
394
395 // if the count of matches is higher than the last one,
396 // update the flags with the shortcode post ID and the current count
397 if ($count > $last_count)
398 {
399 $post_id = $shortcode->post_id;
400 $last_count = $count;
401 }
402 }
403 }
404
405 return $post_id;
406 }
407
408 /**
409 * Implementation of array_diff_assoc with recursion.
410 *
411 * @param array array1 The array to compare from.
412 * @param array array2 An array to compare against.
413 *
414 * @return array Returns an array containing all the values from array1
415 * that are not present in the second array.
416 *
417 * @since 10.1.27
418 */
419 private static function diff($array1, $array2)
420 {
421 $difference = array();
422
423 foreach ($array1 as $key => $value)
424 {
425 /**
426 * Improved the comparison of the language tag, which might be
427 * handled in different ways from different sections of WP.
428 *
429 * @since 10.1.49
430 */
431 if ($key === 'lang')
432 {
433 // check whether at least a language code doesn't mention the country code
434 if (!preg_match("/[_\-]/", $value) || !preg_match("/[_\-]/", $array2[$key]))
435 {
436 // remove country code from locale assigned to the shortcode
437 $array2[$key] = preg_split("/[_\-]/", $array2[$key]);
438 $array2[$key] = array_shift($array2[$key]);
439
440 // remove country code from URL locale
441 $value = preg_split("/[_\-]/", $value);
442 $value = array_shift($value);
443 }
444
445 // normalize locale separators
446 $array2[$key] = str_replace('-', '_', $array2[$key]);
447 $value = str_replace('-', '_', $value);
448 }
449
450 if (is_array($value))
451 {
452 if (!isset($array2[$key]) || !is_array($array2[$key]))
453 {
454 $difference[$key] = $value;
455 }
456 else
457 {
458 $new_diff = self::diff($value, $array2[$key]);
459
460 if (!empty($new_diff))
461 {
462 $difference[$key] = $new_diff;
463 }
464 }
465 }
466 else if (!array_key_exists($key, $array2) || $array2[$key] !== $value)
467 {
468 $difference[$key] = $value;
469 }
470 }
471
472 return $difference;
473 }
474 }
475