PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.2
WooCommerce v10.9.2
11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / vendor / opis / uri / src / UriTemplate.php
woocommerce / vendor / opis / uri / src Last commit date
Punycode.php 1 year ago PunycodeException.php 1 year ago Uri.php 1 year ago UriTemplate.php 1 year ago
UriTemplate.php
520 lines
1 <?php
2 /* ============================================================================
3 * Copyright 2021 Zindex Software
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 * ============================================================================ */
17
18 namespace Opis\Uri;
19
20 use Opis\String\UnicodeString;
21
22 class UriTemplate
23 {
24 /** @var string */
25 protected const TEMPLATE_VARSPEC_REGEX = '~^(?<varname>[a-zA-Z0-9\_\%\.]+)(?:(?<explode>\*)?|\:(?<prefix>\d+))?$~';
26
27 /** @var string */
28 protected const TEMPLATE_REGEX = <<<'REGEX'
29 ~\{
30 (?<operator>[+#./;&=,!@|\?])?
31 (?<varlist>
32 (?:(?P>varspec),)*
33 (?<varspec>(?:
34 [a-zA-Z0-9\_\%\.]+
35 (?:\*|\:\d+)?
36 ))
37 )
38 \}~x
39 REGEX;
40
41 /** @var array */
42 protected const TEMPLATE_TABLE = [
43 '' => [
44 'first' => '',
45 'sep' => ',',
46 'named' => false,
47 'ifemp' => '',
48 'allow' => false,
49 ],
50 '+' => [
51 'first' => '',
52 'sep' => ',',
53 'named' => false,
54 'ifemp' => '',
55 'allow' => true,
56 ],
57 '.' => [
58 'first' => '.',
59 'sep' => '.',
60 'named' => false,
61 'ifemp' => '',
62 'allow' => false,
63 ],
64 '/' => [
65 'first' => '/',
66 'sep' => '/',
67 'named' => false,
68 'ifemp' => '',
69 'allow' => false,
70 ],
71 ';' => [
72 'first' => ';',
73 'sep' => ';',
74 'named' => true,
75 'ifemp' => '',
76 'allow' => false,
77 ],
78 '?' => [
79 'first' => '?',
80 'sep' => '&',
81 'named' => true,
82 'ifemp' => '=',
83 'allow' => false,
84 ],
85 '&' => [
86 'first' => '&',
87 'sep' => '&',
88 'named' => true,
89 'ifemp' => '=',
90 'allow' => false,
91 ],
92 '#' => [
93 'first' => '#',
94 'sep' => ',',
95 'named' => false,
96 'ifemp' => '',
97 'allow' => true,
98 ],
99 ];
100
101 protected string $uri;
102
103 /** @var bool|null|array */
104 protected $parsed = false;
105
106 /**
107 * UriTemplate constructor.
108 * @param string $uri_template
109 */
110 public function __construct(string $uri_template)
111 {
112 $this->uri = $uri_template;
113 }
114
115 /**
116 * @param array $vars
117 * @return string
118 */
119 public function resolve(array $vars): string
120 {
121 if ($this->parsed === false) {
122 $this->parsed = $this->parse($this->uri);
123 }
124 if ($this->parsed === null || !$vars) {
125 return $this->uri;
126 }
127
128 $data = '';
129 $vars = $this->prepareVars($vars);
130
131 foreach ($this->parsed as $item) {
132 if (!is_array($item)) {
133 $data .= $item;
134 continue;
135 }
136
137 $data .= $this->parseTemplateExpression(
138 self::TEMPLATE_TABLE[$item['operator']],
139 $this->resolveVars($item['vars'], $vars)
140 );
141 }
142
143 return $data;
144 }
145
146 /**
147 * @return bool
148 */
149 public function hasPlaceholders(): bool
150 {
151 if ($this->parsed === false) {
152 $this->parse($this->uri);
153 }
154
155 return $this->parsed !== null;
156 }
157
158 /**
159 * @param string $uri
160 * @return array|null
161 */
162 protected function parse(string $uri): ?array
163 {
164 $placeholders = null;
165 preg_match_all(self::TEMPLATE_REGEX, $uri, $placeholders, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
166
167 if (!$placeholders) {
168 return null;
169 }
170
171 $dataIndex = -1;
172 $data = [];
173
174 $hasVars = false;
175 $nextOffset = 0;
176 foreach ($placeholders as &$p) {
177 $offset = $p[0][1];
178 if ($nextOffset < $offset) {
179 $data[] = substr($uri, $nextOffset, $offset - $nextOffset);
180 $dataIndex++;
181 }
182 $matched = $p[0][0];
183 $nextOffset = $offset + strlen($matched);
184
185 $operator = $p['operator'][0] ?? null;
186 if ($operator === null || !isset(self::TEMPLATE_TABLE[$operator])) {
187 if ($dataIndex >= 0 && is_string($data[$dataIndex])) {
188 $data[$dataIndex] .= $matched;
189 } else {
190 $data[] = $matched;
191 $dataIndex++;
192 }
193 continue;
194 }
195
196 $varList = $p['varlist'][0] ?? '';
197 $varList = $varList === '' ? [] : explode(',', $varList);
198 $p = null;
199
200 $varData = [];
201
202 foreach ($varList as $var) {
203 if (!preg_match(self::TEMPLATE_VARSPEC_REGEX, $var, $spec)) {
204 continue;
205 }
206
207 $varData[] = [
208 'name' => $spec['varname'],
209 'explode' => isset($spec['explode']) && $spec['explode'] === '*',
210 'prefix' => isset($spec['prefix']) ? (int)$spec['prefix'] : 0,
211 ];
212
213 unset($var, $spec);
214 }
215
216 if ($varData) {
217 $hasVars = true;
218 $data[] = [
219 'operator' => $operator,
220 'vars' => $varData,
221 ];
222 $dataIndex++;
223 } else {
224 if ($dataIndex >= 0 && is_string($data[$dataIndex])) {
225 $data[$dataIndex] .= $matched;
226 } else {
227 $data[] = $matched;
228 $dataIndex++;
229 }
230 }
231
232 unset($varData, $varList, $operator);
233 }
234
235 if (!$hasVars) {
236 return null;
237 }
238
239 $matched = substr($uri, $nextOffset);
240 if ($matched !== false && $matched !== '') {
241 if ($dataIndex >= 0 && is_string($data[$dataIndex])) {
242 $data[$dataIndex] .= $matched;
243 } else {
244 $data[] = $matched;
245 }
246 }
247
248 return $data;
249 }
250
251 /**
252 * Convert assoc arrays to objects
253 * @param array $vars
254 * @return array
255 */
256 protected function prepareVars(array $vars): array
257 {
258 foreach ($vars as &$value) {
259 if (is_scalar($value)) {
260 if (!is_string($value)) {
261 $value = (string)$value;
262 }
263 continue;
264 }
265
266 if (!is_array($value)) {
267 continue;
268 }
269
270 $len = count($value);
271 for ($i = 0; $i < $len; $i++) {
272 if (!array_key_exists($i, $value)) {
273 $value = (object)$value;
274 break;
275 }
276 }
277 }
278
279 return $vars;
280 }
281
282 /**
283 * @param array $vars
284 * @param array $data
285 * @return array
286 */
287 protected function resolveVars(array $vars, array $data): array
288 {
289 $resolved = [];
290
291 foreach ($vars as $info) {
292 $name = $info['name'];
293
294 if (!isset($data[$name])) {
295 continue;
296 }
297
298 $resolved[] = $info + ['value' => &$data[$name]];
299 }
300
301 return $resolved;
302 }
303
304 /**
305 * @param array $table
306 * @param array $data
307 * @return string
308 */
309 protected function parseTemplateExpression(array $table, array $data): string
310 {
311 $result = [];
312 foreach ($data as $var) {
313 $str = "";
314 if (is_string($var['value'])) {
315 if ($table['named']) {
316 $str .= $var['name'];
317 if ($var['value'] === '') {
318 $str .= $table['ifemp'];
319 } else {
320 $str .= '=';
321 }
322 }
323 if ($var['prefix']) {
324 $str .= $this->encodeTemplateString(self::prefix($var['value'], $var['prefix']), $table['allow']);
325 } else {
326 $str .= $this->encodeTemplateString($var['value'], $table['allow']);
327 }
328 } elseif ($var['explode']) {
329 $list = [];
330 if ($table['named']) {
331 if (is_array($var['value'])) {
332 foreach ($var['value'] as $v) {
333 if (is_null($v) || !is_scalar($v)) {
334 continue;
335 }
336 $v = $this->encodeTemplateString((string)$v, $table['allow']);
337 if ($v === '') {
338 $list[] = $var['name'] . $table['ifemp'];
339 } else {
340 $list[] = $var['name'] . '=' . $v;
341 }
342 }
343 } elseif (is_object($var['value'])) {
344 foreach ($var['value'] as $prop => $v) {
345 if (is_null($v) || !is_scalar($v)) {
346 continue;
347 }
348 $v = $this->encodeTemplateString((string)$v, $table['allow']);
349 $prop = $this->encodeTemplateString((string)$prop, $table['allow']);
350 if ($v === '') {
351 $list[] = $prop . $table['ifemp'];
352 } else {
353 $list[] = $prop . '=' . $v;
354 }
355 }
356 }
357 } else {
358 if (is_array($var['value'])) {
359 foreach ($var['value'] as $v) {
360 if (is_null($v) || !is_scalar($v)) {
361 continue;
362 }
363 $list[] = $this->encodeTemplateString($v, $table['allow']);
364 }
365 } elseif (is_object($var['value'])) {
366 foreach ($var['value'] as $prop => $v) {
367 if (is_null($v) || !is_scalar($v)) {
368 continue;
369 }
370 $v = $this->encodeTemplateString((string)$v, $table['allow']);
371 $prop = $this->encodeTemplateString((string)$prop, $table['allow']);
372 $list[] = $prop . '=' . $v;
373 }
374 }
375 }
376
377 if ($list) {
378 $str .= implode($table['sep'], $list);
379 }
380 unset($list);
381 } else {
382 if ($table['named']) {
383 $str .= $var['name'];
384 if ($var['value'] === '') {
385 $str .= $table['ifemp'];
386 } else {
387 $str .= '=';
388 }
389 }
390 $list = [];
391 if (is_array($var['value'])) {
392 foreach ($var['value'] as $v) {
393 $list[] = $this->encodeTemplateString($v, $table['allow']);
394 }
395 } elseif (is_object($var['value'])) {
396 foreach ($var['value'] as $prop => $v) {
397 $list[] = $this->encodeTemplateString((string)$prop, $table['allow']);
398 $list[] = $this->encodeTemplateString((string)$v, $table['allow']);
399 }
400 }
401 if ($list) {
402 $str .= implode(',', $list);
403 }
404 unset($list);
405 }
406
407 if ($str !== '') {
408 $result[] = $str;
409 }
410 }
411
412 if (!$result) {
413 return '';
414 }
415
416 $result = implode($table['sep'], $result);
417
418 if ($result !== '') {
419 $result = $table['first'] . $result;
420 }
421
422 return $result;
423 }
424
425 /**
426 * @param string $data
427 * @param bool $reserved
428 * @return string
429 */
430 protected function encodeTemplateString(string $data, bool $reserved): string
431 {
432 $skip = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~';
433
434 if ($reserved) {
435 $skip .= ':/?#[]@!$&\'()*+,;=';
436 }
437
438 $result = '';
439 $temp = '';
440 for ($i = 0, $len = strlen($data); $i < $len; $i++) {
441 if (strpos($skip, $data[$i]) !== false) {
442 if ($temp !== '') {
443 $result .= Uri::encodeComponent($temp);
444 $temp = '';
445 }
446 $result .= $data[$i];
447 continue;
448 }
449 if ($reserved && $data[$i] === '%') {
450 if (isset($data[$i + 1]) && isset($data[$i + 2])
451 && strpos('ABCDEF0123456789', $data[$i + 1]) !== false
452 && strpos('ABCDEF0123456789', $data[$i + 2]) !== false) {
453 if ($temp !== '') {
454 $result .= Uri::encodeComponent($temp);
455 }
456 $result .= '%' . $data[$i + 1] . $data[$i + 2];
457 $i += 3;
458 continue;
459 }
460 }
461 $temp .= $data[$i];
462 }
463
464 if ($temp !== '') {
465 $result .= Uri::encodeComponent($temp);
466 }
467
468 return $result;
469 }
470
471 /**
472 * @return string
473 */
474 public function value(): string
475 {
476 return $this->uri;
477 }
478
479 public function __toString(): string
480 {
481 return $this->uri;
482 }
483
484 /**
485 * @param string $uri
486 * @return bool
487 */
488 public static function isTemplate(string $uri): bool
489 {
490 $open = substr_count($uri, '{');
491 if ($open === 0) {
492 return false;
493 }
494 $close = substr_count($uri, '}');
495 if ($open !== $close) {
496 return false;
497 }
498
499 return (bool)preg_match(self::TEMPLATE_REGEX, $uri);
500 }
501
502 /**
503 * @param string $str
504 * @param int $len
505 * @return string
506 */
507 protected static function prefix(string $str, int $len): string
508 {
509 if ($len === 0) {
510 return '';
511 }
512
513 if ($len >= strlen($str)) {
514 // Prefix is longer than string length
515 return $str;
516 }
517
518 return (string)UnicodeString::from($str)->substring(0, $len);
519 }
520 }