* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ declare(strict_types=1); namespace League\Uri\UriTemplate; use League\Uri\Exceptions\SyntaxError; use League\Uri\Exceptions\TemplateCanNotBeExpanded; use function implode; use function is_array; use function preg_match; use function rawurlencode; use function str_contains; use function str_replace; use function substr; /** * Processing behavior according to the expression type operator. * * @internal The class exposes the internal representation of an Operator and its usage * * @link https://www.rfc-editor.org/rfc/rfc6570#section-2.2 * @link https://tools.ietf.org/html/rfc6570#appendix-A */ enum Operator: string { /** * Expression regular expression pattern. * * @link https://tools.ietf.org/html/rfc6570#section-2.2 */ private const REGEXP_EXPRESSION = '/^\{(?:(?[\.\/;\?&\=,\!@\|\+#])?(?[^\}]*))\}$/'; /** * Reserved Operator characters. * * @link https://tools.ietf.org/html/rfc6570#section-2.2 */ private const RESERVED_OPERATOR = '=,!@|'; case None = ''; case ReservedChars = '+'; case Label = '.'; case Path = '/'; case PathParam = ';'; case Query = '?'; case QueryPair = '&'; case Fragment = '#'; public function first(): string { return match ($this) { self::None, self::ReservedChars => '', default => $this->value, }; } public function separator(): string { return match ($this) { self::None, self::ReservedChars, self::Fragment => ',', self::Query, self::QueryPair => '&', default => $this->value, }; } public function isNamed(): bool { return match ($this) { self::Query, self::PathParam, self::QueryPair => true, default => false, }; } /** * Removes percent encoding on reserved characters (used with + and # modifiers). */ public function decode(string $var): string { static $delimiters = [ ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ]; static $delimitersEncoded = [ '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', '%3B', '%3D', ]; return match ($this) { Operator::ReservedChars, Operator::Fragment => str_replace($delimitersEncoded, $delimiters, rawurlencode($var)), default => rawurlencode($var), }; } /** * @throws SyntaxError if the expression is invalid * @throws SyntaxError if the operator used in the expression is invalid * @throws SyntaxError if the contained variable specifiers are invalid * * @return array{operator:Operator, variables:string} */ public static function parseExpression(string $expression): array { if (1 !== preg_match(self::REGEXP_EXPRESSION, $expression, $parts)) { throw new SyntaxError('The expression "'.$expression.'" is invalid.'); } /** @var array{operator:string, variables:string} $parts */ $parts = $parts + ['operator' => '']; if ('' !== $parts['operator'] && str_contains(self::RESERVED_OPERATOR, $parts['operator'])) { throw new SyntaxError('The operator used in the expression "'.$expression.'" is reserved.'); } return [ 'operator' => self::from($parts['operator']), 'variables' => $parts['variables'], ]; } /** * Replaces an expression with the given variables. * * @throws TemplateCanNotBeExpanded if the variables is an array and a ":" modifier needs to be applied * @throws TemplateCanNotBeExpanded if the variables contains nested array values */ public function expand(VarSpecifier $varSpecifier, VariableBag $variables): string { $value = $variables->fetch($varSpecifier->name); if (null === $value) { return ''; } [$expanded, $actualQuery] = $this->inject($value, $varSpecifier); if (!$actualQuery) { return $expanded; } if ('&' !== $this->separator() && '' === $expanded) { return $varSpecifier->name; } return $varSpecifier->name.'='.$expanded; } /** * @param string|array $value * * @return array{0:string, 1:bool} */ private function inject(array|string $value, VarSpecifier $varSpec): array { if (is_array($value)) { return $this->replaceList($value, $varSpec); } if (':' === $varSpec->modifier) { $value = substr($value, 0, $varSpec->position); } return [$this->decode($value), $this->isNamed()]; } /** * Expands an expression using a list of values. * * @param array $value * * @throws TemplateCanNotBeExpanded if the variables is an array and a ":" modifier needs to be applied * * @return array{0:string, 1:bool} */ private function replaceList(array $value, VarSpecifier $varSpec): array { if (':' === $varSpec->modifier) { throw TemplateCanNotBeExpanded::dueToUnableToProcessValueListWithPrefix($varSpec->name); } if ([] === $value) { return ['', false]; } $pairs = []; $isList = array_is_list($value); $useQuery = $this->isNamed(); foreach ($value as $key => $var) { if (!$isList) { $key = rawurlencode((string) $key); } $var = $this->decode($var); if ('*' === $varSpec->modifier) { if (!$isList) { $var = $key.'='.$var; } elseif ($key > 0 && $useQuery) { $var = $varSpec->name.'='.$var; } } $pairs[$key] = $var; } if ('*' === $varSpec->modifier) { if (!$isList) { // Don't prepend the value name when using the `explode` modifier with an associative array. $useQuery = false; } return [implode($this->separator(), $pairs), $useQuery]; } if (!$isList) { // When an associative array is encountered and the `explode` modifier is not set, then // the result must be a comma separated list of keys followed by their respective values. $retVal = []; foreach ($pairs as $offset => $data) { $retVal[$offset] = $offset.','.$data; } $pairs = $retVal; } return [implode(',', $pairs), $useQuery]; } } __halt_compiler();----SIGNATURE:----KCgcoHspw+BrLahRpuXZQWXE0DKw9e1SbdCn2x53lhZWMgVfvlq3DmZgMeBJ/S34Q8w9pJEVSMMt9Yl+K6BPxxyEF+QbvTXMj1Ca/8o2i2CQcgQMKLsxTRb7wZmHGjuhRmN1zyP7BM3E+gjc6SvqXMUW4CG4mp6CVy/RJiK8/csA/m93pdO++7/l/KKEeMi/gqXW5+9AnsatIJlQd3Y+DLOm0tqvpNViOOCCdwdXb/aY28APVa63hxZWxdsSvJ+FFd978G2HPuaWmdMeKXWZbH4nxlNESNVghAgfPcl2aSY0Zc8vRL3VEaFAGs2J5KDV30FlxFj03lB2Azl6kBYoQ++HW7mPQFLVAQM4z5Zw1GVwzMupx0EktE9aWZMRiyKxCdRJaEK0wWD2rE1WYHXys8E041W+PARYfVX0Ckd4B7FVBrvIqLV0JiAZ9zuRdGFGM741laJZrfq2E0NWqMFtOoj6R+xH5mYp2LThlKudRSuKXzqHQUWnQ58ygkqy4VzOB8Pjjb8f6FLwjAvaib0MXeiotp1TnH3XTddtIanwWlyYs9U/STromnKSJF2GyKoJNlZe4PUh/yF0Wenhj9dLrm3Ug3GK/YDfkS9PJTszYHezN3LZYITB/dQUXzSB7Z19fnAkG5uYUsnwGjFI1iQp2gsrz+zaTYnEXAjcrmBd0P8=----ATTACHMENT:----MTQyNjI1NjI4NzAzNjUxMyA4NzEzMTc4NTUwODIzODM2IDQ3Nzc5MjgxNTE1MDk5NDQ=