PluginProbe
Yatra – Travel Booking & Tour Operator Software / trunk
Yatra – Travel Booking & Tour Operator Software vtrunk
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 2.0.10 All 83 releases
yatra / app / Utils / FrontendThemeCss.php

FrontendThemeCss.php in Yatra – Travel Booking & Tour Operator Software trunk, at app/Utils/FrontendThemeCss.php

207 lines 6.5 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\Utils;
6
7 /**
8 * Frontend brand color → CSS custom properties for Yatra trip/booking/listing UI.
9 */
10 final class FrontendThemeCss
11 {
12 public const DEFAULT_PRIMARY = '#3b82f6';
13
14 /**
15 * @param mixed $value
16 */
17 public static function sanitizePrimaryColor($value): string
18 {
19 if (!is_string($value)) {
20 return self::DEFAULT_PRIMARY;
21 }
22 $s = trim($value);
23 if ($s === '') {
24 return self::DEFAULT_PRIMARY;
25 }
26 if ($s[0] !== '#') {
27 $s = '#' . $s;
28 }
29 if (!preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $s)) {
30 return self::DEFAULT_PRIMARY;
31 }
32 if (strlen($s) === 4) {
33 $s = '#' . $s[1] . $s[1] . $s[2] . $s[2] . $s[3] . $s[3];
34 }
35
36 return strtolower($s);
37 }
38
39 /**
40 * Inline :root rules to override bundled defaults. Safe to print inside <style> / wp_add_inline_style.
41 */
42 public static function buildInlineRootCss(string $primaryHex): string
43 {
44 $primary = self::sanitizePrimaryColor($primaryHex);
45 $dark = self::mix($primary, '#000000', 0.14);
46 $darker = self::mix($primary, '#000000', 0.32);
47 $light = self::mix($primary, '#ffffff', 0.28);
48 $softBg = self::mix($primary, '#ffffff', 0.92);
49 $darkModeSurface = self::mix($primary, '#000000', 0.72);
50 $darkModeAccent = self::mix($primary, '#ffffff', 0.22);
51
52 $primaryE = esc_attr($primary);
53 $darkE = esc_attr($dark);
54 $darkerE = esc_attr($darker);
55 $lightE = esc_attr($light);
56 $softE = esc_attr($softBg);
57 $dmSurfE = esc_attr($darkModeSurface);
58 $dmAccE = esc_attr($darkModeAccent);
59
60 return ':root{'
61 . '--yatra-primary:' . $primaryE . ';'
62 . '--yatra-primary-dark:' . $darkE . ';'
63 . '--yatra-primary-light:' . $lightE . ';'
64 . '--yatra-primary-color:' . $primaryE . ';'
65 . '--yatra-primary-light-soft:' . $softE . ';'
66 . '--yatra-primary-color-dark:' . $dmAccE . ';'
67 . '--yatra-primary-light-dark:' . $dmSurfE . ';'
68 . '--yatra-primary-darker:' . $darkerE . ';'
69 . '}';
70 }
71
72 /**
73 * @return array{r:int,g:int,b:int}|null
74 */
75 private static function hexToRgb(string $hex): ?array
76 {
77 if (strlen($hex) !== 7 || $hex[0] !== '#') {
78 return null;
79 }
80 $r = hexdec(substr($hex, 1, 2));
81 $g = hexdec(substr($hex, 3, 2));
82 $b = hexdec(substr($hex, 5, 2));
83
84 return ['r' => $r, 'g' => $g, 'b' => $b];
85 }
86
87 private static function mix(string $fromHex, string $towardHex, float $ratio): string
88 {
89 $ratio = max(0.0, min(1.0, $ratio));
90 $a = self::hexToRgb(self::sanitizePrimaryColor($fromHex));
91 $b = self::hexToRgb(self::sanitizePrimaryColor($towardHex));
92 if ($a === null || $b === null) {
93 return self::DEFAULT_PRIMARY;
94 }
95 $r = (int) round($a['r'] * (1 - $ratio) + $b['r'] * $ratio);
96 $g = (int) round($a['g'] * (1 - $ratio) + $b['g'] * $ratio);
97 $bb = (int) round($a['b'] * (1 - $ratio) + $b['b'] * $ratio);
98
99 return sprintf('#%02x%02x%02x', max(0, min(255, $r)), max(0, min(255, $g)), max(0, min(255, $bb)));
100 }
101
102 /**
103 * Max content width from block theme (theme.json) or $content_width, for --yatra-container-max-width.
104 *
105 * @return non-falsy-string|null Safe CSS length (e.g. 1200px, 40rem) or null to keep bundled default.
106 */
107 public static function resolveThemeContainerMaxWidth(): ?string
108 {
109 $filtered = apply_filters('yatra_container_max_width', null);
110 if (is_string($filtered)) {
111 $t = trim($filtered);
112 if ($t !== '' && self::isSafeCssLengthToken($t)) {
113 if (preg_match('/^\d+$/', $t)) {
114 return (int) $t . 'px';
115 }
116
117 return $t;
118 }
119 }
120
121 if (function_exists('wp_get_global_settings')) {
122 $settings = wp_get_global_settings();
123 $layout = is_array($settings['layout'] ?? null) ? $settings['layout'] : [];
124 $wide = isset($layout['wideSize']) && is_string($layout['wideSize']) ? trim($layout['wideSize']) : '';
125 if ($wide !== '' && self::isSafeCssLengthToken($wide)) {
126 return $wide;
127 }
128 $content = isset($layout['contentSize']) && is_string($layout['contentSize']) ? trim($layout['contentSize']) : '';
129 if ($content !== '' && self::isSafeCssLengthToken($content)) {
130 return $content;
131 }
132 }
133
134 global $content_width;
135 if (is_int($content_width) || is_float($content_width)) {
136 $w = (int) round((float) $content_width);
137 if ($w > 0 && $w <= 3840) {
138 return $w . 'px';
139 }
140 }
141 if (is_string($content_width)) {
142 $cw = trim($content_width);
143 if ($cw !== '' && self::isSafeCssLengthToken($cw)) {
144 return $cw;
145 }
146 if ($cw !== '' && ctype_digit($cw)) {
147 $w = (int) $cw;
148 if ($w > 0 && $w <= 3840) {
149 return $w . 'px';
150 }
151 }
152 }
153
154 return null;
155 }
156
157 /**
158 * Sanitize Design → container max width (stored setting). Empty string = use theme / filter resolution.
159 */
160 public static function sanitizeContainerMaxWidthSetting($value): string
161 {
162 if (!is_string($value)) {
163 return '';
164 }
165 $t = trim($value);
166 if ($t === '') {
167 return '';
168 }
169 if (preg_match('/^\d+$/', $t)) {
170 $w = (int) $t;
171 if ($w > 0 && $w <= 3840) {
172 return $w . 'px';
173 }
174
175 return '';
176 }
177 if (self::isSafeCssLengthToken($t)) {
178 return $t;
179 }
180
181 return '';
182 }
183
184 /**
185 * @param non-falsy-string $value
186 */
187 private static function isSafeCssLengthToken(string $value): bool
188 {
189 if (strlen($value) > 96) {
190 return false;
191 }
192 if (preg_match('/[<>{}\';\\\\]|\/\*/', $value)) {
193 return false;
194 }
195 if (preg_match('/^\d+$/', $value)) {
196 $w = (int) $value;
197
198 return $w > 0 && $w <= 3840;
199 }
200
201 return (bool) preg_match(
202 '/^(?:clamp\([^)]+\)|min\([^)]+\)|max\([^)]+\)|calc\([^)]+\)|[\d.]+\s*(?:px|rem|em|vw|vh|%))$/i',
203 $value
204 );
205 }
206 }
207