PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.15.4
Independent Analytics – WordPress Analytics Plugin v2.15.4
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / vendor / league / uri / src / UriTemplate / Expression.php
independent-analytics / vendor / league / uri / src / UriTemplate Last commit date
Expression.php 1 month ago Template.php 1 month ago VarSpecifier.php 1 month ago VariableBag.php 1 month ago
Expression.php
263 lines
1 <?php
2
3 /**
4 * League.Uri (https://uri.thephpleague.com)
5 *
6 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 declare (strict_types=1);
12 namespace IAWPSCOPED\League\Uri\UriTemplate;
13
14 use IAWPSCOPED\League\Uri\Exceptions\SyntaxError;
15 use IAWPSCOPED\League\Uri\Exceptions\TemplateCanNotBeExpanded;
16 use function array_filter;
17 use function array_keys;
18 use function array_map;
19 use function array_unique;
20 use function explode;
21 use function implode;
22 use function preg_match;
23 use function rawurlencode;
24 use function str_replace;
25 use function strpos;
26 use function substr;
27 /** @internal */
28 final class Expression
29 {
30 /**
31 * Expression regular expression pattern.
32 *
33 * @link https://tools.ietf.org/html/rfc6570#section-2.2
34 */
35 private const REGEXP_EXPRESSION = '/^\\{
36 (?:
37 (?<operator>[\\.\\/;\\?&\\=,\\!@\\|\\+#])?
38 (?<variables>[^\\}]*)
39 )
40 \\}$/x';
41 /**
42 * Reserved Operator characters.
43 *
44 * @link https://tools.ietf.org/html/rfc6570#section-2.2
45 */
46 private const RESERVED_OPERATOR = '=,!@|';
47 /**
48 * Processing behavior according to the expression type operator.
49 *
50 * @link https://tools.ietf.org/html/rfc6570#appendix-A
51 */
52 private const OPERATOR_HASH_LOOKUP = ['' => ['prefix' => '', 'joiner' => ',', 'query' => \false], '+' => ['prefix' => '', 'joiner' => ',', 'query' => \false], '#' => ['prefix' => '#', 'joiner' => ',', 'query' => \false], '.' => ['prefix' => '.', 'joiner' => '.', 'query' => \false], '/' => ['prefix' => '/', 'joiner' => '/', 'query' => \false], ';' => ['prefix' => ';', 'joiner' => ';', 'query' => \true], '?' => ['prefix' => '?', 'joiner' => '&', 'query' => \true], '&' => ['prefix' => '&', 'joiner' => '&', 'query' => \true]];
53 private string $operator;
54 /** @var array<VarSpecifier> */
55 private array $varSpecifiers;
56 private string $joiner;
57 /** @var array<string> */
58 private array $variableNames;
59 private string $expressionString;
60 private function __construct(string $operator, VarSpecifier ...$varSpecifiers)
61 {
62 $this->operator = $operator;
63 $this->varSpecifiers = $varSpecifiers;
64 $this->joiner = self::OPERATOR_HASH_LOOKUP[$operator]['joiner'];
65 $this->variableNames = $this->setVariableNames();
66 $this->expressionString = $this->setExpressionString();
67 }
68 /**
69 * @return array<string>
70 */
71 private function setVariableNames() : array
72 {
73 return array_unique(array_map(static fn(VarSpecifier $varSpecifier): string => $varSpecifier->name(), $this->varSpecifiers));
74 }
75 private function setExpressionString() : string
76 {
77 $varSpecifierString = implode(',', array_map(static fn(VarSpecifier $variable): string => $variable->toString(), $this->varSpecifiers));
78 return '{' . $this->operator . $varSpecifierString . '}';
79 }
80 /**
81 * {@inheritDoc}
82 */
83 public static function __set_state(array $properties) : self
84 {
85 return new self($properties['operator'], ...$properties['varSpecifiers']);
86 }
87 /**
88 * @throws SyntaxError if the expression is invalid
89 * @throws SyntaxError if the operator used in the expression is invalid
90 * @throws SyntaxError if the variable specifiers is invalid
91 */
92 public static function createFromString(string $expression) : self
93 {
94 if (1 !== preg_match(self::REGEXP_EXPRESSION, $expression, $parts)) {
95 throw new SyntaxError('The expression "' . $expression . '" is invalid.');
96 }
97 /** @var array{operator:string, variables:string} $parts */
98 $parts = $parts + ['operator' => ''];
99 if ('' !== $parts['operator'] && \false !== strpos(self::RESERVED_OPERATOR, $parts['operator'])) {
100 throw new SyntaxError('The operator used in the expression "' . $expression . '" is reserved.');
101 }
102 return new Expression($parts['operator'], ...array_map(static fn(string $varSpec): VarSpecifier => VarSpecifier::createFromString($varSpec), explode(',', $parts['variables'])));
103 }
104 /**
105 * Returns the expression string representation.
106 *
107 */
108 public function toString() : string
109 {
110 return $this->expressionString;
111 }
112 /**
113 * @return array<string>
114 */
115 public function variableNames() : array
116 {
117 return $this->variableNames;
118 }
119 public function expand(VariableBag $variables) : string
120 {
121 $parts = [];
122 foreach ($this->varSpecifiers as $varSpecifier) {
123 $parts[] = $this->replace($varSpecifier, $variables);
124 }
125 $expanded = implode($this->joiner, array_filter($parts, static fn($value): bool => '' !== $value));
126 if ('' === $expanded) {
127 return $expanded;
128 }
129 $prefix = self::OPERATOR_HASH_LOOKUP[$this->operator]['prefix'];
130 if ('' === $prefix) {
131 return $expanded;
132 }
133 return $prefix . $expanded;
134 }
135 /**
136 * Replaces an expression with the given variables.
137 *
138 * @throws TemplateCanNotBeExpanded if the variables is an array and a ":" modifier needs to be applied
139 * @throws TemplateCanNotBeExpanded if the variables contains nested array values
140 */
141 private function replace(VarSpecifier $varSpec, VariableBag $variables) : string
142 {
143 $value = $variables->fetch($varSpec->name());
144 if (null === $value) {
145 return '';
146 }
147 $useQuery = self::OPERATOR_HASH_LOOKUP[$this->operator]['query'];
148 [$expanded, $actualQuery] = $this->inject($value, $varSpec, $useQuery);
149 if (!$actualQuery) {
150 return $expanded;
151 }
152 if ('&' !== $this->joiner && '' === $expanded) {
153 return $varSpec->name();
154 }
155 return $varSpec->name() . '=' . $expanded;
156 }
157 /**
158 * @param string|array<string> $value
159 *
160 * @return array{0:string, 1:bool}
161 */
162 private function inject($value, VarSpecifier $varSpec, bool $useQuery) : array
163 {
164 if (\is_string($value)) {
165 return $this->replaceString($value, $varSpec, $useQuery);
166 }
167 return $this->replaceList($value, $varSpec, $useQuery);
168 }
169 /**
170 * Expands an expression using a string value.
171 *
172 * @return array{0:string, 1:bool}
173 */
174 private function replaceString(string $value, VarSpecifier $varSpec, bool $useQuery) : array
175 {
176 if (':' === $varSpec->modifier()) {
177 $value = substr($value, 0, $varSpec->position());
178 }
179 $expanded = rawurlencode($value);
180 if ('+' === $this->operator || '#' === $this->operator) {
181 return [$this->decodeReserved($expanded), $useQuery];
182 }
183 return [$expanded, $useQuery];
184 }
185 /**
186 * Expands an expression using a list of values.
187 *
188 * @param array<string> $value
189 *
190 * @throws TemplateCanNotBeExpanded if the variables is an array and a ":" modifier needs to be applied
191 *
192 * @return array{0:string, 1:bool}
193 */
194 private function replaceList(array $value, VarSpecifier $varSpec, bool $useQuery) : array
195 {
196 if ([] === $value) {
197 return ['', \false];
198 }
199 if (':' === $varSpec->modifier()) {
200 throw TemplateCanNotBeExpanded::dueToUnableToProcessValueListWithPrefix($varSpec->name());
201 }
202 $pairs = [];
203 $isAssoc = $this->isAssoc($value);
204 foreach ($value as $key => $var) {
205 if ($isAssoc) {
206 $key = rawurlencode((string) $key);
207 }
208 $var = rawurlencode($var);
209 if ('+' === $this->operator || '#' === $this->operator) {
210 $var = $this->decodeReserved($var);
211 }
212 if ('*' === $varSpec->modifier()) {
213 if ($isAssoc) {
214 $var = $key . '=' . $var;
215 } elseif ($key > 0 && $useQuery) {
216 $var = $varSpec->name() . '=' . $var;
217 }
218 }
219 $pairs[$key] = $var;
220 }
221 if ('*' === $varSpec->modifier()) {
222 if ($isAssoc) {
223 // Don't prepend the value name when using the explode
224 // modifier with an associative array.
225 $useQuery = \false;
226 }
227 return [implode($this->joiner, $pairs), $useQuery];
228 }
229 if ($isAssoc) {
230 // When an associative array is encountered and the
231 // explode modifier is not set, then the result must be
232 // a comma separated list of keys followed by their
233 // respective values.
234 foreach ($pairs as $offset => &$data) {
235 $data = $offset . ',' . $data;
236 }
237 unset($data);
238 }
239 return [implode(',', $pairs), $useQuery];
240 }
241 /**
242 * Determines if an array is associative.
243 *
244 * This makes the assumption that input arrays are sequences or hashes.
245 * This assumption is a trade-off for accuracy in favor of speed, but it
246 * should work in almost every case where input is supplied for a URI
247 * template.
248 */
249 private function isAssoc(array $array) : bool
250 {
251 return [] !== $array && 0 !== array_keys($array)[0];
252 }
253 /**
254 * Removes percent encoding on reserved characters (used with + and # modifiers).
255 */
256 private function decodeReserved(string $str) : string
257 {
258 static $delimiters = [':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='];
259 static $delimitersEncoded = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', '%3B', '%3D'];
260 return str_replace($delimitersEncoded, $delimiters, $str);
261 }
262 }
263