PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.7
Yatra – Travel Booking & Tour Operator Software v3.0.2.7
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
yatra / app / Core / Routing / PlainPageMatcher.php

PlainPageMatcher.php in Yatra – Travel Booking & Tour Operator Software 3.0.2.7, at app/Core/Routing/PlainPageMatcher.php

300 lines 9.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Core\Routing;
6
7 use Yatra\Services\SettingsService;
8
9 /**
10 * Resolves storefront routes when WordPress uses plain permalinks (?p=…).
11 * Primary discriminator: {@see query_var `yatra_page`} — value matches saved URL bases from settings
12 * (trip_base, booking_base, destination_base, activity_base, trip_category_base).
13 *
14 * Single trip (plain) may also be requested as ?{trip_base}=slug (same segment as the trip URL base)
15 * without yatra_page. Legacy: ?yatra_trip= / ?yatra_trip_slug=.
16 *
17 * Taxonomy singles use the same pattern: ?{destination_base}=slug, ?{activity_base}=slug,
18 * ?{trip_category_base}=slug. Legacy: ?yatra_page={base}&yatra_*_slug=slug still supported.
19 */
20 final class PlainPageMatcher
21 {
22 /**
23 * @return array<string, mixed>|null Route data compatible with {@see Router::handleRouteData()}
24 */
25 public static function match(): ?array
26 {
27 // Plain-style query routing (?yatra_page=activity&paged=2) must run even when the site uses
28 // pretty permalinks — pagination links use home_url('/') + query args. Without this, the router
29 // never sees those URLs and WordPress may 404 on paged front-page requests.
30
31 $trip_base = self::sanitizeBaseSegment(SettingsService::getTripBase());
32 $booking_base = self::sanitizeBaseSegment(SettingsService::getBookingBase());
33 $dest_base = self::permalinkBase('destination_base', 'destination');
34 $act_base = self::permalinkBase('activity_base', 'activity');
35 $cat_base = self::permalinkBase('trip_category_base', 'trip-category');
36
37 $raw = $_GET['yatra_page'] ?? '';
38 $raw = is_string($raw) ? trim(wp_unslash($raw)) : '';
39
40 // Plain single trip: ?{trip_base}=slug (legacy ?yatra_trip= / ?yatra_trip_slug=)
41 $trip_slug_only = self::getTripSlugFromRequest();
42 if ($trip_slug_only !== '' && $raw === '') {
43 return [
44 'type' => 'trip',
45 'slug' => $trip_slug_only,
46 'base' => $trip_base,
47 ];
48 }
49
50 if ($raw === '') {
51 // On pretty permalinks, trip listing is /{trip_base}/?destination=…&activity=… — those keys
52 // match taxonomy plain routing below and must NOT be treated as /{destination_base}/slug/ singles.
53 $tripSeg = self::sanitizeBaseSegment($trip_base);
54 $cleanPath = trim((string) UrlParser::getCleanRequestPath(), '/');
55 $taxPlain = null;
56 if (!self::isTripArchiveRequestPath($cleanPath, $tripSeg)) {
57 $taxPlain = self::matchTaxonomyPlainBaseSlugs(
58 $tripSeg,
59 $dest_base,
60 $act_base,
61 $cat_base
62 );
63 }
64 if ($taxPlain !== null) {
65 return $taxPlain;
66 }
67 }
68
69 if ($raw === '') {
70 if (!empty($_GET['yatra_login_page'])) {
71 return [
72 'type' => 'login',
73 'page' => 'main',
74 'base' => 'login',
75 ];
76 }
77
78 $checkoutToken = isset($_GET['yatra_remaining_checkout']) ? (string) wp_unslash($_GET['yatra_remaining_checkout']) : '';
79 $checkoutToken = preg_replace('/[^a-zA-Z0-9_-]/', '', $checkoutToken) ?? '';
80 if ($checkoutToken !== '') {
81 return [
82 'type' => 'checkout',
83 'token' => $checkoutToken,
84 ];
85 }
86
87 return null;
88 }
89
90 $page = self::sanitizeBaseSegment($raw);
91 if ($page === '') {
92 return null;
93 }
94
95 if ($page === $booking_base && !SettingsService::useCustomBookingPage()) {
96 return [
97 'type' => 'booking',
98 'page' => 'main',
99 'base' => $booking_base,
100 ];
101 }
102
103 $account_base = self::sanitizeBaseSegment(SettingsService::getAccountBase());
104 if ($page === $account_base) {
105 $tab = isset($_GET['tab']) ? sanitize_key((string) $_GET['tab']) : '';
106
107 return [
108 'type' => 'account',
109 'page' => self::accountTabToPage($tab),
110 'base' => SettingsService::getAccountBase(),
111 ];
112 }
113
114 if ($page === $trip_base) {
115 $slug = self::getTripSlugFromRequest();
116 if ($slug !== '') {
117 return [
118 'type' => 'trip',
119 'slug' => $slug,
120 'base' => $trip_base,
121 ];
122 }
123
124 $paged = self::currentPaged();
125
126 return [
127 'type' => 'listing',
128 'listing_type' => 'trip',
129 'base' => $trip_base,
130 'paged' => $paged,
131 ];
132 }
133
134 if ($page === $dest_base) {
135 return self::matchTaxonomy('destination', $dest_base, 'yatra_destination_slug');
136 }
137
138 if ($page === $act_base) {
139 return self::matchTaxonomy('activity', $act_base, 'yatra_activity_slug');
140 }
141
142 if ($page === $cat_base) {
143 return self::matchTaxonomy('category', $cat_base, 'yatra_category_slug');
144 }
145
146 return null;
147 }
148
149 private static function matchTaxonomy(string $taxonomyType, string $base, string $legacySlugKey): ?array
150 {
151 $baseKey = self::sanitizeBaseSegment($base);
152 $slug = '';
153 if ($baseKey !== '') {
154 $fromBase = $_GET[$baseKey] ?? null;
155 if (is_string($fromBase)) {
156 $slug = sanitize_title(wp_unslash($fromBase));
157 }
158 }
159 if ($slug === '') {
160 $legacy = $_GET[$legacySlugKey] ?? '';
161 $slug = is_string($legacy) ? sanitize_title(wp_unslash($legacy)) : '';
162 }
163 if ($slug !== '') {
164 return [
165 'type' => 'taxonomy',
166 'taxonomy_type' => $taxonomyType,
167 'slug' => $slug,
168 'base' => $base,
169 'paged' => self::currentPaged(),
170 ];
171 }
172
173 return [
174 'type' => 'listing',
175 'listing_type' => $taxonomyType,
176 'base' => $base,
177 'paged' => self::currentPaged(),
178 ];
179 }
180
181 /**
182 * Plain taxonomy singles: ?{destination_base}=slug (no yatra_page), same idea as trip.
183 * Skips keys identical to trip_base so ?trip= only resolves as a trip.
184 *
185 * @return array<string, mixed>|null
186 */
187 /**
188 * Trip archive (or /{trip_base}/page/N/) — query params like ?destination= are filters, not taxonomy singles.
189 */
190 private static function isTripArchiveRequestPath(string $cleanPath, string $tripBaseSanitized): bool
191 {
192 if ($tripBaseSanitized === '') {
193 return false;
194 }
195 if ($cleanPath === $tripBaseSanitized) {
196 return true;
197 }
198
199 return (bool) preg_match('/^' . preg_quote($tripBaseSanitized, '/') . '\/page\/\d+$/', $cleanPath);
200 }
201
202 private static function matchTaxonomyPlainBaseSlugs(
203 string $trip_base_sanitized,
204 string $dest_base,
205 string $act_base,
206 string $cat_base
207 ): ?array {
208 $pairs = [
209 [self::sanitizeBaseSegment($dest_base), 'destination'],
210 [self::sanitizeBaseSegment($act_base), 'activity'],
211 [self::sanitizeBaseSegment($cat_base), 'category'],
212 ];
213 foreach ($pairs as [$b, $taxType]) {
214 if ($b === '' || $b === $trip_base_sanitized) {
215 continue;
216 }
217 if (!isset($_GET[$b])) {
218 continue;
219 }
220 $raw = $_GET[$b];
221 $slug = is_string($raw) ? sanitize_title(wp_unslash($raw)) : '';
222 if ($slug === '') {
223 continue;
224 }
225
226 return [
227 'type' => 'taxonomy',
228 'taxonomy_type' => $taxType,
229 'slug' => $slug,
230 'base' => $b,
231 'paged' => self::currentPaged(),
232 ];
233 }
234
235 return null;
236 }
237
238 private static function accountTabToPage(string $tab): string
239 {
240 $allowed = ['dashboard', 'bookings', 'payments', 'documents', 'profile', 'saved-trips'];
241 if ($tab === '' || !in_array($tab, $allowed, true)) {
242 return 'dashboard';
243 }
244
245 return $tab;
246 }
247
248 private static function getTripSlugFromRequest(): string
249 {
250 $key = self::tripSlugQueryKey();
251 $slug = $_GET[$key] ?? $_GET['yatra_trip'] ?? $_GET['yatra_trip_slug'] ?? '';
252 $slug = is_string($slug) ? sanitize_title(wp_unslash($slug)) : '';
253
254 return $slug;
255 }
256
257 /**
258 * Query parameter name for single-trip slug in plain mode — matches {@see SettingsService::getTripBase()}.
259 */
260 private static function tripSlugQueryKey(): string
261 {
262 $b = self::sanitizeBaseSegment(SettingsService::getTripBase());
263
264 return $b !== '' ? $b : 'trip';
265 }
266
267 private static function currentPaged(): int
268 {
269 $p = get_query_var('paged');
270 if ($p !== '' && $p !== null) {
271 return max(1, (int) $p);
272 }
273
274 if (isset($_GET['paged'])) {
275 return max(1, (int) wp_unslash($_GET['paged']));
276 }
277
278 if (isset($_GET['page'])) {
279 return max(1, (int) wp_unslash($_GET['page']));
280 }
281
282 return 1;
283 }
284
285 private static function sanitizeBaseSegment(string $value): string
286 {
287 $value = preg_replace('/[^a-z0-9_-]/i', '', $value) ?? '';
288
289 return $value;
290 }
291
292 private static function permalinkBase(string $optionKey, string $default): string
293 {
294 $raw = SettingsService::getString($optionKey, $default);
295 $sanitized = preg_replace('/[^a-z0-9_-]/i', '', $raw) ?? '';
296
297 return $sanitized !== '' ? $sanitized : $default;
298 }
299 }
300