PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.2.0
Kirki – Freeform Page Builder, Website Builder & Customizer v6.2.0
6.2.0 6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / libraries / framework / Routing / RouteParser.php
kirki / libraries / framework / Routing Last commit date
CurrentRoute.php 5 days ago RouteParser.php 5 days ago SiteRouter.php 5 days ago
RouteParser.php
172 lines
1 <?php
2
3 /**
4 * Shared URI segment parser for REST and site routes.
5 * Supports {name} and {name:type} syntax plus where() pattern overrides.
6 *
7 * @package Framework
8 * @subpackage Routing
9 * @since 1.0.0
10 */
11 namespace Kirki\Framework\Routing;
12
13 \defined('ABSPATH') || exit;
14 use Kirki\Framework\Sanitizer;
15 class RouteParser
16 {
17 /**
18 * Predefined param types mapped to regex fragments without capturing parens.
19 *
20 * @var array<string, string>
21 *
22 * @since 1.0.0
23 */
24 protected const TYPES = ['any' => '[^/]+', 'int' => '\\d+', 'alpha' => '[a-zA-Z]+', 'alnum' => '[a-zA-Z0-9]+', 'slug' => '[a-zA-Z0-9-]+'];
25 /**
26 * Sanitizer callbacks keyed by param type.
27 *
28 * @var array<string, string>
29 *
30 * @since 1.0.0
31 */
32 protected const SANITIZERS = ['int' => Sanitizer::INT, 'alpha' => Sanitizer::TEXT, 'alnum' => Sanitizer::TEXT, 'slug' => Sanitizer::TITLE, 'any' => Sanitizer::TEXT];
33 /**
34 * Parse a Laravel-style URI into an ordered list of segments.
35 *
36 * @param string $uri Route URI such as products/{id:int}.
37 *
38 * @return array
39 *
40 * @since 1.0.0
41 */
42 public function parse_segments(string $uri)
43 {
44 $parts = \explode('/', \trim($uri, '/'));
45 $segments = [];
46 foreach ($parts as $part) {
47 if ($part === '') {
48 continue;
49 }
50 if (\preg_match('/^\\{(\\w+)(?::(.+))?\\}$/', $part, $matches)) {
51 $segments[] = ['type' => 'param', 'name' => $matches[1], 'inline_type' => $matches[2] ?? null];
52 continue;
53 }
54 $segments[] = ['type' => 'literal', 'value' => $part];
55 }
56 return $segments;
57 }
58 /**
59 * Extract inline param types from parsed segments.
60 *
61 * @param array $segments Parsed segments.
62 *
63 * @return array<string, string>
64 *
65 * @since 1.0.0
66 */
67 public function extract_param_types(array $segments)
68 {
69 $param_types = [];
70 foreach ($segments as $segment) {
71 if ($segment['type'] !== 'param' || $segment['inline_type'] === null) {
72 continue;
73 }
74 $param_types[$segment['name']] = $segment['inline_type'];
75 }
76 return $param_types;
77 }
78 /**
79 * Resolve a type keyword or raw regex to a regex fragment.
80 *
81 * @param string $type Type keyword or raw regex.
82 *
83 * @return string
84 *
85 * @since 1.0.0
86 */
87 public function resolve_regex(string $type)
88 {
89 return static::TYPES[$type] ?? $type;
90 }
91 /**
92 * Resolve a sanitizer rule for a param type.
93 *
94 * @param string $type Type keyword.
95 *
96 * @return string
97 *
98 * @since 1.0.0
99 */
100 public function resolve_sanitizer(string $type)
101 {
102 return static::SANITIZERS[$type] ?? Sanitizer::TEXT;
103 }
104 /**
105 * Build a REST-compatible endpoint pattern with named capture groups.
106 *
107 * @param string $endpoint The route endpoint.
108 * @param array $patterns Param name to regex overrides from where().
109 *
110 * @return string
111 *
112 * @since 1.0.0
113 */
114 public function format_rest_endpoint(string $endpoint, array $patterns = [])
115 {
116 $segments = $this->parse_segments($endpoint);
117 $inline_types = $this->extract_param_types($segments);
118 $compiled = [];
119 foreach ($segments as $segment) {
120 if ($segment['type'] === 'literal') {
121 $compiled[] = $segment['value'];
122 continue;
123 }
124 $name = $segment['name'];
125 $type = $patterns[$name] ?? $inline_types[$name] ?? 'any';
126 $regex = $this->resolve_regex($type);
127 $compiled[] = '(?P<' . $name . '>' . $regex . ')';
128 }
129 return \implode('/', $compiled);
130 }
131 /**
132 * Build a site-route rewrite matching pattern and ordered param names.
133 *
134 * @param array $segments Parsed segments.
135 * @param array $param_types Param name to type/regex map.
136 *
137 * @return array{0:string,1:string[]}
138 *
139 * @since 1.0.0
140 */
141 public function build_site_pattern(array $segments, array $param_types = [])
142 {
143 $compiled = [];
144 $param_names = [];
145 foreach ($segments as $segment) {
146 if ($segment['type'] === 'literal') {
147 $compiled[] = \preg_quote($segment['value'], '#');
148 continue;
149 }
150 $name = $segment['name'];
151 $type = $param_types[$name] ?? 'any';
152 $param_names[] = $name;
153 $compiled[] = '(' . $this->resolve_regex($type) . ')';
154 }
155 $pattern = '^' . \implode('/', $compiled) . '/?$';
156 return [$pattern, $param_names];
157 }
158 /**
159 * Normalize a URI by stripping leading/trailing slashes.
160 *
161 * @param string $uri The URI.
162 *
163 * @return string
164 *
165 * @since 1.0.0
166 */
167 public function normalize_uri(string $uri)
168 {
169 return \trim($uri, '/');
170 }
171 }
172