Файловый менеджер - Редактировать - /home/tuudkjt/globeasy/wp-includes/ID3/opis.tar
Назад
string/src/Exception/InvalidCodePointException.php 0000777 00000002450 15252016106 0016370 0 ustar 00 <?php /* =========================================================================== * Copyright 2020-2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\String\Exception; use Throwable; class InvalidCodePointException extends UnicodeException { /** * @var mixed */ protected $codePoint; /** * @param $codePoint * @param Throwable|null $previous */ public function __construct($codePoint, ?Throwable $previous = null) { parent::__construct("Invalid code point", 0, $previous); $this->codePoint = $codePoint; } /** * @return mixed */ public function codePoint() { return$this->codePoint; } } string/src/Exception/InvalidStringException.php 0000777 00000003077 15252016106 0015760 0 ustar 00 <?php /* =========================================================================== * Copyright 2020-2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\String\Exception; use Throwable; class InvalidStringException extends UnicodeException { /** * @var string */ protected string $string; /** * @var int */ protected int $offset; /** * @param string $string * @param int $offset * @param Throwable|null $previous */ public function __construct(string $string, int $offset = -1, ?Throwable $previous = null) { parent::__construct("Invalid UTF-8 string at offset {$offset}", 0, $previous); $this->string = $string; $this->offset = $offset; } /** * @return string */ public function string(): string { return $this->string; } /** * @return int */ public function offset(): int { return $this->offset; } } string/src/Exception/UnicodeException.php 0000777 00000001553 15252016106 0014566 0 ustar 00 <?php /* =========================================================================== * Copyright 2020-2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\String\Exception; use RuntimeException; class UnicodeException extends RuntimeException { } string/src/UnicodeString.php 0000777 00000127574 15252016106 0012154 0 ustar 00 <?php /* =========================================================================== * Copyright 2018-2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\String; use RuntimeException; use OutOfBoundsException; use Countable, ArrayAccess; use JsonSerializable; use Opis\String\Exception\{ UnicodeException, InvalidStringException, InvalidCodePointException }; class UnicodeString implements Countable, ArrayAccess, JsonSerializable { const KEEP_CASE = 0; const LOWER_CASE = 1; const UPPER_CASE = 2; const FOLD_CASE = 3; const ASCII_CONV = 4; /** * @var int[] */ private array $codes; /** * @var string[]|null */ private ?array $chars = null; private int $length; private ?string $str = null; private ?array $cache = null; /** * @var int[][] */ private static array $maps = []; /** * @param int[] $codes */ private function __construct(array $codes = []) { $this->codes = $codes; $this->length = count($codes); } /** * @return int[] */ public function codePoints(): array { return $this->codes; } /** * @return string[] */ public function chars(): array { if ($this->chars === null) { $this->chars = self::getCharsFromCodePoints($this->codes); } return $this->chars; } /** * @return int */ public function length(): int { return $this->length; } /** * @return bool */ public function isEmpty(): bool { return $this->length === 0; } /** * @param string|self|int[]|string[] $text * @param bool $ignoreCase * @return bool */ public function equals($text, bool $ignoreCase = false): bool { return $this->compareTo($text, $ignoreCase) === 0; } /** * @param string|self|int[]|string[] $text * @param bool $ignoreCase * @return int */ public function compareTo($text, bool $ignoreCase = false): int { $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; $text = self::resolveCodePoints($text, $mode); $length = count($text); if ($length !== $this->length) { return $this->length <=> $length; } return $this->getMappedCodes($mode) <=> $text; } /** * @param string|self|int[]|string[] $text * @param bool $ignoreCase * @return bool */ public function contains($text, bool $ignoreCase = false): bool { return $this->indexOf($text, 0, $ignoreCase) !== -1; } /** * @param string|self|int[]|string[] $text * @param bool $ignoreCase * @return bool */ public function startsWith($text, bool $ignoreCase = false): bool { $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; $text = self::resolveCodePoints($text, $mode); $len = count($text); if ($len === 0 || $len > $this->length) { return false; } return array_slice($this->getMappedCodes($mode), 0, $len) === $text; } /** * @param string|self|int[]|string[] $text * @param bool $ignoreCase * @return bool */ public function endsWith($text, bool $ignoreCase = false): bool { $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; $text = self::resolveCodePoints($text, $mode); if (empty($text)) { return false; } $codes = $this->getMappedCodes($mode); $offset = $this->length - count($text); if ($offset < 0) { return false; } return array_slice($codes, $offset) === $text; } /** * @param string|self|int[]|string[] $text * @param int $offset * @param bool $ignoreCase * @return int */ public function indexOf($text, int $offset = 0, bool $ignoreCase = false): int { if ($offset < 0) { $offset += $this->length; } if ($offset < 0 || $offset >= $this->length) { return -1; } $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; $text = self::resolveCodePoints($text, $mode); $len = count($text); if ($len === 0 || $offset + $len > $this->length) { return -1; } return $this->doIndexOf($this->getMappedCodes($mode), $text, $offset); } /** * @param string|self|int[]|string[] $text * @param int $offset * @param bool $ignoreCase * @return int */ public function lastIndexOf($text, int $offset = 0, bool $ignoreCase = false): int { if ($offset < 0) { $start = $this->length + $offset; if ($start < 0) { return -1; } $last = 0; } else { if ($offset >= $this->length) { return -1; } $start = $this->length - 1; $last = $offset; } $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; $text = self::resolveCodePoints($text, $mode); $len = count($text); if ($len === 0) { return -1; } if ($offset < 0) { if ($len > $this->length) { return -1; } $start = min($start, $this->length - $len); } elseif ($offset + $len > $this->length) { return -1; } $codes = $this->getMappedCodes($mode); for ($i = $start; $i >= $last; $i--) { $match = true; for ($j = 0; $j < $len; $j++) { if ($codes[$i + $j] !== $text[$j]) { $match = false; break; } } if ($match) { return $i; } } return -1; } /** * @param string|self|int[]|string[] $text * @param bool $ignoreCase * @param bool $allowPrefixOnly If true the result can contain only the prefix * @return $this */ public function ensurePrefix($text, bool $ignoreCase = false, bool $allowPrefixOnly = true): self { $text = self::resolveCodePoints($text); $len = count($text); if ($len === 0) { return clone $this; } if ($this->length === 0) { return new static($text); } if ($ignoreCase) { $prefix = self::getMappedCodePoints($text, self::FOLD_CASE); } else { $prefix = &$text; } if ($this->length === $len) { $part = $this->getMappedCodes($ignoreCase ? self::FOLD_CASE : self::KEEP_CASE); if ($allowPrefixOnly && $part === $prefix) { return clone $this; } // Remove last element to avoid double check array_pop($part); } elseif ($this->length < $len) { $part = $this->getMappedCodes($ignoreCase ? self::FOLD_CASE : self::KEEP_CASE); // Checks if this can be a suffix if ($allowPrefixOnly && (array_slice($prefix, 0, $this->length) === $part)) { $text = array_slice($text, $this->length); return new static(array_merge($this->codes, $text)); } } else { $part = array_slice($this->codes, 0, $len); if ($ignoreCase) { $part = self::getMappedCodePoints($part, self::FOLD_CASE); } if ($part === $prefix) { return clone $this; } // Remove last element to avoid double check array_pop($part); } $copy = $len; $part_len = count($part); while ($part_len) { if ($part === array_slice($prefix, -$part_len)) { $copy = $len - $part_len; break; } array_pop($part); $part_len--; } if ($copy === 0) { return clone $this; } if ($copy < $len) { $text = array_slice($text, 0, $copy); } return new static(array_merge($text, $this->codes)); } /** * @param string|self|int[]|string[] $text * @param bool $ignoreCase * @param bool $allowSuffixOnly If true the result can contain only the suffix * @return static */ public function ensureSuffix($text, bool $ignoreCase = false, bool $allowSuffixOnly = true): self { $text = self::resolveCodePoints($text); $len = count($text); if ($len === 0) { return clone $this; } if ($this->length === 0) { return new static($text); } if ($ignoreCase) { $suffix = self::getMappedCodePoints($text, self::FOLD_CASE); } else { $suffix = &$text; } if ($this->length === $len) { $part = $this->getMappedCodes($ignoreCase ? self::FOLD_CASE : self::KEEP_CASE); if ($allowSuffixOnly && $part === $suffix) { return clone $this; } // Remove first element to avoid double check array_shift($part); } elseif ($this->length < $len) { $part = $this->getMappedCodes($ignoreCase ? self::FOLD_CASE : self::KEEP_CASE); // Checks if this can be a prefix if ($allowSuffixOnly && (array_slice($suffix, -$this->length) === $part)) { $text = array_slice($text, 0, $len - $this->length); return new static(array_merge($text, $this->codes)); } } else { $part = array_slice($this->codes, -$len); if ($ignoreCase) { $part = self::getMappedCodePoints($part, self::FOLD_CASE); } if ($part === $suffix) { return clone $this; } // Remove first element to avoid double check array_shift($part); } $skip = 0; $part_len = count($part); while ($part_len) { if ($part === array_slice($suffix, 0, $part_len)) { $skip = $part_len; break; } array_shift($part); $part_len--; } if ($skip === $len) { return clone $this; } if ($skip) { array_splice($text, 0, $skip); } return new static(array_merge($this->codes, $text)); } /** * @param string|self|int[]|string[] $text * @param int $mode * @return static */ public function append($text, int $mode = self::KEEP_CASE): self { return new static(array_merge($this->codes, self::resolveCodePoints($text, $mode))); } /** * @param string|self|int[]|string[] $text * @param int $mode * @return static */ public function prepend($text, int $mode = self::KEEP_CASE): self { return new static(array_merge(self::resolveCodePoints($text, $mode), $this->codes)); } /** * @param string|self|int[]|string[] $text * @param int $offset * @param int $mode * @return static */ public function insert($text, int $offset, int $mode = self::KEEP_CASE): self { $codes = $this->codes; array_splice($codes, $offset, 0, self::resolveCodePoints($text, $mode)); return new static($codes); } /** * @param int $offset * @param int|null $length * @return static */ public function remove(int $offset, ?int $length = null): self { $codes = $this->codes; if ($length === null) { array_splice($codes, $offset); } else { array_splice($codes, $offset, $length); } return new static($codes); } /** * @param string|self|int[]|string[] $mask * @return static */ public function trim($mask = " \t\n\r\0\x0B"): self { return $this->doTrim($mask, true, true); } /** * @param string|self|int[]|string[] $mask * @return static */ public function trimLeft($mask = " \t\n\r\0\x0B"): self { return $this->doTrim($mask, true, false); } /** * @param string|self|int[]|string[] $mask * @return static */ public function trimRight($mask = " \t\n\r\0\x0B"): self { return $this->doTrim($mask, false, true); } /** * @return static */ public function reverse(): self { return new static(array_reverse($this->codes)); } /** * @param int $times * @return static */ public function repeat(int $times = 1): self { if ($times <= 1) { return clone $this; } $codes = []; while ($times--) { $codes = array_merge($codes, $this->codes); } return new static($codes); } /** * @param string|self|int[]|string[] $subject * @param string|self|int[]|string[] $replace * @param int $offset * @param bool $ignoreCase * @return static */ public function replace($subject, $replace, int $offset = 0, bool $ignoreCase = false): self { if ($offset < 0) { $offset += $this->length; } if ($offset < 0 || $offset >= $this->length) { return clone $this; } $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; $subject = self::resolveCodePoints($subject, $mode); $len = count($subject); if ($len === 0 || $offset + $len > $this->length) { return clone $this; } $offset = $this->doIndexOf($this->getMappedCodes($mode), $subject, $offset); if ($offset === -1) { return clone $this; } $codes = $this->codes; array_splice($codes, $offset, count($subject), self::resolveCodePoints($replace)); return new static($codes); } /** * @param string|self|int[]|string[] $subject * @param string|self|int[]|string[] $replace * @param bool $ignoreCase * @param int $offset * @return static */ public function replaceAll($subject, $replace, int $offset = 0, bool $ignoreCase = false): self { if ($offset < 0) { $offset += $this->length; } if ($offset < 0 || $offset >= $this->length) { return clone $this; } $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; $subject = self::resolveCodePoints($subject, $mode); $len = count($subject); if ($len === 0 || $offset + $len > $this->length) { return clone $this; } $replace = self::resolveCodePoints($replace); $codes = $this->getMappedCodes($mode); $copy = $this->codes; $fix = count($replace) - $len; $t = 0; while (($pos = $this->doIndexOf($codes, $subject, $offset)) >= 0) { array_splice($copy, $pos + $t * $fix, $len, $replace); $offset = $pos + $len; $t++; } return new static($copy); } /** * @param string|self|int[]|string[] $delimiter * @param bool $ignoreCase * @return array */ public function split($delimiter = '', bool $ignoreCase = false): array { $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; $delimiter = self::resolveCodePoints($delimiter, $mode); $len = count($delimiter); $ret = []; if ($len === 0) { foreach ($this->codes as $code) { $ret[] = new static([$code]); } } else { $codes = $this->getMappedCodes($mode); $offset = 0; while (($pos = $this->doIndexOf($codes, $delimiter, $offset)) >= 0) { $ret[] = new static(array_slice($this->codes, $offset, $pos - $offset)); $offset = $pos + $len; } $ret[] = new static(array_slice($this->codes, $offset)); } return $ret; } /** * @param int $start * @param int|null $length * @return static */ public function substring(int $start, ?int $length = null): self { return new static(array_slice($this->codes, $start, $length)); } /** * @param int $size If negative then pad left otherwise pad right * @param self|string|int $char A char or a code point * @return static */ public function pad(int $size, $char = 0x20): self { return new static(array_pad($this->codes, $size, self::resolveFirstCodePoint($char, 0x20))); } /** * @param int $size * @param self|string|int $char * @return static */ public function padLeft(int $size, $char = 0x20): self { if ($size > 0) { $size = -$size; } return $this->pad($size, $char); } /** * @param int $size * @param self|string|int $char * @return static */ public function padRight(int $size, $char = 0x20): self { if ($size < 0) { $size = -$size; } return $this->pad($size, $char); } /** * @return bool */ public function isLowerCase(): bool { return $this->isCase(self::LOWER_CASE); } /** * @return bool */ public function isUpperCase(): bool { return $this->isCase(self::UPPER_CASE); } /** * @return bool */ public function isAscii(): bool { $key = 'i' . self::ASCII_CONV; if (!isset($this->cache[$key])) { $ok = true; foreach ($this->codes as $code) { if ($code >= 0x80) { $ok = false; break; } } $this->cache[$key] = $ok; } return $this->cache[$key]; } /** * Convert all chars to lower case (where possible) * @return static */ public function toLower(): self { if ($this->cache['i' . self::LOWER_CASE] ?? false) { return clone $this; } return new static($this->getMappedCodes(self::LOWER_CASE)); } /** * Convert all chars to upper case (where possible) * @return static */ public function toUpper(): self { if ($this->cache['i' . self::UPPER_CASE] ?? false) { return clone $this; } return new static($this->getMappedCodes(self::UPPER_CASE)); } /** * Converts all chars to their ASCII equivalent (if any) * @return static */ public function toAscii(): self { if ($this->cache['i' . self::ASCII_CONV] ?? false) { return clone $this; } return new static($this->getMappedCodes(self::ASCII_CONV)); } /** * @param int $index * @return string */ public function charAt(int $index): string { // Allow negative index if ($index < 0 && $index + $this->length >= 0) { $index += $this->length; } if ($index < 0 || $index >= $this->length) { return ''; } return $this->chars()[$index]; } /** * @param int $index * @return int */ public function codePointAt(int $index): int { // Allow negative index if ($index < 0 && $index + $this->length >= 0) { $index += $this->length; } if ($index < 0 || $index >= $this->length) { return -1; } return $this->codes[$index]; } /** * @param int $offset * @return int */ public function __invoke(int $offset): int { if ($offset < 0) { if ($offset + $this->length < 0) { throw new OutOfBoundsException("Undefined offset: {$offset}"); } $offset += $this->length; } elseif ($offset >= $this->length) { throw new OutOfBoundsException("Undefined offset: {$offset}"); } return $this->codes[$offset]; } /** * @inheritDoc */ public function offsetExists($offset): bool { // Allow negative index if ($offset < 0) { $offset += $this->length; } return isset($this->codes[$offset]); } /** * @inheritDoc */ public function offsetGet($offset): string { if ($offset < 0) { if ($offset + $this->length < 0) { throw new OutOfBoundsException("Undefined offset: {$offset}"); } $offset += $this->length; } elseif ($offset >= $this->length) { throw new OutOfBoundsException("Undefined offset: {$offset}"); } return $this->chars()[$offset]; } /** * @inheritDoc */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { // Allow negative index if ($offset < 0) { $offset += $this->length; } if (!isset($this->codes[$offset])) { return; } $value = self::resolveFirstCodePoint($value); if ($value === -1) { return; } if ($value === $this->codes[$offset]) { // Same value, nothing to do return; } $this->codes[$offset] = $value; // Clear cache $this->str = null; $this->cache = null; if ($this->chars) { $this->chars[$offset] = self::getCharFromCodePoint($value); } } /** * @inheritDoc */ #[\ReturnTypeWillChange] public function offsetUnset($offset) { throw new RuntimeException("Invalid operation"); } /** * @inheritDoc */ public function count(): int { return $this->length; } /** * @return string */ public function __toString(): string { if ($this->str === null) { $this->str = self::getStringFromCodePoints($this->codes); } return $this->str; } /** * @inheritDoc */ public function jsonSerialize(): string { return $this->__toString(); } public function __serialize(): array { return [ 'value' => $this->__toString(), ]; } public function __unserialize(array $data): void { $this->str = $data['value']; $this->codes = self::getCodePointsFromString($this->str); $this->length = count($this->codes); } /** * Creates an unicode string instance from raw string * @param string $string * @param string|null $encoding Defaults to UTF-8 * @param int $mode * @return static * @throws InvalidStringException */ public static function from(string $string, ?string $encoding = null, int $mode = self::KEEP_CASE): self { if ($encoding !== null && strcasecmp($encoding, 'UTF-8') !== 0) { if (false === $string = @iconv($encoding, 'UTF-8', $string)) { throw new UnicodeException("Could not convert string from '$encoding' encoding to UTF-8 encoding"); } } $instance = new static(self::getCodePointsFromString($string, $mode)); if ($mode === self::KEEP_CASE) { $instance->str = $string; } return $instance; } /** * Creates an unicode string instance from code points * @param int[] $codes * @param int $mode * @return static * @throws InvalidCodePointException */ public static function fromCodePoints(array $codes, int $mode = self::KEEP_CASE): self { $map = self::getMapByMode($mode); foreach ($codes as &$code) { if (!is_int($codes) || !self::isValidCodePoint($code)) { throw new InvalidCodePointException($code); } else { $code = $map[$code] ?? $code; } } return new static(array_values($codes)); } /** * Converts the code point to corresponding char * @param int $code * @return string The char or an empty string if code point is invalid */ public static function getCharFromCodePoint(int $code): string { if ($code < 0) { return ''; } if ($code < 0x80) { return chr($code); } if ($code < 0x800) { return chr(($code >> 6) + 0xC0) . chr(($code & 0x3F) + 0x80); } if ($code >= 0xD800 && $code <= 0xDFFF) { /* The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF, which are reserved for use with the UTF-16 encoding form (as surrogate pairs) and do not directly represent characters. */ return ''; } if ($code <= 0xFFFF) { return chr(($code >> 12) + 0xE0) . chr((($code >> 6) & 0x3F) + 0x80) . chr(($code & 0x3F) + 0x80); } if ($code <= 0x10FFFF) { return chr(($code >> 18) + 0xF0) . chr((($code >> 12) & 0x3F) + 0x80) . chr((($code >> 6) & 0x3F) + 0x80) . chr(($code & 0x3F) + 0x80); } /* Restricted the range of characters to 0000-10FFFF (the UTF-16 accessible range). */ return ''; } /** * Convert a string to a code point array * @param string $str * @param int $mode * @return array * @throws InvalidStringException */ public static function getCodePointsFromString(string $str, int $mode = self::KEEP_CASE): array { // 0x00-0x7F // 0xC2-0xDF 0x80-0xBF // 0xE0-0xE0 0xA0-0xBF 0x80-0xBF // 0xE1-0xEC 0x80-0xBF 0x80-0xBF // 0xED-0xED 0x80-0x9F 0x80-0xBF // 0xEE-0xEF 0x80-0xBF 0x80-0xBF // 0xF0-0xF0 0x90-0xBF 0x80-0xBF 0x80-0xBF // 0xF1-0xF3 0x80-0xBF 0x80-0xBF 0x80-0xBF // 0xF4-0xF4 0x80-0x8F 0x80-0xBF 0x80-0xBF $codes = []; $length = strlen($str); $mode = self::getMapByMode($mode); $i = 0; while ($i < $length) { $ord0 = ord($str[$i++]); if ($ord0 < 0x80) { $codes[] = $mode[$ord0] ?? $ord0; continue; } if ($i === $length || $ord0 < 0xC2 || $ord0 > 0xF4) { throw new InvalidStringException($str, $i - 1); } $ord1 = ord($str[$i++]); if ($ord0 < 0xE0) { if ($ord1 < 0x80 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 1); } $ord1 = ($ord0 - 0xC0) * 64 + $ord1 - 0x80; $codes[] = $mode[$ord1] ?? $ord1; continue; } if ($i === $length) { throw new InvalidStringException($str, $i - 1); } $ord2 = ord($str[$i++]); if ($ord0 < 0xF0) { if ($ord0 === 0xE0) { if ($ord1 < 0xA0 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 2); } } elseif ($ord0 === 0xED) { if ($ord1 < 0x80 || $ord1 >= 0xA0) { throw new InvalidStringException($str, $i - 2); } } elseif ($ord1 < 0x80 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 2); } if ($ord2 < 0x80 || $ord2 >= 0xC0) { throw new InvalidStringException($str, $i - 1); } $ord2 = ($ord0 - 0xE0) * 0x1000 + ($ord1 - 0x80) * 64 + $ord2 - 0x80; $codes[] = $mode[$ord2] ?? $ord2; continue; } if ($i === $length) { throw new InvalidStringException($str, $i - 1); } $ord3 = ord($str[$i++]); if ($ord0 < 0xF5) { if ($ord0 === 0xF0) { if ($ord1 < 0x90 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 3); } } elseif ($ord0 === 0xF4) { if ($ord1 < 0x80 || $ord1 >= 0x90) { throw new InvalidStringException($str, $i - 3); } } elseif ($ord1 < 0x80 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 3); } if ($ord2 < 0x80 || $ord2 >= 0xC0) { throw new InvalidStringException($str, $i - 2); } if ($ord3 < 0x80 || $ord3 >= 0xC0) { throw new InvalidStringException($str, $i - 1); } $ord3 = ($ord0 - 0xF0) * 0x40000 + ($ord1 - 0x80) * 0x1000 + ($ord2 - 0x80) * 64 + $ord3 - 0x80; $codes[] = $mode[$ord3] ?? $ord3; continue; } throw new InvalidStringException($str, $i - 1); } return $codes; } /** * @param string $str * @return iterable * * The key represents the current char index * Value is a two element array * - first element is an integer representing the code point * - second element is an array of integers (length 1 to 4) representing bytes */ public static function walkString(string $str): iterable { $i = 0; $length = strlen($str); while ($i < $length) { $index = $i; $ord0 = ord($str[$i++]); if ($ord0 < 0x80) { yield $index => [ $ord0, [$ord0] ]; continue; } if ($i === $length || $ord0 < 0xC2 || $ord0 > 0xF4) { throw new InvalidStringException($str, $i - 1); } $ord1 = ord($str[$i++]); if ($ord0 < 0xE0) { if ($ord1 < 0x80 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 1); } yield $index => [ ($ord0 - 0xC0) * 64 + $ord1 - 0x80, [$ord0, $ord1] ]; continue; } if ($i === $length) { throw new InvalidStringException($str, $i - 1); } $ord2 = ord($str[$i++]); if ($ord0 < 0xF0) { if ($ord0 === 0xE0) { if ($ord1 < 0xA0 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 2); } } elseif ($ord0 === 0xED) { if ($ord1 < 0x80 || $ord1 >= 0xA0) { throw new InvalidStringException($str, $i - 2); } } elseif ($ord1 < 0x80 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 2); } if ($ord2 < 0x80 || $ord2 >= 0xC0) { throw new InvalidStringException($str, $i - 1); } yield $index => [ ($ord0 - 0xE0) * 0x1000 + ($ord1 - 0x80) * 64 + $ord2 - 0x80, [$ord0, $ord1, $ord2] ]; continue; } if ($i === $length) { throw new InvalidStringException($str, $i - 1); } $ord3 = ord($str[$i++]); if ($ord0 < 0xF5) { if ($ord0 === 0xF0) { if ($ord1 < 0x90 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 3); } } elseif ($ord0 === 0xF4) { if ($ord1 < 0x80 || $ord1 >= 0x90) { throw new InvalidStringException($str, $i - 3); } } elseif ($ord1 < 0x80 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 3); } if ($ord2 < 0x80 || $ord2 >= 0xC0) { throw new InvalidStringException($str, $i - 2); } if ($ord3 < 0x80 || $ord3 >= 0xC0) { throw new InvalidStringException($str, $i - 1); } yield $index => [ ($ord0 - 0xF0) * 0x40000 + ($ord1 - 0x80) * 0x1000 + ($ord2 - 0x80) * 64 + $ord3 - 0x80, [$ord0, $ord1, $ord2, $ord3] ]; continue; } throw new InvalidStringException($str, $i - 1); } } /** * Compute string length * @param string $str * @return int * @throws InvalidStringException */ public static function getStringLength(string $str): int { $count = 0; $length = strlen($str); $i = 0; while ($i < $length) { $ord0 = ord($str[$i++]); if ($ord0 < 0x80) { $count++; continue; } if ($i === $length || $ord0 < 0xC2 || $ord0 > 0xF4) { throw new InvalidStringException($str, $i - 1); } $ord1 = ord($str[$i++]); if ($ord0 < 0xE0) { if ($ord1 < 0x80 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 1); } // $ord1 = ($ord0 - 0xC0) * 64 + $ord1 - 0x80; $count++; continue; } if ($i === $length) { throw new InvalidStringException($str, $i - 1); } $ord2 = ord($str[$i++]); if ($ord0 < 0xF0) { if ($ord0 === 0xE0) { if ($ord1 < 0xA0 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 2); } } elseif ($ord0 === 0xED) { if ($ord1 < 0x80 || $ord1 >= 0xA0) { throw new InvalidStringException($str, $i - 2); } } elseif ($ord1 < 0x80 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 2); } if ($ord2 < 0x80 || $ord2 >= 0xC0) { throw new InvalidStringException($str, $i - 1); } // $ord2 = ($ord0 - 0xE0) * 0x1000 + ($ord1 - 0x80) * 64 + $ord2 - 0x80; $count++; continue; } if ($i === $length) { throw new InvalidStringException($str, $i - 1); } $ord3 = ord($str[$i++]); if ($ord0 < 0xF5) { if ($ord0 === 0xF0) { if ($ord1 < 0x90 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 3); } } elseif ($ord0 === 0xF4) { if ($ord1 < 0x80 || $ord1 >= 0x90) { throw new InvalidStringException($str, $i - 3); } } elseif ($ord1 < 0x80 || $ord1 >= 0xC0) { throw new InvalidStringException($str, $i - 3); } if ($ord2 < 0x80 || $ord2 >= 0xC0) { throw new InvalidStringException($str, $i - 2); } if ($ord3 < 0x80 || $ord3 >= 0xC0) { throw new InvalidStringException($str, $i - 1); } // $ord3 = ($ord0 - 0xF0) * 0x40000 + ($ord1 - 0x80) * 0x1000 + ($ord2 - 0x80) * 64 + $ord3 - 0x80; $count++; continue; } throw new InvalidStringException($str, $i - 1); } return $count; } /** * Converts each code point to a char * @param array $codes * @param int $mode * @return array * @throws InvalidCodePointException */ public static function getCharsFromCodePoints(array $codes, int $mode = self::KEEP_CASE): array { $mode = self::getMapByMode($mode); foreach ($codes as &$code) { $char = self::getCharFromCodePoint($mode[$code] ?? $code); if ($char === '') { throw new InvalidCodePointException($code); } else { $code = $char; } } return $codes; } /** * @param string $str * @param int $mode * @return string[] */ public static function getCharsFromString(string $str, int $mode = self::KEEP_CASE): array { return self::getCharsFromCodePoints(self::getCodePointsFromString($str), $mode); } /** * Converts all code points to chars and returns the string * Invalid code points are ignored * @param array $codes * @param int $mode * @return string */ public static function getStringFromCodePoints(array $codes, int $mode = self::KEEP_CASE): string { $str = ''; $mode = self::getMapByMode($mode); foreach ($codes as $code) { if (isset($mode[$code])) { $code = $mode[$code]; } if ($code < 0x80) { $str .= chr($code); continue; } if ($code < 0x800) { $str .= chr(($code >> 6) + 0xC0) . chr(($code & 0x3F) + 0x80); continue; } if ($code >= 0xD800 && $code <= 0xDFFF) { continue; } if ($code <= 0xFFFF) { $str .= chr(($code >> 12) + 0xE0) . chr((($code >> 6) & 0x3F) + 0x80) . chr(($code & 0x3F) + 0x80); continue; } if ($code <= 0x10FFFF) { $str .= chr(($code >> 18) + 0xF0) . chr((($code >> 12) & 0x3F) + 0x80) . chr((($code >> 6) & 0x3F) + 0x80) . chr(($code & 0x3F) + 0x80); } } return $str; } /** * @param array $codes * @param int $mode * @return array */ public static function getMappedCodePoints(array $codes, int $mode): array { if ($mode === self::KEEP_CASE) { return $codes; } $mode = self::getMapByMode($mode); if (empty($mode)) { return $codes; } foreach ($codes as &$code) { $code = $mode[$code] ?? $code; } return $codes; } /** * Checks if a code point is valid * @param int $code * @return bool */ public static function isValidCodePoint(int $code): bool { if ($code < 0 || $code > 0x10FFFF) { return false; } return $code < 0xD800 || $code > 0xDFFF; } /** * @param int $mode * @return int[] */ private function getMappedCodes(int $mode): array { if ($mode === self::KEEP_CASE || ($this->cache['i' . $mode] ?? false)) { return $this->codes; } $key = 'm' . $mode; if (!isset($this->cache[$key])) { $this->cache[$key] = self::getMappedCodePoints($this->codes, $mode); } return $this->cache[$key]; } /** * @param int $mode * @return bool */ private function isCase(int $mode): bool { $key = 'i' . $mode; if (!isset($this->cache[$key])) { $list = self::getMapByMode($mode); foreach ($this->codes as $code) { if (isset($list[$code])) { return $this->cache[$key] = false; } } return $this->cache[$key] = true; } return $this->cache[$key]; } /** * @param int[] $codes * @param int[] $text * @param int $offset * @return int */ private function doIndexOf(array $codes, array $text, int $offset = 0): int { $len = count($text); for ($i = $offset, $last = count($codes) - $len; $i <= $last; $i++) { $match = true; for ($j = 0; $j < $len; $j++) { if ($codes[$i + $j] !== $text[$j]) { $match = false; break; } } if ($match) { return $i; } } return -1; } /** * @param string|self|int[]|string[] $mask * @param bool $left * @param bool $right * @return static */ private function doTrim($mask, bool $left, bool $right): self { if ($this->length === 0) { return clone $this; } $mask = self::resolveCodePoints($mask); if (empty($mask)) { return clone $this; } $codes = $this->codes; if ($left) { while (in_array($codes[0], $mask, true)) { array_shift($codes); if (empty($codes)) { return new static(); } } } if ($right) { $last = count($codes) - 1; while (in_array($codes[$last], $mask, true)) { array_pop($codes); if (--$last < 0) { return new static(); } } } return new static($codes); } /** * @param string|self|int[]|string[] $text * @param int $mode * @return array */ private static function resolveCodePoints($text, int $mode = self::KEEP_CASE): array { if ($text instanceof self) { return $text->getMappedCodes($mode); } if (is_string($text)) { return self::getCodePointsFromString($text, $mode); } if ($text && is_array($text) && is_int($text[0])) { // assume code point array return self::getMappedCodePoints($text, $mode); } return []; } /** * @param self|string|int|string[]|int[] $text * @param int $invalid * @return int */ private static function resolveFirstCodePoint($text, int $invalid = -1): int { if ($text instanceof self) { return $text->length === 0 ? $invalid : $text->codes[0]; } if (is_array($text)) { if (empty($text)) { return $invalid; } $text = reset($text); } if (is_string($text)) { if (isset($text[4])) { $text = substr($text, 0, 4); } return self::getCodePointsFromString($text)[0] ?? $invalid; } if (is_int($text)) { return self::isValidCodePoint($text) ? $text : $invalid; } return $invalid; } /** * @param int $mode * @return int[] */ private static function getMapByMode(int $mode): array { if (isset(self::$maps[$mode])) { return self::$maps[$mode]; } switch ($mode) { case self::LOWER_CASE: $file = 'lower'; break; case self::UPPER_CASE: $file = 'upper'; break; case self::ASCII_CONV: $file = 'ascii'; break; case self::FOLD_CASE: $file = 'fold'; break; default: return []; } /** @noinspection PhpIncludeInspection */ return self::$maps[$mode] = include(__DIR__ . "/../res/{$file}.php"); } } string/res/upper.php 0000777 00000060255 15252016106 0010524 0 ustar 00 <?php return [ 0x61 => 0x41, 0x62 => 0x42, 0x63 => 0x43, 0x64 => 0x44, 0x65 => 0x45, 0x66 => 0x46, 0x67 => 0x47, 0x68 => 0x48, 0x69 => 0x49, 0x6A => 0x4A, 0x6B => 0x4B, 0x6C => 0x4C, 0x6D => 0x4D, 0x6E => 0x4E, 0x6F => 0x4F, 0x70 => 0x50, 0x71 => 0x51, 0x72 => 0x52, 0x73 => 0x53, 0x74 => 0x54, 0x75 => 0x55, 0x76 => 0x56, 0x77 => 0x57, 0x78 => 0x58, 0x79 => 0x59, 0x7A => 0x5A, 0xB5 => 0x39C, 0xE0 => 0xC0, 0xE1 => 0xC1, 0xE2 => 0xC2, 0xE3 => 0xC3, 0xE4 => 0xC4, 0xE5 => 0xC5, 0xE6 => 0xC6, 0xE7 => 0xC7, 0xE8 => 0xC8, 0xE9 => 0xC9, 0xEA => 0xCA, 0xEB => 0xCB, 0xEC => 0xCC, 0xED => 0xCD, 0xEE => 0xCE, 0xEF => 0xCF, 0xF0 => 0xD0, 0xF1 => 0xD1, 0xF2 => 0xD2, 0xF3 => 0xD3, 0xF4 => 0xD4, 0xF5 => 0xD5, 0xF6 => 0xD6, 0xF8 => 0xD8, 0xF9 => 0xD9, 0xFA => 0xDA, 0xFB => 0xDB, 0xFC => 0xDC, 0xFD => 0xDD, 0xFE => 0xDE, 0xFF => 0x178, 0x101 => 0x100, 0x103 => 0x102, 0x105 => 0x104, 0x107 => 0x106, 0x109 => 0x108, 0x10B => 0x10A, 0x10D => 0x10C, 0x10F => 0x10E, 0x111 => 0x110, 0x113 => 0x112, 0x115 => 0x114, 0x117 => 0x116, 0x119 => 0x118, 0x11B => 0x11A, 0x11D => 0x11C, 0x11F => 0x11E, 0x121 => 0x120, 0x123 => 0x122, 0x125 => 0x124, 0x127 => 0x126, 0x129 => 0x128, 0x12B => 0x12A, 0x12D => 0x12C, 0x12F => 0x12E, 0x131 => 0x49, 0x133 => 0x132, 0x135 => 0x134, 0x137 => 0x136, 0x13A => 0x139, 0x13C => 0x13B, 0x13E => 0x13D, 0x140 => 0x13F, 0x142 => 0x141, 0x144 => 0x143, 0x146 => 0x145, 0x148 => 0x147, 0x14B => 0x14A, 0x14D => 0x14C, 0x14F => 0x14E, 0x151 => 0x150, 0x153 => 0x152, 0x155 => 0x154, 0x157 => 0x156, 0x159 => 0x158, 0x15B => 0x15A, 0x15D => 0x15C, 0x15F => 0x15E, 0x161 => 0x160, 0x163 => 0x162, 0x165 => 0x164, 0x167 => 0x166, 0x169 => 0x168, 0x16B => 0x16A, 0x16D => 0x16C, 0x16F => 0x16E, 0x171 => 0x170, 0x173 => 0x172, 0x175 => 0x174, 0x177 => 0x176, 0x17A => 0x179, 0x17C => 0x17B, 0x17E => 0x17D, 0x17F => 0x53, 0x180 => 0x243, 0x183 => 0x182, 0x185 => 0x184, 0x188 => 0x187, 0x18C => 0x18B, 0x192 => 0x191, 0x195 => 0x1F6, 0x199 => 0x198, 0x19A => 0x23D, 0x19E => 0x220, 0x1A1 => 0x1A0, 0x1A3 => 0x1A2, 0x1A5 => 0x1A4, 0x1A8 => 0x1A7, 0x1AD => 0x1AC, 0x1B0 => 0x1AF, 0x1B4 => 0x1B3, 0x1B6 => 0x1B5, 0x1B9 => 0x1B8, 0x1BD => 0x1BC, 0x1BF => 0x1F7, 0x1C5 => 0x1C4, 0x1C6 => 0x1C4, 0x1C8 => 0x1C7, 0x1C9 => 0x1C7, 0x1CB => 0x1CA, 0x1CC => 0x1CA, 0x1CE => 0x1CD, 0x1D0 => 0x1CF, 0x1D2 => 0x1D1, 0x1D4 => 0x1D3, 0x1D6 => 0x1D5, 0x1D8 => 0x1D7, 0x1DA => 0x1D9, 0x1DC => 0x1DB, 0x1DD => 0x18E, 0x1DF => 0x1DE, 0x1E1 => 0x1E0, 0x1E3 => 0x1E2, 0x1E5 => 0x1E4, 0x1E7 => 0x1E6, 0x1E9 => 0x1E8, 0x1EB => 0x1EA, 0x1ED => 0x1EC, 0x1EF => 0x1EE, 0x1F2 => 0x1F1, 0x1F3 => 0x1F1, 0x1F5 => 0x1F4, 0x1F9 => 0x1F8, 0x1FB => 0x1FA, 0x1FD => 0x1FC, 0x1FF => 0x1FE, 0x201 => 0x200, 0x203 => 0x202, 0x205 => 0x204, 0x207 => 0x206, 0x209 => 0x208, 0x20B => 0x20A, 0x20D => 0x20C, 0x20F => 0x20E, 0x211 => 0x210, 0x213 => 0x212, 0x215 => 0x214, 0x217 => 0x216, 0x219 => 0x218, 0x21B => 0x21A, 0x21D => 0x21C, 0x21F => 0x21E, 0x223 => 0x222, 0x225 => 0x224, 0x227 => 0x226, 0x229 => 0x228, 0x22B => 0x22A, 0x22D => 0x22C, 0x22F => 0x22E, 0x231 => 0x230, 0x233 => 0x232, 0x23C => 0x23B, 0x23F => 0x2C7E, 0x240 => 0x2C7F, 0x242 => 0x241, 0x247 => 0x246, 0x249 => 0x248, 0x24B => 0x24A, 0x24D => 0x24C, 0x24F => 0x24E, 0x250 => 0x2C6F, 0x251 => 0x2C6D, 0x252 => 0x2C70, 0x253 => 0x181, 0x254 => 0x186, 0x256 => 0x189, 0x257 => 0x18A, 0x259 => 0x18F, 0x25B => 0x190, 0x25C => 0xA7AB, 0x260 => 0x193, 0x261 => 0xA7AC, 0x263 => 0x194, 0x265 => 0xA78D, 0x266 => 0xA7AA, 0x268 => 0x197, 0x269 => 0x196, 0x26A => 0xA7AE, 0x26B => 0x2C62, 0x26C => 0xA7AD, 0x26F => 0x19C, 0x271 => 0x2C6E, 0x272 => 0x19D, 0x275 => 0x19F, 0x27D => 0x2C64, 0x280 => 0x1A6, 0x282 => 0xA7C5, 0x283 => 0x1A9, 0x287 => 0xA7B1, 0x288 => 0x1AE, 0x289 => 0x244, 0x28A => 0x1B1, 0x28B => 0x1B2, 0x28C => 0x245, 0x292 => 0x1B7, 0x29D => 0xA7B2, 0x29E => 0xA7B0, 0x345 => 0x399, 0x371 => 0x370, 0x373 => 0x372, 0x377 => 0x376, 0x37B => 0x3FD, 0x37C => 0x3FE, 0x37D => 0x3FF, 0x3AC => 0x386, 0x3AD => 0x388, 0x3AE => 0x389, 0x3AF => 0x38A, 0x3B1 => 0x391, 0x3B2 => 0x392, 0x3B3 => 0x393, 0x3B4 => 0x394, 0x3B5 => 0x395, 0x3B6 => 0x396, 0x3B7 => 0x397, 0x3B8 => 0x398, 0x3B9 => 0x399, 0x3BA => 0x39A, 0x3BB => 0x39B, 0x3BC => 0x39C, 0x3BD => 0x39D, 0x3BE => 0x39E, 0x3BF => 0x39F, 0x3C0 => 0x3A0, 0x3C1 => 0x3A1, 0x3C2 => 0x3A3, 0x3C3 => 0x3A3, 0x3C4 => 0x3A4, 0x3C5 => 0x3A5, 0x3C6 => 0x3A6, 0x3C7 => 0x3A7, 0x3C8 => 0x3A8, 0x3C9 => 0x3A9, 0x3CA => 0x3AA, 0x3CB => 0x3AB, 0x3CC => 0x38C, 0x3CD => 0x38E, 0x3CE => 0x38F, 0x3D0 => 0x392, 0x3D1 => 0x398, 0x3D5 => 0x3A6, 0x3D6 => 0x3A0, 0x3D7 => 0x3CF, 0x3D9 => 0x3D8, 0x3DB => 0x3DA, 0x3DD => 0x3DC, 0x3DF => 0x3DE, 0x3E1 => 0x3E0, 0x3E3 => 0x3E2, 0x3E5 => 0x3E4, 0x3E7 => 0x3E6, 0x3E9 => 0x3E8, 0x3EB => 0x3EA, 0x3ED => 0x3EC, 0x3EF => 0x3EE, 0x3F0 => 0x39A, 0x3F1 => 0x3A1, 0x3F2 => 0x3F9, 0x3F3 => 0x37F, 0x3F5 => 0x395, 0x3F8 => 0x3F7, 0x3FB => 0x3FA, 0x430 => 0x410, 0x431 => 0x411, 0x432 => 0x412, 0x433 => 0x413, 0x434 => 0x414, 0x435 => 0x415, 0x436 => 0x416, 0x437 => 0x417, 0x438 => 0x418, 0x439 => 0x419, 0x43A => 0x41A, 0x43B => 0x41B, 0x43C => 0x41C, 0x43D => 0x41D, 0x43E => 0x41E, 0x43F => 0x41F, 0x440 => 0x420, 0x441 => 0x421, 0x442 => 0x422, 0x443 => 0x423, 0x444 => 0x424, 0x445 => 0x425, 0x446 => 0x426, 0x447 => 0x427, 0x448 => 0x428, 0x449 => 0x429, 0x44A => 0x42A, 0x44B => 0x42B, 0x44C => 0x42C, 0x44D => 0x42D, 0x44E => 0x42E, 0x44F => 0x42F, 0x450 => 0x400, 0x451 => 0x401, 0x452 => 0x402, 0x453 => 0x403, 0x454 => 0x404, 0x455 => 0x405, 0x456 => 0x406, 0x457 => 0x407, 0x458 => 0x408, 0x459 => 0x409, 0x45A => 0x40A, 0x45B => 0x40B, 0x45C => 0x40C, 0x45D => 0x40D, 0x45E => 0x40E, 0x45F => 0x40F, 0x461 => 0x460, 0x463 => 0x462, 0x465 => 0x464, 0x467 => 0x466, 0x469 => 0x468, 0x46B => 0x46A, 0x46D => 0x46C, 0x46F => 0x46E, 0x471 => 0x470, 0x473 => 0x472, 0x475 => 0x474, 0x477 => 0x476, 0x479 => 0x478, 0x47B => 0x47A, 0x47D => 0x47C, 0x47F => 0x47E, 0x481 => 0x480, 0x48B => 0x48A, 0x48D => 0x48C, 0x48F => 0x48E, 0x491 => 0x490, 0x493 => 0x492, 0x495 => 0x494, 0x497 => 0x496, 0x499 => 0x498, 0x49B => 0x49A, 0x49D => 0x49C, 0x49F => 0x49E, 0x4A1 => 0x4A0, 0x4A3 => 0x4A2, 0x4A5 => 0x4A4, 0x4A7 => 0x4A6, 0x4A9 => 0x4A8, 0x4AB => 0x4AA, 0x4AD => 0x4AC, 0x4AF => 0x4AE, 0x4B1 => 0x4B0, 0x4B3 => 0x4B2, 0x4B5 => 0x4B4, 0x4B7 => 0x4B6, 0x4B9 => 0x4B8, 0x4BB => 0x4BA, 0x4BD => 0x4BC, 0x4BF => 0x4BE, 0x4C2 => 0x4C1, 0x4C4 => 0x4C3, 0x4C6 => 0x4C5, 0x4C8 => 0x4C7, 0x4CA => 0x4C9, 0x4CC => 0x4CB, 0x4CE => 0x4CD, 0x4CF => 0x4C0, 0x4D1 => 0x4D0, 0x4D3 => 0x4D2, 0x4D5 => 0x4D4, 0x4D7 => 0x4D6, 0x4D9 => 0x4D8, 0x4DB => 0x4DA, 0x4DD => 0x4DC, 0x4DF => 0x4DE, 0x4E1 => 0x4E0, 0x4E3 => 0x4E2, 0x4E5 => 0x4E4, 0x4E7 => 0x4E6, 0x4E9 => 0x4E8, 0x4EB => 0x4EA, 0x4ED => 0x4EC, 0x4EF => 0x4EE, 0x4F1 => 0x4F0, 0x4F3 => 0x4F2, 0x4F5 => 0x4F4, 0x4F7 => 0x4F6, 0x4F9 => 0x4F8, 0x4FB => 0x4FA, 0x4FD => 0x4FC, 0x4FF => 0x4FE, 0x501 => 0x500, 0x503 => 0x502, 0x505 => 0x504, 0x507 => 0x506, 0x509 => 0x508, 0x50B => 0x50A, 0x50D => 0x50C, 0x50F => 0x50E, 0x511 => 0x510, 0x513 => 0x512, 0x515 => 0x514, 0x517 => 0x516, 0x519 => 0x518, 0x51B => 0x51A, 0x51D => 0x51C, 0x51F => 0x51E, 0x521 => 0x520, 0x523 => 0x522, 0x525 => 0x524, 0x527 => 0x526, 0x529 => 0x528, 0x52B => 0x52A, 0x52D => 0x52C, 0x52F => 0x52E, 0x561 => 0x531, 0x562 => 0x532, 0x563 => 0x533, 0x564 => 0x534, 0x565 => 0x535, 0x566 => 0x536, 0x567 => 0x537, 0x568 => 0x538, 0x569 => 0x539, 0x56A => 0x53A, 0x56B => 0x53B, 0x56C => 0x53C, 0x56D => 0x53D, 0x56E => 0x53E, 0x56F => 0x53F, 0x570 => 0x540, 0x571 => 0x541, 0x572 => 0x542, 0x573 => 0x543, 0x574 => 0x544, 0x575 => 0x545, 0x576 => 0x546, 0x577 => 0x547, 0x578 => 0x548, 0x579 => 0x549, 0x57A => 0x54A, 0x57B => 0x54B, 0x57C => 0x54C, 0x57D => 0x54D, 0x57E => 0x54E, 0x57F => 0x54F, 0x580 => 0x550, 0x581 => 0x551, 0x582 => 0x552, 0x583 => 0x553, 0x584 => 0x554, 0x585 => 0x555, 0x586 => 0x556, 0x10D0 => 0x1C90, 0x10D1 => 0x1C91, 0x10D2 => 0x1C92, 0x10D3 => 0x1C93, 0x10D4 => 0x1C94, 0x10D5 => 0x1C95, 0x10D6 => 0x1C96, 0x10D7 => 0x1C97, 0x10D8 => 0x1C98, 0x10D9 => 0x1C99, 0x10DA => 0x1C9A, 0x10DB => 0x1C9B, 0x10DC => 0x1C9C, 0x10DD => 0x1C9D, 0x10DE => 0x1C9E, 0x10DF => 0x1C9F, 0x10E0 => 0x1CA0, 0x10E1 => 0x1CA1, 0x10E2 => 0x1CA2, 0x10E3 => 0x1CA3, 0x10E4 => 0x1CA4, 0x10E5 => 0x1CA5, 0x10E6 => 0x1CA6, 0x10E7 => 0x1CA7, 0x10E8 => 0x1CA8, 0x10E9 => 0x1CA9, 0x10EA => 0x1CAA, 0x10EB => 0x1CAB, 0x10EC => 0x1CAC, 0x10ED => 0x1CAD, 0x10EE => 0x1CAE, 0x10EF => 0x1CAF, 0x10F0 => 0x1CB0, 0x10F1 => 0x1CB1, 0x10F2 => 0x1CB2, 0x10F3 => 0x1CB3, 0x10F4 => 0x1CB4, 0x10F5 => 0x1CB5, 0x10F6 => 0x1CB6, 0x10F7 => 0x1CB7, 0x10F8 => 0x1CB8, 0x10F9 => 0x1CB9, 0x10FA => 0x1CBA, 0x10FD => 0x1CBD, 0x10FE => 0x1CBE, 0x10FF => 0x1CBF, 0x13F8 => 0x13F0, 0x13F9 => 0x13F1, 0x13FA => 0x13F2, 0x13FB => 0x13F3, 0x13FC => 0x13F4, 0x13FD => 0x13F5, 0x1C80 => 0x412, 0x1C81 => 0x414, 0x1C82 => 0x41E, 0x1C83 => 0x421, 0x1C84 => 0x422, 0x1C85 => 0x422, 0x1C86 => 0x42A, 0x1C87 => 0x462, 0x1C88 => 0xA64A, 0x1D79 => 0xA77D, 0x1D7D => 0x2C63, 0x1D8E => 0xA7C6, 0x1E01 => 0x1E00, 0x1E03 => 0x1E02, 0x1E05 => 0x1E04, 0x1E07 => 0x1E06, 0x1E09 => 0x1E08, 0x1E0B => 0x1E0A, 0x1E0D => 0x1E0C, 0x1E0F => 0x1E0E, 0x1E11 => 0x1E10, 0x1E13 => 0x1E12, 0x1E15 => 0x1E14, 0x1E17 => 0x1E16, 0x1E19 => 0x1E18, 0x1E1B => 0x1E1A, 0x1E1D => 0x1E1C, 0x1E1F => 0x1E1E, 0x1E21 => 0x1E20, 0x1E23 => 0x1E22, 0x1E25 => 0x1E24, 0x1E27 => 0x1E26, 0x1E29 => 0x1E28, 0x1E2B => 0x1E2A, 0x1E2D => 0x1E2C, 0x1E2F => 0x1E2E, 0x1E31 => 0x1E30, 0x1E33 => 0x1E32, 0x1E35 => 0x1E34, 0x1E37 => 0x1E36, 0x1E39 => 0x1E38, 0x1E3B => 0x1E3A, 0x1E3D => 0x1E3C, 0x1E3F => 0x1E3E, 0x1E41 => 0x1E40, 0x1E43 => 0x1E42, 0x1E45 => 0x1E44, 0x1E47 => 0x1E46, 0x1E49 => 0x1E48, 0x1E4B => 0x1E4A, 0x1E4D => 0x1E4C, 0x1E4F => 0x1E4E, 0x1E51 => 0x1E50, 0x1E53 => 0x1E52, 0x1E55 => 0x1E54, 0x1E57 => 0x1E56, 0x1E59 => 0x1E58, 0x1E5B => 0x1E5A, 0x1E5D => 0x1E5C, 0x1E5F => 0x1E5E, 0x1E61 => 0x1E60, 0x1E63 => 0x1E62, 0x1E65 => 0x1E64, 0x1E67 => 0x1E66, 0x1E69 => 0x1E68, 0x1E6B => 0x1E6A, 0x1E6D => 0x1E6C, 0x1E6F => 0x1E6E, 0x1E71 => 0x1E70, 0x1E73 => 0x1E72, 0x1E75 => 0x1E74, 0x1E77 => 0x1E76, 0x1E79 => 0x1E78, 0x1E7B => 0x1E7A, 0x1E7D => 0x1E7C, 0x1E7F => 0x1E7E, 0x1E81 => 0x1E80, 0x1E83 => 0x1E82, 0x1E85 => 0x1E84, 0x1E87 => 0x1E86, 0x1E89 => 0x1E88, 0x1E8B => 0x1E8A, 0x1E8D => 0x1E8C, 0x1E8F => 0x1E8E, 0x1E91 => 0x1E90, 0x1E93 => 0x1E92, 0x1E95 => 0x1E94, 0x1E9B => 0x1E60, 0x1EA1 => 0x1EA0, 0x1EA3 => 0x1EA2, 0x1EA5 => 0x1EA4, 0x1EA7 => 0x1EA6, 0x1EA9 => 0x1EA8, 0x1EAB => 0x1EAA, 0x1EAD => 0x1EAC, 0x1EAF => 0x1EAE, 0x1EB1 => 0x1EB0, 0x1EB3 => 0x1EB2, 0x1EB5 => 0x1EB4, 0x1EB7 => 0x1EB6, 0x1EB9 => 0x1EB8, 0x1EBB => 0x1EBA, 0x1EBD => 0x1EBC, 0x1EBF => 0x1EBE, 0x1EC1 => 0x1EC0, 0x1EC3 => 0x1EC2, 0x1EC5 => 0x1EC4, 0x1EC7 => 0x1EC6, 0x1EC9 => 0x1EC8, 0x1ECB => 0x1ECA, 0x1ECD => 0x1ECC, 0x1ECF => 0x1ECE, 0x1ED1 => 0x1ED0, 0x1ED3 => 0x1ED2, 0x1ED5 => 0x1ED4, 0x1ED7 => 0x1ED6, 0x1ED9 => 0x1ED8, 0x1EDB => 0x1EDA, 0x1EDD => 0x1EDC, 0x1EDF => 0x1EDE, 0x1EE1 => 0x1EE0, 0x1EE3 => 0x1EE2, 0x1EE5 => 0x1EE4, 0x1EE7 => 0x1EE6, 0x1EE9 => 0x1EE8, 0x1EEB => 0x1EEA, 0x1EED => 0x1EEC, 0x1EEF => 0x1EEE, 0x1EF1 => 0x1EF0, 0x1EF3 => 0x1EF2, 0x1EF5 => 0x1EF4, 0x1EF7 => 0x1EF6, 0x1EF9 => 0x1EF8, 0x1EFB => 0x1EFA, 0x1EFD => 0x1EFC, 0x1EFF => 0x1EFE, 0x1F00 => 0x1F08, 0x1F01 => 0x1F09, 0x1F02 => 0x1F0A, 0x1F03 => 0x1F0B, 0x1F04 => 0x1F0C, 0x1F05 => 0x1F0D, 0x1F06 => 0x1F0E, 0x1F07 => 0x1F0F, 0x1F10 => 0x1F18, 0x1F11 => 0x1F19, 0x1F12 => 0x1F1A, 0x1F13 => 0x1F1B, 0x1F14 => 0x1F1C, 0x1F15 => 0x1F1D, 0x1F20 => 0x1F28, 0x1F21 => 0x1F29, 0x1F22 => 0x1F2A, 0x1F23 => 0x1F2B, 0x1F24 => 0x1F2C, 0x1F25 => 0x1F2D, 0x1F26 => 0x1F2E, 0x1F27 => 0x1F2F, 0x1F30 => 0x1F38, 0x1F31 => 0x1F39, 0x1F32 => 0x1F3A, 0x1F33 => 0x1F3B, 0x1F34 => 0x1F3C, 0x1F35 => 0x1F3D, 0x1F36 => 0x1F3E, 0x1F37 => 0x1F3F, 0x1F40 => 0x1F48, 0x1F41 => 0x1F49, 0x1F42 => 0x1F4A, 0x1F43 => 0x1F4B, 0x1F44 => 0x1F4C, 0x1F45 => 0x1F4D, 0x1F51 => 0x1F59, 0x1F53 => 0x1F5B, 0x1F55 => 0x1F5D, 0x1F57 => 0x1F5F, 0x1F60 => 0x1F68, 0x1F61 => 0x1F69, 0x1F62 => 0x1F6A, 0x1F63 => 0x1F6B, 0x1F64 => 0x1F6C, 0x1F65 => 0x1F6D, 0x1F66 => 0x1F6E, 0x1F67 => 0x1F6F, 0x1F70 => 0x1FBA, 0x1F71 => 0x1FBB, 0x1F72 => 0x1FC8, 0x1F73 => 0x1FC9, 0x1F74 => 0x1FCA, 0x1F75 => 0x1FCB, 0x1F76 => 0x1FDA, 0x1F77 => 0x1FDB, 0x1F78 => 0x1FF8, 0x1F79 => 0x1FF9, 0x1F7A => 0x1FEA, 0x1F7B => 0x1FEB, 0x1F7C => 0x1FFA, 0x1F7D => 0x1FFB, 0x1F80 => 0x1F88, 0x1F81 => 0x1F89, 0x1F82 => 0x1F8A, 0x1F83 => 0x1F8B, 0x1F84 => 0x1F8C, 0x1F85 => 0x1F8D, 0x1F86 => 0x1F8E, 0x1F87 => 0x1F8F, 0x1F90 => 0x1F98, 0x1F91 => 0x1F99, 0x1F92 => 0x1F9A, 0x1F93 => 0x1F9B, 0x1F94 => 0x1F9C, 0x1F95 => 0x1F9D, 0x1F96 => 0x1F9E, 0x1F97 => 0x1F9F, 0x1FA0 => 0x1FA8, 0x1FA1 => 0x1FA9, 0x1FA2 => 0x1FAA, 0x1FA3 => 0x1FAB, 0x1FA4 => 0x1FAC, 0x1FA5 => 0x1FAD, 0x1FA6 => 0x1FAE, 0x1FA7 => 0x1FAF, 0x1FB0 => 0x1FB8, 0x1FB1 => 0x1FB9, 0x1FB3 => 0x1FBC, 0x1FBE => 0x399, 0x1FC3 => 0x1FCC, 0x1FD0 => 0x1FD8, 0x1FD1 => 0x1FD9, 0x1FE0 => 0x1FE8, 0x1FE1 => 0x1FE9, 0x1FE5 => 0x1FEC, 0x1FF3 => 0x1FFC, 0x214E => 0x2132, 0x2170 => 0x2160, 0x2171 => 0x2161, 0x2172 => 0x2162, 0x2173 => 0x2163, 0x2174 => 0x2164, 0x2175 => 0x2165, 0x2176 => 0x2166, 0x2177 => 0x2167, 0x2178 => 0x2168, 0x2179 => 0x2169, 0x217A => 0x216A, 0x217B => 0x216B, 0x217C => 0x216C, 0x217D => 0x216D, 0x217E => 0x216E, 0x217F => 0x216F, 0x2184 => 0x2183, 0x24D0 => 0x24B6, 0x24D1 => 0x24B7, 0x24D2 => 0x24B8, 0x24D3 => 0x24B9, 0x24D4 => 0x24BA, 0x24D5 => 0x24BB, 0x24D6 => 0x24BC, 0x24D7 => 0x24BD, 0x24D8 => 0x24BE, 0x24D9 => 0x24BF, 0x24DA => 0x24C0, 0x24DB => 0x24C1, 0x24DC => 0x24C2, 0x24DD => 0x24C3, 0x24DE => 0x24C4, 0x24DF => 0x24C5, 0x24E0 => 0x24C6, 0x24E1 => 0x24C7, 0x24E2 => 0x24C8, 0x24E3 => 0x24C9, 0x24E4 => 0x24CA, 0x24E5 => 0x24CB, 0x24E6 => 0x24CC, 0x24E7 => 0x24CD, 0x24E8 => 0x24CE, 0x24E9 => 0x24CF, 0x2C30 => 0x2C00, 0x2C31 => 0x2C01, 0x2C32 => 0x2C02, 0x2C33 => 0x2C03, 0x2C34 => 0x2C04, 0x2C35 => 0x2C05, 0x2C36 => 0x2C06, 0x2C37 => 0x2C07, 0x2C38 => 0x2C08, 0x2C39 => 0x2C09, 0x2C3A => 0x2C0A, 0x2C3B => 0x2C0B, 0x2C3C => 0x2C0C, 0x2C3D => 0x2C0D, 0x2C3E => 0x2C0E, 0x2C3F => 0x2C0F, 0x2C40 => 0x2C10, 0x2C41 => 0x2C11, 0x2C42 => 0x2C12, 0x2C43 => 0x2C13, 0x2C44 => 0x2C14, 0x2C45 => 0x2C15, 0x2C46 => 0x2C16, 0x2C47 => 0x2C17, 0x2C48 => 0x2C18, 0x2C49 => 0x2C19, 0x2C4A => 0x2C1A, 0x2C4B => 0x2C1B, 0x2C4C => 0x2C1C, 0x2C4D => 0x2C1D, 0x2C4E => 0x2C1E, 0x2C4F => 0x2C1F, 0x2C50 => 0x2C20, 0x2C51 => 0x2C21, 0x2C52 => 0x2C22, 0x2C53 => 0x2C23, 0x2C54 => 0x2C24, 0x2C55 => 0x2C25, 0x2C56 => 0x2C26, 0x2C57 => 0x2C27, 0x2C58 => 0x2C28, 0x2C59 => 0x2C29, 0x2C5A => 0x2C2A, 0x2C5B => 0x2C2B, 0x2C5C => 0x2C2C, 0x2C5D => 0x2C2D, 0x2C5E => 0x2C2E, 0x2C61 => 0x2C60, 0x2C65 => 0x23A, 0x2C66 => 0x23E, 0x2C68 => 0x2C67, 0x2C6A => 0x2C69, 0x2C6C => 0x2C6B, 0x2C73 => 0x2C72, 0x2C76 => 0x2C75, 0x2C81 => 0x2C80, 0x2C83 => 0x2C82, 0x2C85 => 0x2C84, 0x2C87 => 0x2C86, 0x2C89 => 0x2C88, 0x2C8B => 0x2C8A, 0x2C8D => 0x2C8C, 0x2C8F => 0x2C8E, 0x2C91 => 0x2C90, 0x2C93 => 0x2C92, 0x2C95 => 0x2C94, 0x2C97 => 0x2C96, 0x2C99 => 0x2C98, 0x2C9B => 0x2C9A, 0x2C9D => 0x2C9C, 0x2C9F => 0x2C9E, 0x2CA1 => 0x2CA0, 0x2CA3 => 0x2CA2, 0x2CA5 => 0x2CA4, 0x2CA7 => 0x2CA6, 0x2CA9 => 0x2CA8, 0x2CAB => 0x2CAA, 0x2CAD => 0x2CAC, 0x2CAF => 0x2CAE, 0x2CB1 => 0x2CB0, 0x2CB3 => 0x2CB2, 0x2CB5 => 0x2CB4, 0x2CB7 => 0x2CB6, 0x2CB9 => 0x2CB8, 0x2CBB => 0x2CBA, 0x2CBD => 0x2CBC, 0x2CBF => 0x2CBE, 0x2CC1 => 0x2CC0, 0x2CC3 => 0x2CC2, 0x2CC5 => 0x2CC4, 0x2CC7 => 0x2CC6, 0x2CC9 => 0x2CC8, 0x2CCB => 0x2CCA, 0x2CCD => 0x2CCC, 0x2CCF => 0x2CCE, 0x2CD1 => 0x2CD0, 0x2CD3 => 0x2CD2, 0x2CD5 => 0x2CD4, 0x2CD7 => 0x2CD6, 0x2CD9 => 0x2CD8, 0x2CDB => 0x2CDA, 0x2CDD => 0x2CDC, 0x2CDF => 0x2CDE, 0x2CE1 => 0x2CE0, 0x2CE3 => 0x2CE2, 0x2CEC => 0x2CEB, 0x2CEE => 0x2CED, 0x2CF3 => 0x2CF2, 0x2D00 => 0x10A0, 0x2D01 => 0x10A1, 0x2D02 => 0x10A2, 0x2D03 => 0x10A3, 0x2D04 => 0x10A4, 0x2D05 => 0x10A5, 0x2D06 => 0x10A6, 0x2D07 => 0x10A7, 0x2D08 => 0x10A8, 0x2D09 => 0x10A9, 0x2D0A => 0x10AA, 0x2D0B => 0x10AB, 0x2D0C => 0x10AC, 0x2D0D => 0x10AD, 0x2D0E => 0x10AE, 0x2D0F => 0x10AF, 0x2D10 => 0x10B0, 0x2D11 => 0x10B1, 0x2D12 => 0x10B2, 0x2D13 => 0x10B3, 0x2D14 => 0x10B4, 0x2D15 => 0x10B5, 0x2D16 => 0x10B6, 0x2D17 => 0x10B7, 0x2D18 => 0x10B8, 0x2D19 => 0x10B9, 0x2D1A => 0x10BA, 0x2D1B => 0x10BB, 0x2D1C => 0x10BC, 0x2D1D => 0x10BD, 0x2D1E => 0x10BE, 0x2D1F => 0x10BF, 0x2D20 => 0x10C0, 0x2D21 => 0x10C1, 0x2D22 => 0x10C2, 0x2D23 => 0x10C3, 0x2D24 => 0x10C4, 0x2D25 => 0x10C5, 0x2D27 => 0x10C7, 0x2D2D => 0x10CD, 0xA641 => 0xA640, 0xA643 => 0xA642, 0xA645 => 0xA644, 0xA647 => 0xA646, 0xA649 => 0xA648, 0xA64B => 0xA64A, 0xA64D => 0xA64C, 0xA64F => 0xA64E, 0xA651 => 0xA650, 0xA653 => 0xA652, 0xA655 => 0xA654, 0xA657 => 0xA656, 0xA659 => 0xA658, 0xA65B => 0xA65A, 0xA65D => 0xA65C, 0xA65F => 0xA65E, 0xA661 => 0xA660, 0xA663 => 0xA662, 0xA665 => 0xA664, 0xA667 => 0xA666, 0xA669 => 0xA668, 0xA66B => 0xA66A, 0xA66D => 0xA66C, 0xA681 => 0xA680, 0xA683 => 0xA682, 0xA685 => 0xA684, 0xA687 => 0xA686, 0xA689 => 0xA688, 0xA68B => 0xA68A, 0xA68D => 0xA68C, 0xA68F => 0xA68E, 0xA691 => 0xA690, 0xA693 => 0xA692, 0xA695 => 0xA694, 0xA697 => 0xA696, 0xA699 => 0xA698, 0xA69B => 0xA69A, 0xA723 => 0xA722, 0xA725 => 0xA724, 0xA727 => 0xA726, 0xA729 => 0xA728, 0xA72B => 0xA72A, 0xA72D => 0xA72C, 0xA72F => 0xA72E, 0xA733 => 0xA732, 0xA735 => 0xA734, 0xA737 => 0xA736, 0xA739 => 0xA738, 0xA73B => 0xA73A, 0xA73D => 0xA73C, 0xA73F => 0xA73E, 0xA741 => 0xA740, 0xA743 => 0xA742, 0xA745 => 0xA744, 0xA747 => 0xA746, 0xA749 => 0xA748, 0xA74B => 0xA74A, 0xA74D => 0xA74C, 0xA74F => 0xA74E, 0xA751 => 0xA750, 0xA753 => 0xA752, 0xA755 => 0xA754, 0xA757 => 0xA756, 0xA759 => 0xA758, 0xA75B => 0xA75A, 0xA75D => 0xA75C, 0xA75F => 0xA75E, 0xA761 => 0xA760, 0xA763 => 0xA762, 0xA765 => 0xA764, 0xA767 => 0xA766, 0xA769 => 0xA768, 0xA76B => 0xA76A, 0xA76D => 0xA76C, 0xA76F => 0xA76E, 0xA77A => 0xA779, 0xA77C => 0xA77B, 0xA77F => 0xA77E, 0xA781 => 0xA780, 0xA783 => 0xA782, 0xA785 => 0xA784, 0xA787 => 0xA786, 0xA78C => 0xA78B, 0xA791 => 0xA790, 0xA793 => 0xA792, 0xA794 => 0xA7C4, 0xA797 => 0xA796, 0xA799 => 0xA798, 0xA79B => 0xA79A, 0xA79D => 0xA79C, 0xA79F => 0xA79E, 0xA7A1 => 0xA7A0, 0xA7A3 => 0xA7A2, 0xA7A5 => 0xA7A4, 0xA7A7 => 0xA7A6, 0xA7A9 => 0xA7A8, 0xA7B5 => 0xA7B4, 0xA7B7 => 0xA7B6, 0xA7B9 => 0xA7B8, 0xA7BB => 0xA7BA, 0xA7BD => 0xA7BC, 0xA7BF => 0xA7BE, 0xA7C3 => 0xA7C2, 0xA7C8 => 0xA7C7, 0xA7CA => 0xA7C9, 0xA7F6 => 0xA7F5, 0xAB53 => 0xA7B3, 0xAB70 => 0x13A0, 0xAB71 => 0x13A1, 0xAB72 => 0x13A2, 0xAB73 => 0x13A3, 0xAB74 => 0x13A4, 0xAB75 => 0x13A5, 0xAB76 => 0x13A6, 0xAB77 => 0x13A7, 0xAB78 => 0x13A8, 0xAB79 => 0x13A9, 0xAB7A => 0x13AA, 0xAB7B => 0x13AB, 0xAB7C => 0x13AC, 0xAB7D => 0x13AD, 0xAB7E => 0x13AE, 0xAB7F => 0x13AF, 0xAB80 => 0x13B0, 0xAB81 => 0x13B1, 0xAB82 => 0x13B2, 0xAB83 => 0x13B3, 0xAB84 => 0x13B4, 0xAB85 => 0x13B5, 0xAB86 => 0x13B6, 0xAB87 => 0x13B7, 0xAB88 => 0x13B8, 0xAB89 => 0x13B9, 0xAB8A => 0x13BA, 0xAB8B => 0x13BB, 0xAB8C => 0x13BC, 0xAB8D => 0x13BD, 0xAB8E => 0x13BE, 0xAB8F => 0x13BF, 0xAB90 => 0x13C0, 0xAB91 => 0x13C1, 0xAB92 => 0x13C2, 0xAB93 => 0x13C3, 0xAB94 => 0x13C4, 0xAB95 => 0x13C5, 0xAB96 => 0x13C6, 0xAB97 => 0x13C7, 0xAB98 => 0x13C8, 0xAB99 => 0x13C9, 0xAB9A => 0x13CA, 0xAB9B => 0x13CB, 0xAB9C => 0x13CC, 0xAB9D => 0x13CD, 0xAB9E => 0x13CE, 0xAB9F => 0x13CF, 0xABA0 => 0x13D0, 0xABA1 => 0x13D1, 0xABA2 => 0x13D2, 0xABA3 => 0x13D3, 0xABA4 => 0x13D4, 0xABA5 => 0x13D5, 0xABA6 => 0x13D6, 0xABA7 => 0x13D7, 0xABA8 => 0x13D8, 0xABA9 => 0x13D9, 0xABAA => 0x13DA, 0xABAB => 0x13DB, 0xABAC => 0x13DC, 0xABAD => 0x13DD, 0xABAE => 0x13DE, 0xABAF => 0x13DF, 0xABB0 => 0x13E0, 0xABB1 => 0x13E1, 0xABB2 => 0x13E2, 0xABB3 => 0x13E3, 0xABB4 => 0x13E4, 0xABB5 => 0x13E5, 0xABB6 => 0x13E6, 0xABB7 => 0x13E7, 0xABB8 => 0x13E8, 0xABB9 => 0x13E9, 0xABBA => 0x13EA, 0xABBB => 0x13EB, 0xABBC => 0x13EC, 0xABBD => 0x13ED, 0xABBE => 0x13EE, 0xABBF => 0x13EF, 0xFF41 => 0xFF21, 0xFF42 => 0xFF22, 0xFF43 => 0xFF23, 0xFF44 => 0xFF24, 0xFF45 => 0xFF25, 0xFF46 => 0xFF26, 0xFF47 => 0xFF27, 0xFF48 => 0xFF28, 0xFF49 => 0xFF29, 0xFF4A => 0xFF2A, 0xFF4B => 0xFF2B, 0xFF4C => 0xFF2C, 0xFF4D => 0xFF2D, 0xFF4E => 0xFF2E, 0xFF4F => 0xFF2F, 0xFF50 => 0xFF30, 0xFF51 => 0xFF31, 0xFF52 => 0xFF32, 0xFF53 => 0xFF33, 0xFF54 => 0xFF34, 0xFF55 => 0xFF35, 0xFF56 => 0xFF36, 0xFF57 => 0xFF37, 0xFF58 => 0xFF38, 0xFF59 => 0xFF39, 0xFF5A => 0xFF3A, 0x10428 => 0x10400, 0x10429 => 0x10401, 0x1042A => 0x10402, 0x1042B => 0x10403, 0x1042C => 0x10404, 0x1042D => 0x10405, 0x1042E => 0x10406, 0x1042F => 0x10407, 0x10430 => 0x10408, 0x10431 => 0x10409, 0x10432 => 0x1040A, 0x10433 => 0x1040B, 0x10434 => 0x1040C, 0x10435 => 0x1040D, 0x10436 => 0x1040E, 0x10437 => 0x1040F, 0x10438 => 0x10410, 0x10439 => 0x10411, 0x1043A => 0x10412, 0x1043B => 0x10413, 0x1043C => 0x10414, 0x1043D => 0x10415, 0x1043E => 0x10416, 0x1043F => 0x10417, 0x10440 => 0x10418, 0x10441 => 0x10419, 0x10442 => 0x1041A, 0x10443 => 0x1041B, 0x10444 => 0x1041C, 0x10445 => 0x1041D, 0x10446 => 0x1041E, 0x10447 => 0x1041F, 0x10448 => 0x10420, 0x10449 => 0x10421, 0x1044A => 0x10422, 0x1044B => 0x10423, 0x1044C => 0x10424, 0x1044D => 0x10425, 0x1044E => 0x10426, 0x1044F => 0x10427, 0x104D8 => 0x104B0, 0x104D9 => 0x104B1, 0x104DA => 0x104B2, 0x104DB => 0x104B3, 0x104DC => 0x104B4, 0x104DD => 0x104B5, 0x104DE => 0x104B6, 0x104DF => 0x104B7, 0x104E0 => 0x104B8, 0x104E1 => 0x104B9, 0x104E2 => 0x104BA, 0x104E3 => 0x104BB, 0x104E4 => 0x104BC, 0x104E5 => 0x104BD, 0x104E6 => 0x104BE, 0x104E7 => 0x104BF, 0x104E8 => 0x104C0, 0x104E9 => 0x104C1, 0x104EA => 0x104C2, 0x104EB => 0x104C3, 0x104EC => 0x104C4, 0x104ED => 0x104C5, 0x104EE => 0x104C6, 0x104EF => 0x104C7, 0x104F0 => 0x104C8, 0x104F1 => 0x104C9, 0x104F2 => 0x104CA, 0x104F3 => 0x104CB, 0x104F4 => 0x104CC, 0x104F5 => 0x104CD, 0x104F6 => 0x104CE, 0x104F7 => 0x104CF, 0x104F8 => 0x104D0, 0x104F9 => 0x104D1, 0x104FA => 0x104D2, 0x104FB => 0x104D3, 0x10CC0 => 0x10C80, 0x10CC1 => 0x10C81, 0x10CC2 => 0x10C82, 0x10CC3 => 0x10C83, 0x10CC4 => 0x10C84, 0x10CC5 => 0x10C85, 0x10CC6 => 0x10C86, 0x10CC7 => 0x10C87, 0x10CC8 => 0x10C88, 0x10CC9 => 0x10C89, 0x10CCA => 0x10C8A, 0x10CCB => 0x10C8B, 0x10CCC => 0x10C8C, 0x10CCD => 0x10C8D, 0x10CCE => 0x10C8E, 0x10CCF => 0x10C8F, 0x10CD0 => 0x10C90, 0x10CD1 => 0x10C91, 0x10CD2 => 0x10C92, 0x10CD3 => 0x10C93, 0x10CD4 => 0x10C94, 0x10CD5 => 0x10C95, 0x10CD6 => 0x10C96, 0x10CD7 => 0x10C97, 0x10CD8 => 0x10C98, 0x10CD9 => 0x10C99, 0x10CDA => 0x10C9A, 0x10CDB => 0x10C9B, 0x10CDC => 0x10C9C, 0x10CDD => 0x10C9D, 0x10CDE => 0x10C9E, 0x10CDF => 0x10C9F, 0x10CE0 => 0x10CA0, 0x10CE1 => 0x10CA1, 0x10CE2 => 0x10CA2, 0x10CE3 => 0x10CA3, 0x10CE4 => 0x10CA4, 0x10CE5 => 0x10CA5, 0x10CE6 => 0x10CA6, 0x10CE7 => 0x10CA7, 0x10CE8 => 0x10CA8, 0x10CE9 => 0x10CA9, 0x10CEA => 0x10CAA, 0x10CEB => 0x10CAB, 0x10CEC => 0x10CAC, 0x10CED => 0x10CAD, 0x10CEE => 0x10CAE, 0x10CEF => 0x10CAF, 0x10CF0 => 0x10CB0, 0x10CF1 => 0x10CB1, 0x10CF2 => 0x10CB2, 0x118C0 => 0x118A0, 0x118C1 => 0x118A1, 0x118C2 => 0x118A2, 0x118C3 => 0x118A3, 0x118C4 => 0x118A4, 0x118C5 => 0x118A5, 0x118C6 => 0x118A6, 0x118C7 => 0x118A7, 0x118C8 => 0x118A8, 0x118C9 => 0x118A9, 0x118CA => 0x118AA, 0x118CB => 0x118AB, 0x118CC => 0x118AC, 0x118CD => 0x118AD, 0x118CE => 0x118AE, 0x118CF => 0x118AF, 0x118D0 => 0x118B0, 0x118D1 => 0x118B1, 0x118D2 => 0x118B2, 0x118D3 => 0x118B3, 0x118D4 => 0x118B4, 0x118D5 => 0x118B5, 0x118D6 => 0x118B6, 0x118D7 => 0x118B7, 0x118D8 => 0x118B8, 0x118D9 => 0x118B9, 0x118DA => 0x118BA, 0x118DB => 0x118BB, 0x118DC => 0x118BC, 0x118DD => 0x118BD, 0x118DE => 0x118BE, 0x118DF => 0x118BF, 0x16E60 => 0x16E40, 0x16E61 => 0x16E41, 0x16E62 => 0x16E42, 0x16E63 => 0x16E43, 0x16E64 => 0x16E44, 0x16E65 => 0x16E45, 0x16E66 => 0x16E46, 0x16E67 => 0x16E47, 0x16E68 => 0x16E48, 0x16E69 => 0x16E49, 0x16E6A => 0x16E4A, 0x16E6B => 0x16E4B, 0x16E6C => 0x16E4C, 0x16E6D => 0x16E4D, 0x16E6E => 0x16E4E, 0x16E6F => 0x16E4F, 0x16E70 => 0x16E50, 0x16E71 => 0x16E51, 0x16E72 => 0x16E52, 0x16E73 => 0x16E53, 0x16E74 => 0x16E54, 0x16E75 => 0x16E55, 0x16E76 => 0x16E56, 0x16E77 => 0x16E57, 0x16E78 => 0x16E58, 0x16E79 => 0x16E59, 0x16E7A => 0x16E5A, 0x16E7B => 0x16E5B, 0x16E7C => 0x16E5C, 0x16E7D => 0x16E5D, 0x16E7E => 0x16E5E, 0x16E7F => 0x16E5F, 0x1E922 => 0x1E900, 0x1E923 => 0x1E901, 0x1E924 => 0x1E902, 0x1E925 => 0x1E903, 0x1E926 => 0x1E904, 0x1E927 => 0x1E905, 0x1E928 => 0x1E906, 0x1E929 => 0x1E907, 0x1E92A => 0x1E908, 0x1E92B => 0x1E909, 0x1E92C => 0x1E90A, 0x1E92D => 0x1E90B, 0x1E92E => 0x1E90C, 0x1E92F => 0x1E90D, 0x1E930 => 0x1E90E, 0x1E931 => 0x1E90F, 0x1E932 => 0x1E910, 0x1E933 => 0x1E911, 0x1E934 => 0x1E912, 0x1E935 => 0x1E913, 0x1E936 => 0x1E914, 0x1E937 => 0x1E915, 0x1E938 => 0x1E916, 0x1E939 => 0x1E917, 0x1E93A => 0x1E918, 0x1E93B => 0x1E919, 0x1E93C => 0x1E91A, 0x1E93D => 0x1E91B, 0x1E93E => 0x1E91C, 0x1E93F => 0x1E91D, 0x1E940 => 0x1E91E, 0x1E941 => 0x1E91F, 0x1E942 => 0x1E920, 0x1E943 => 0x1E921, ]; string/res/lower.php 0000777 00000057623 15252016106 0010526 0 ustar 00 <?php return [ 0x41 => 0x61, 0x42 => 0x62, 0x43 => 0x63, 0x44 => 0x64, 0x45 => 0x65, 0x46 => 0x66, 0x47 => 0x67, 0x48 => 0x68, 0x49 => 0x69, 0x4A => 0x6A, 0x4B => 0x6B, 0x4C => 0x6C, 0x4D => 0x6D, 0x4E => 0x6E, 0x4F => 0x6F, 0x50 => 0x70, 0x51 => 0x71, 0x52 => 0x72, 0x53 => 0x73, 0x54 => 0x74, 0x55 => 0x75, 0x56 => 0x76, 0x57 => 0x77, 0x58 => 0x78, 0x59 => 0x79, 0x5A => 0x7A, 0xC0 => 0xE0, 0xC1 => 0xE1, 0xC2 => 0xE2, 0xC3 => 0xE3, 0xC4 => 0xE4, 0xC5 => 0xE5, 0xC6 => 0xE6, 0xC7 => 0xE7, 0xC8 => 0xE8, 0xC9 => 0xE9, 0xCA => 0xEA, 0xCB => 0xEB, 0xCC => 0xEC, 0xCD => 0xED, 0xCE => 0xEE, 0xCF => 0xEF, 0xD0 => 0xF0, 0xD1 => 0xF1, 0xD2 => 0xF2, 0xD3 => 0xF3, 0xD4 => 0xF4, 0xD5 => 0xF5, 0xD6 => 0xF6, 0xD8 => 0xF8, 0xD9 => 0xF9, 0xDA => 0xFA, 0xDB => 0xFB, 0xDC => 0xFC, 0xDD => 0xFD, 0xDE => 0xFE, 0x100 => 0x101, 0x102 => 0x103, 0x104 => 0x105, 0x106 => 0x107, 0x108 => 0x109, 0x10A => 0x10B, 0x10C => 0x10D, 0x10E => 0x10F, 0x110 => 0x111, 0x112 => 0x113, 0x114 => 0x115, 0x116 => 0x117, 0x118 => 0x119, 0x11A => 0x11B, 0x11C => 0x11D, 0x11E => 0x11F, 0x120 => 0x121, 0x122 => 0x123, 0x124 => 0x125, 0x126 => 0x127, 0x128 => 0x129, 0x12A => 0x12B, 0x12C => 0x12D, 0x12E => 0x12F, 0x130 => 0x69, 0x132 => 0x133, 0x134 => 0x135, 0x136 => 0x137, 0x139 => 0x13A, 0x13B => 0x13C, 0x13D => 0x13E, 0x13F => 0x140, 0x141 => 0x142, 0x143 => 0x144, 0x145 => 0x146, 0x147 => 0x148, 0x14A => 0x14B, 0x14C => 0x14D, 0x14E => 0x14F, 0x150 => 0x151, 0x152 => 0x153, 0x154 => 0x155, 0x156 => 0x157, 0x158 => 0x159, 0x15A => 0x15B, 0x15C => 0x15D, 0x15E => 0x15F, 0x160 => 0x161, 0x162 => 0x163, 0x164 => 0x165, 0x166 => 0x167, 0x168 => 0x169, 0x16A => 0x16B, 0x16C => 0x16D, 0x16E => 0x16F, 0x170 => 0x171, 0x172 => 0x173, 0x174 => 0x175, 0x176 => 0x177, 0x178 => 0xFF, 0x179 => 0x17A, 0x17B => 0x17C, 0x17D => 0x17E, 0x181 => 0x253, 0x182 => 0x183, 0x184 => 0x185, 0x186 => 0x254, 0x187 => 0x188, 0x189 => 0x256, 0x18A => 0x257, 0x18B => 0x18C, 0x18E => 0x1DD, 0x18F => 0x259, 0x190 => 0x25B, 0x191 => 0x192, 0x193 => 0x260, 0x194 => 0x263, 0x196 => 0x269, 0x197 => 0x268, 0x198 => 0x199, 0x19C => 0x26F, 0x19D => 0x272, 0x19F => 0x275, 0x1A0 => 0x1A1, 0x1A2 => 0x1A3, 0x1A4 => 0x1A5, 0x1A6 => 0x280, 0x1A7 => 0x1A8, 0x1A9 => 0x283, 0x1AC => 0x1AD, 0x1AE => 0x288, 0x1AF => 0x1B0, 0x1B1 => 0x28A, 0x1B2 => 0x28B, 0x1B3 => 0x1B4, 0x1B5 => 0x1B6, 0x1B7 => 0x292, 0x1B8 => 0x1B9, 0x1BC => 0x1BD, 0x1C4 => 0x1C6, 0x1C5 => 0x1C6, 0x1C7 => 0x1C9, 0x1C8 => 0x1C9, 0x1CA => 0x1CC, 0x1CB => 0x1CC, 0x1CD => 0x1CE, 0x1CF => 0x1D0, 0x1D1 => 0x1D2, 0x1D3 => 0x1D4, 0x1D5 => 0x1D6, 0x1D7 => 0x1D8, 0x1D9 => 0x1DA, 0x1DB => 0x1DC, 0x1DE => 0x1DF, 0x1E0 => 0x1E1, 0x1E2 => 0x1E3, 0x1E4 => 0x1E5, 0x1E6 => 0x1E7, 0x1E8 => 0x1E9, 0x1EA => 0x1EB, 0x1EC => 0x1ED, 0x1EE => 0x1EF, 0x1F1 => 0x1F3, 0x1F2 => 0x1F3, 0x1F4 => 0x1F5, 0x1F6 => 0x195, 0x1F7 => 0x1BF, 0x1F8 => 0x1F9, 0x1FA => 0x1FB, 0x1FC => 0x1FD, 0x1FE => 0x1FF, 0x200 => 0x201, 0x202 => 0x203, 0x204 => 0x205, 0x206 => 0x207, 0x208 => 0x209, 0x20A => 0x20B, 0x20C => 0x20D, 0x20E => 0x20F, 0x210 => 0x211, 0x212 => 0x213, 0x214 => 0x215, 0x216 => 0x217, 0x218 => 0x219, 0x21A => 0x21B, 0x21C => 0x21D, 0x21E => 0x21F, 0x220 => 0x19E, 0x222 => 0x223, 0x224 => 0x225, 0x226 => 0x227, 0x228 => 0x229, 0x22A => 0x22B, 0x22C => 0x22D, 0x22E => 0x22F, 0x230 => 0x231, 0x232 => 0x233, 0x23A => 0x2C65, 0x23B => 0x23C, 0x23D => 0x19A, 0x23E => 0x2C66, 0x241 => 0x242, 0x243 => 0x180, 0x244 => 0x289, 0x245 => 0x28C, 0x246 => 0x247, 0x248 => 0x249, 0x24A => 0x24B, 0x24C => 0x24D, 0x24E => 0x24F, 0x370 => 0x371, 0x372 => 0x373, 0x376 => 0x377, 0x37F => 0x3F3, 0x386 => 0x3AC, 0x388 => 0x3AD, 0x389 => 0x3AE, 0x38A => 0x3AF, 0x38C => 0x3CC, 0x38E => 0x3CD, 0x38F => 0x3CE, 0x391 => 0x3B1, 0x392 => 0x3B2, 0x393 => 0x3B3, 0x394 => 0x3B4, 0x395 => 0x3B5, 0x396 => 0x3B6, 0x397 => 0x3B7, 0x398 => 0x3B8, 0x399 => 0x3B9, 0x39A => 0x3BA, 0x39B => 0x3BB, 0x39C => 0x3BC, 0x39D => 0x3BD, 0x39E => 0x3BE, 0x39F => 0x3BF, 0x3A0 => 0x3C0, 0x3A1 => 0x3C1, 0x3A3 => 0x3C3, 0x3A4 => 0x3C4, 0x3A5 => 0x3C5, 0x3A6 => 0x3C6, 0x3A7 => 0x3C7, 0x3A8 => 0x3C8, 0x3A9 => 0x3C9, 0x3AA => 0x3CA, 0x3AB => 0x3CB, 0x3CF => 0x3D7, 0x3D8 => 0x3D9, 0x3DA => 0x3DB, 0x3DC => 0x3DD, 0x3DE => 0x3DF, 0x3E0 => 0x3E1, 0x3E2 => 0x3E3, 0x3E4 => 0x3E5, 0x3E6 => 0x3E7, 0x3E8 => 0x3E9, 0x3EA => 0x3EB, 0x3EC => 0x3ED, 0x3EE => 0x3EF, 0x3F4 => 0x3B8, 0x3F7 => 0x3F8, 0x3F9 => 0x3F2, 0x3FA => 0x3FB, 0x3FD => 0x37B, 0x3FE => 0x37C, 0x3FF => 0x37D, 0x400 => 0x450, 0x401 => 0x451, 0x402 => 0x452, 0x403 => 0x453, 0x404 => 0x454, 0x405 => 0x455, 0x406 => 0x456, 0x407 => 0x457, 0x408 => 0x458, 0x409 => 0x459, 0x40A => 0x45A, 0x40B => 0x45B, 0x40C => 0x45C, 0x40D => 0x45D, 0x40E => 0x45E, 0x40F => 0x45F, 0x410 => 0x430, 0x411 => 0x431, 0x412 => 0x432, 0x413 => 0x433, 0x414 => 0x434, 0x415 => 0x435, 0x416 => 0x436, 0x417 => 0x437, 0x418 => 0x438, 0x419 => 0x439, 0x41A => 0x43A, 0x41B => 0x43B, 0x41C => 0x43C, 0x41D => 0x43D, 0x41E => 0x43E, 0x41F => 0x43F, 0x420 => 0x440, 0x421 => 0x441, 0x422 => 0x442, 0x423 => 0x443, 0x424 => 0x444, 0x425 => 0x445, 0x426 => 0x446, 0x427 => 0x447, 0x428 => 0x448, 0x429 => 0x449, 0x42A => 0x44A, 0x42B => 0x44B, 0x42C => 0x44C, 0x42D => 0x44D, 0x42E => 0x44E, 0x42F => 0x44F, 0x460 => 0x461, 0x462 => 0x463, 0x464 => 0x465, 0x466 => 0x467, 0x468 => 0x469, 0x46A => 0x46B, 0x46C => 0x46D, 0x46E => 0x46F, 0x470 => 0x471, 0x472 => 0x473, 0x474 => 0x475, 0x476 => 0x477, 0x478 => 0x479, 0x47A => 0x47B, 0x47C => 0x47D, 0x47E => 0x47F, 0x480 => 0x481, 0x48A => 0x48B, 0x48C => 0x48D, 0x48E => 0x48F, 0x490 => 0x491, 0x492 => 0x493, 0x494 => 0x495, 0x496 => 0x497, 0x498 => 0x499, 0x49A => 0x49B, 0x49C => 0x49D, 0x49E => 0x49F, 0x4A0 => 0x4A1, 0x4A2 => 0x4A3, 0x4A4 => 0x4A5, 0x4A6 => 0x4A7, 0x4A8 => 0x4A9, 0x4AA => 0x4AB, 0x4AC => 0x4AD, 0x4AE => 0x4AF, 0x4B0 => 0x4B1, 0x4B2 => 0x4B3, 0x4B4 => 0x4B5, 0x4B6 => 0x4B7, 0x4B8 => 0x4B9, 0x4BA => 0x4BB, 0x4BC => 0x4BD, 0x4BE => 0x4BF, 0x4C0 => 0x4CF, 0x4C1 => 0x4C2, 0x4C3 => 0x4C4, 0x4C5 => 0x4C6, 0x4C7 => 0x4C8, 0x4C9 => 0x4CA, 0x4CB => 0x4CC, 0x4CD => 0x4CE, 0x4D0 => 0x4D1, 0x4D2 => 0x4D3, 0x4D4 => 0x4D5, 0x4D6 => 0x4D7, 0x4D8 => 0x4D9, 0x4DA => 0x4DB, 0x4DC => 0x4DD, 0x4DE => 0x4DF, 0x4E0 => 0x4E1, 0x4E2 => 0x4E3, 0x4E4 => 0x4E5, 0x4E6 => 0x4E7, 0x4E8 => 0x4E9, 0x4EA => 0x4EB, 0x4EC => 0x4ED, 0x4EE => 0x4EF, 0x4F0 => 0x4F1, 0x4F2 => 0x4F3, 0x4F4 => 0x4F5, 0x4F6 => 0x4F7, 0x4F8 => 0x4F9, 0x4FA => 0x4FB, 0x4FC => 0x4FD, 0x4FE => 0x4FF, 0x500 => 0x501, 0x502 => 0x503, 0x504 => 0x505, 0x506 => 0x507, 0x508 => 0x509, 0x50A => 0x50B, 0x50C => 0x50D, 0x50E => 0x50F, 0x510 => 0x511, 0x512 => 0x513, 0x514 => 0x515, 0x516 => 0x517, 0x518 => 0x519, 0x51A => 0x51B, 0x51C => 0x51D, 0x51E => 0x51F, 0x520 => 0x521, 0x522 => 0x523, 0x524 => 0x525, 0x526 => 0x527, 0x528 => 0x529, 0x52A => 0x52B, 0x52C => 0x52D, 0x52E => 0x52F, 0x531 => 0x561, 0x532 => 0x562, 0x533 => 0x563, 0x534 => 0x564, 0x535 => 0x565, 0x536 => 0x566, 0x537 => 0x567, 0x538 => 0x568, 0x539 => 0x569, 0x53A => 0x56A, 0x53B => 0x56B, 0x53C => 0x56C, 0x53D => 0x56D, 0x53E => 0x56E, 0x53F => 0x56F, 0x540 => 0x570, 0x541 => 0x571, 0x542 => 0x572, 0x543 => 0x573, 0x544 => 0x574, 0x545 => 0x575, 0x546 => 0x576, 0x547 => 0x577, 0x548 => 0x578, 0x549 => 0x579, 0x54A => 0x57A, 0x54B => 0x57B, 0x54C => 0x57C, 0x54D => 0x57D, 0x54E => 0x57E, 0x54F => 0x57F, 0x550 => 0x580, 0x551 => 0x581, 0x552 => 0x582, 0x553 => 0x583, 0x554 => 0x584, 0x555 => 0x585, 0x556 => 0x586, 0x10A0 => 0x2D00, 0x10A1 => 0x2D01, 0x10A2 => 0x2D02, 0x10A3 => 0x2D03, 0x10A4 => 0x2D04, 0x10A5 => 0x2D05, 0x10A6 => 0x2D06, 0x10A7 => 0x2D07, 0x10A8 => 0x2D08, 0x10A9 => 0x2D09, 0x10AA => 0x2D0A, 0x10AB => 0x2D0B, 0x10AC => 0x2D0C, 0x10AD => 0x2D0D, 0x10AE => 0x2D0E, 0x10AF => 0x2D0F, 0x10B0 => 0x2D10, 0x10B1 => 0x2D11, 0x10B2 => 0x2D12, 0x10B3 => 0x2D13, 0x10B4 => 0x2D14, 0x10B5 => 0x2D15, 0x10B6 => 0x2D16, 0x10B7 => 0x2D17, 0x10B8 => 0x2D18, 0x10B9 => 0x2D19, 0x10BA => 0x2D1A, 0x10BB => 0x2D1B, 0x10BC => 0x2D1C, 0x10BD => 0x2D1D, 0x10BE => 0x2D1E, 0x10BF => 0x2D1F, 0x10C0 => 0x2D20, 0x10C1 => 0x2D21, 0x10C2 => 0x2D22, 0x10C3 => 0x2D23, 0x10C4 => 0x2D24, 0x10C5 => 0x2D25, 0x10C7 => 0x2D27, 0x10CD => 0x2D2D, 0x13A0 => 0xAB70, 0x13A1 => 0xAB71, 0x13A2 => 0xAB72, 0x13A3 => 0xAB73, 0x13A4 => 0xAB74, 0x13A5 => 0xAB75, 0x13A6 => 0xAB76, 0x13A7 => 0xAB77, 0x13A8 => 0xAB78, 0x13A9 => 0xAB79, 0x13AA => 0xAB7A, 0x13AB => 0xAB7B, 0x13AC => 0xAB7C, 0x13AD => 0xAB7D, 0x13AE => 0xAB7E, 0x13AF => 0xAB7F, 0x13B0 => 0xAB80, 0x13B1 => 0xAB81, 0x13B2 => 0xAB82, 0x13B3 => 0xAB83, 0x13B4 => 0xAB84, 0x13B5 => 0xAB85, 0x13B6 => 0xAB86, 0x13B7 => 0xAB87, 0x13B8 => 0xAB88, 0x13B9 => 0xAB89, 0x13BA => 0xAB8A, 0x13BB => 0xAB8B, 0x13BC => 0xAB8C, 0x13BD => 0xAB8D, 0x13BE => 0xAB8E, 0x13BF => 0xAB8F, 0x13C0 => 0xAB90, 0x13C1 => 0xAB91, 0x13C2 => 0xAB92, 0x13C3 => 0xAB93, 0x13C4 => 0xAB94, 0x13C5 => 0xAB95, 0x13C6 => 0xAB96, 0x13C7 => 0xAB97, 0x13C8 => 0xAB98, 0x13C9 => 0xAB99, 0x13CA => 0xAB9A, 0x13CB => 0xAB9B, 0x13CC => 0xAB9C, 0x13CD => 0xAB9D, 0x13CE => 0xAB9E, 0x13CF => 0xAB9F, 0x13D0 => 0xABA0, 0x13D1 => 0xABA1, 0x13D2 => 0xABA2, 0x13D3 => 0xABA3, 0x13D4 => 0xABA4, 0x13D5 => 0xABA5, 0x13D6 => 0xABA6, 0x13D7 => 0xABA7, 0x13D8 => 0xABA8, 0x13D9 => 0xABA9, 0x13DA => 0xABAA, 0x13DB => 0xABAB, 0x13DC => 0xABAC, 0x13DD => 0xABAD, 0x13DE => 0xABAE, 0x13DF => 0xABAF, 0x13E0 => 0xABB0, 0x13E1 => 0xABB1, 0x13E2 => 0xABB2, 0x13E3 => 0xABB3, 0x13E4 => 0xABB4, 0x13E5 => 0xABB5, 0x13E6 => 0xABB6, 0x13E7 => 0xABB7, 0x13E8 => 0xABB8, 0x13E9 => 0xABB9, 0x13EA => 0xABBA, 0x13EB => 0xABBB, 0x13EC => 0xABBC, 0x13ED => 0xABBD, 0x13EE => 0xABBE, 0x13EF => 0xABBF, 0x13F0 => 0x13F8, 0x13F1 => 0x13F9, 0x13F2 => 0x13FA, 0x13F3 => 0x13FB, 0x13F4 => 0x13FC, 0x13F5 => 0x13FD, 0x1C90 => 0x10D0, 0x1C91 => 0x10D1, 0x1C92 => 0x10D2, 0x1C93 => 0x10D3, 0x1C94 => 0x10D4, 0x1C95 => 0x10D5, 0x1C96 => 0x10D6, 0x1C97 => 0x10D7, 0x1C98 => 0x10D8, 0x1C99 => 0x10D9, 0x1C9A => 0x10DA, 0x1C9B => 0x10DB, 0x1C9C => 0x10DC, 0x1C9D => 0x10DD, 0x1C9E => 0x10DE, 0x1C9F => 0x10DF, 0x1CA0 => 0x10E0, 0x1CA1 => 0x10E1, 0x1CA2 => 0x10E2, 0x1CA3 => 0x10E3, 0x1CA4 => 0x10E4, 0x1CA5 => 0x10E5, 0x1CA6 => 0x10E6, 0x1CA7 => 0x10E7, 0x1CA8 => 0x10E8, 0x1CA9 => 0x10E9, 0x1CAA => 0x10EA, 0x1CAB => 0x10EB, 0x1CAC => 0x10EC, 0x1CAD => 0x10ED, 0x1CAE => 0x10EE, 0x1CAF => 0x10EF, 0x1CB0 => 0x10F0, 0x1CB1 => 0x10F1, 0x1CB2 => 0x10F2, 0x1CB3 => 0x10F3, 0x1CB4 => 0x10F4, 0x1CB5 => 0x10F5, 0x1CB6 => 0x10F6, 0x1CB7 => 0x10F7, 0x1CB8 => 0x10F8, 0x1CB9 => 0x10F9, 0x1CBA => 0x10FA, 0x1CBD => 0x10FD, 0x1CBE => 0x10FE, 0x1CBF => 0x10FF, 0x1E00 => 0x1E01, 0x1E02 => 0x1E03, 0x1E04 => 0x1E05, 0x1E06 => 0x1E07, 0x1E08 => 0x1E09, 0x1E0A => 0x1E0B, 0x1E0C => 0x1E0D, 0x1E0E => 0x1E0F, 0x1E10 => 0x1E11, 0x1E12 => 0x1E13, 0x1E14 => 0x1E15, 0x1E16 => 0x1E17, 0x1E18 => 0x1E19, 0x1E1A => 0x1E1B, 0x1E1C => 0x1E1D, 0x1E1E => 0x1E1F, 0x1E20 => 0x1E21, 0x1E22 => 0x1E23, 0x1E24 => 0x1E25, 0x1E26 => 0x1E27, 0x1E28 => 0x1E29, 0x1E2A => 0x1E2B, 0x1E2C => 0x1E2D, 0x1E2E => 0x1E2F, 0x1E30 => 0x1E31, 0x1E32 => 0x1E33, 0x1E34 => 0x1E35, 0x1E36 => 0x1E37, 0x1E38 => 0x1E39, 0x1E3A => 0x1E3B, 0x1E3C => 0x1E3D, 0x1E3E => 0x1E3F, 0x1E40 => 0x1E41, 0x1E42 => 0x1E43, 0x1E44 => 0x1E45, 0x1E46 => 0x1E47, 0x1E48 => 0x1E49, 0x1E4A => 0x1E4B, 0x1E4C => 0x1E4D, 0x1E4E => 0x1E4F, 0x1E50 => 0x1E51, 0x1E52 => 0x1E53, 0x1E54 => 0x1E55, 0x1E56 => 0x1E57, 0x1E58 => 0x1E59, 0x1E5A => 0x1E5B, 0x1E5C => 0x1E5D, 0x1E5E => 0x1E5F, 0x1E60 => 0x1E61, 0x1E62 => 0x1E63, 0x1E64 => 0x1E65, 0x1E66 => 0x1E67, 0x1E68 => 0x1E69, 0x1E6A => 0x1E6B, 0x1E6C => 0x1E6D, 0x1E6E => 0x1E6F, 0x1E70 => 0x1E71, 0x1E72 => 0x1E73, 0x1E74 => 0x1E75, 0x1E76 => 0x1E77, 0x1E78 => 0x1E79, 0x1E7A => 0x1E7B, 0x1E7C => 0x1E7D, 0x1E7E => 0x1E7F, 0x1E80 => 0x1E81, 0x1E82 => 0x1E83, 0x1E84 => 0x1E85, 0x1E86 => 0x1E87, 0x1E88 => 0x1E89, 0x1E8A => 0x1E8B, 0x1E8C => 0x1E8D, 0x1E8E => 0x1E8F, 0x1E90 => 0x1E91, 0x1E92 => 0x1E93, 0x1E94 => 0x1E95, 0x1E9E => 0xDF, 0x1EA0 => 0x1EA1, 0x1EA2 => 0x1EA3, 0x1EA4 => 0x1EA5, 0x1EA6 => 0x1EA7, 0x1EA8 => 0x1EA9, 0x1EAA => 0x1EAB, 0x1EAC => 0x1EAD, 0x1EAE => 0x1EAF, 0x1EB0 => 0x1EB1, 0x1EB2 => 0x1EB3, 0x1EB4 => 0x1EB5, 0x1EB6 => 0x1EB7, 0x1EB8 => 0x1EB9, 0x1EBA => 0x1EBB, 0x1EBC => 0x1EBD, 0x1EBE => 0x1EBF, 0x1EC0 => 0x1EC1, 0x1EC2 => 0x1EC3, 0x1EC4 => 0x1EC5, 0x1EC6 => 0x1EC7, 0x1EC8 => 0x1EC9, 0x1ECA => 0x1ECB, 0x1ECC => 0x1ECD, 0x1ECE => 0x1ECF, 0x1ED0 => 0x1ED1, 0x1ED2 => 0x1ED3, 0x1ED4 => 0x1ED5, 0x1ED6 => 0x1ED7, 0x1ED8 => 0x1ED9, 0x1EDA => 0x1EDB, 0x1EDC => 0x1EDD, 0x1EDE => 0x1EDF, 0x1EE0 => 0x1EE1, 0x1EE2 => 0x1EE3, 0x1EE4 => 0x1EE5, 0x1EE6 => 0x1EE7, 0x1EE8 => 0x1EE9, 0x1EEA => 0x1EEB, 0x1EEC => 0x1EED, 0x1EEE => 0x1EEF, 0x1EF0 => 0x1EF1, 0x1EF2 => 0x1EF3, 0x1EF4 => 0x1EF5, 0x1EF6 => 0x1EF7, 0x1EF8 => 0x1EF9, 0x1EFA => 0x1EFB, 0x1EFC => 0x1EFD, 0x1EFE => 0x1EFF, 0x1F08 => 0x1F00, 0x1F09 => 0x1F01, 0x1F0A => 0x1F02, 0x1F0B => 0x1F03, 0x1F0C => 0x1F04, 0x1F0D => 0x1F05, 0x1F0E => 0x1F06, 0x1F0F => 0x1F07, 0x1F18 => 0x1F10, 0x1F19 => 0x1F11, 0x1F1A => 0x1F12, 0x1F1B => 0x1F13, 0x1F1C => 0x1F14, 0x1F1D => 0x1F15, 0x1F28 => 0x1F20, 0x1F29 => 0x1F21, 0x1F2A => 0x1F22, 0x1F2B => 0x1F23, 0x1F2C => 0x1F24, 0x1F2D => 0x1F25, 0x1F2E => 0x1F26, 0x1F2F => 0x1F27, 0x1F38 => 0x1F30, 0x1F39 => 0x1F31, 0x1F3A => 0x1F32, 0x1F3B => 0x1F33, 0x1F3C => 0x1F34, 0x1F3D => 0x1F35, 0x1F3E => 0x1F36, 0x1F3F => 0x1F37, 0x1F48 => 0x1F40, 0x1F49 => 0x1F41, 0x1F4A => 0x1F42, 0x1F4B => 0x1F43, 0x1F4C => 0x1F44, 0x1F4D => 0x1F45, 0x1F59 => 0x1F51, 0x1F5B => 0x1F53, 0x1F5D => 0x1F55, 0x1F5F => 0x1F57, 0x1F68 => 0x1F60, 0x1F69 => 0x1F61, 0x1F6A => 0x1F62, 0x1F6B => 0x1F63, 0x1F6C => 0x1F64, 0x1F6D => 0x1F65, 0x1F6E => 0x1F66, 0x1F6F => 0x1F67, 0x1F88 => 0x1F80, 0x1F89 => 0x1F81, 0x1F8A => 0x1F82, 0x1F8B => 0x1F83, 0x1F8C => 0x1F84, 0x1F8D => 0x1F85, 0x1F8E => 0x1F86, 0x1F8F => 0x1F87, 0x1F98 => 0x1F90, 0x1F99 => 0x1F91, 0x1F9A => 0x1F92, 0x1F9B => 0x1F93, 0x1F9C => 0x1F94, 0x1F9D => 0x1F95, 0x1F9E => 0x1F96, 0x1F9F => 0x1F97, 0x1FA8 => 0x1FA0, 0x1FA9 => 0x1FA1, 0x1FAA => 0x1FA2, 0x1FAB => 0x1FA3, 0x1FAC => 0x1FA4, 0x1FAD => 0x1FA5, 0x1FAE => 0x1FA6, 0x1FAF => 0x1FA7, 0x1FB8 => 0x1FB0, 0x1FB9 => 0x1FB1, 0x1FBA => 0x1F70, 0x1FBB => 0x1F71, 0x1FBC => 0x1FB3, 0x1FC8 => 0x1F72, 0x1FC9 => 0x1F73, 0x1FCA => 0x1F74, 0x1FCB => 0x1F75, 0x1FCC => 0x1FC3, 0x1FD8 => 0x1FD0, 0x1FD9 => 0x1FD1, 0x1FDA => 0x1F76, 0x1FDB => 0x1F77, 0x1FE8 => 0x1FE0, 0x1FE9 => 0x1FE1, 0x1FEA => 0x1F7A, 0x1FEB => 0x1F7B, 0x1FEC => 0x1FE5, 0x1FF8 => 0x1F78, 0x1FF9 => 0x1F79, 0x1FFA => 0x1F7C, 0x1FFB => 0x1F7D, 0x1FFC => 0x1FF3, 0x2126 => 0x3C9, 0x212A => 0x6B, 0x212B => 0xE5, 0x2132 => 0x214E, 0x2160 => 0x2170, 0x2161 => 0x2171, 0x2162 => 0x2172, 0x2163 => 0x2173, 0x2164 => 0x2174, 0x2165 => 0x2175, 0x2166 => 0x2176, 0x2167 => 0x2177, 0x2168 => 0x2178, 0x2169 => 0x2179, 0x216A => 0x217A, 0x216B => 0x217B, 0x216C => 0x217C, 0x216D => 0x217D, 0x216E => 0x217E, 0x216F => 0x217F, 0x2183 => 0x2184, 0x24B6 => 0x24D0, 0x24B7 => 0x24D1, 0x24B8 => 0x24D2, 0x24B9 => 0x24D3, 0x24BA => 0x24D4, 0x24BB => 0x24D5, 0x24BC => 0x24D6, 0x24BD => 0x24D7, 0x24BE => 0x24D8, 0x24BF => 0x24D9, 0x24C0 => 0x24DA, 0x24C1 => 0x24DB, 0x24C2 => 0x24DC, 0x24C3 => 0x24DD, 0x24C4 => 0x24DE, 0x24C5 => 0x24DF, 0x24C6 => 0x24E0, 0x24C7 => 0x24E1, 0x24C8 => 0x24E2, 0x24C9 => 0x24E3, 0x24CA => 0x24E4, 0x24CB => 0x24E5, 0x24CC => 0x24E6, 0x24CD => 0x24E7, 0x24CE => 0x24E8, 0x24CF => 0x24E9, 0x2C00 => 0x2C30, 0x2C01 => 0x2C31, 0x2C02 => 0x2C32, 0x2C03 => 0x2C33, 0x2C04 => 0x2C34, 0x2C05 => 0x2C35, 0x2C06 => 0x2C36, 0x2C07 => 0x2C37, 0x2C08 => 0x2C38, 0x2C09 => 0x2C39, 0x2C0A => 0x2C3A, 0x2C0B => 0x2C3B, 0x2C0C => 0x2C3C, 0x2C0D => 0x2C3D, 0x2C0E => 0x2C3E, 0x2C0F => 0x2C3F, 0x2C10 => 0x2C40, 0x2C11 => 0x2C41, 0x2C12 => 0x2C42, 0x2C13 => 0x2C43, 0x2C14 => 0x2C44, 0x2C15 => 0x2C45, 0x2C16 => 0x2C46, 0x2C17 => 0x2C47, 0x2C18 => 0x2C48, 0x2C19 => 0x2C49, 0x2C1A => 0x2C4A, 0x2C1B => 0x2C4B, 0x2C1C => 0x2C4C, 0x2C1D => 0x2C4D, 0x2C1E => 0x2C4E, 0x2C1F => 0x2C4F, 0x2C20 => 0x2C50, 0x2C21 => 0x2C51, 0x2C22 => 0x2C52, 0x2C23 => 0x2C53, 0x2C24 => 0x2C54, 0x2C25 => 0x2C55, 0x2C26 => 0x2C56, 0x2C27 => 0x2C57, 0x2C28 => 0x2C58, 0x2C29 => 0x2C59, 0x2C2A => 0x2C5A, 0x2C2B => 0x2C5B, 0x2C2C => 0x2C5C, 0x2C2D => 0x2C5D, 0x2C2E => 0x2C5E, 0x2C60 => 0x2C61, 0x2C62 => 0x26B, 0x2C63 => 0x1D7D, 0x2C64 => 0x27D, 0x2C67 => 0x2C68, 0x2C69 => 0x2C6A, 0x2C6B => 0x2C6C, 0x2C6D => 0x251, 0x2C6E => 0x271, 0x2C6F => 0x250, 0x2C70 => 0x252, 0x2C72 => 0x2C73, 0x2C75 => 0x2C76, 0x2C7E => 0x23F, 0x2C7F => 0x240, 0x2C80 => 0x2C81, 0x2C82 => 0x2C83, 0x2C84 => 0x2C85, 0x2C86 => 0x2C87, 0x2C88 => 0x2C89, 0x2C8A => 0x2C8B, 0x2C8C => 0x2C8D, 0x2C8E => 0x2C8F, 0x2C90 => 0x2C91, 0x2C92 => 0x2C93, 0x2C94 => 0x2C95, 0x2C96 => 0x2C97, 0x2C98 => 0x2C99, 0x2C9A => 0x2C9B, 0x2C9C => 0x2C9D, 0x2C9E => 0x2C9F, 0x2CA0 => 0x2CA1, 0x2CA2 => 0x2CA3, 0x2CA4 => 0x2CA5, 0x2CA6 => 0x2CA7, 0x2CA8 => 0x2CA9, 0x2CAA => 0x2CAB, 0x2CAC => 0x2CAD, 0x2CAE => 0x2CAF, 0x2CB0 => 0x2CB1, 0x2CB2 => 0x2CB3, 0x2CB4 => 0x2CB5, 0x2CB6 => 0x2CB7, 0x2CB8 => 0x2CB9, 0x2CBA => 0x2CBB, 0x2CBC => 0x2CBD, 0x2CBE => 0x2CBF, 0x2CC0 => 0x2CC1, 0x2CC2 => 0x2CC3, 0x2CC4 => 0x2CC5, 0x2CC6 => 0x2CC7, 0x2CC8 => 0x2CC9, 0x2CCA => 0x2CCB, 0x2CCC => 0x2CCD, 0x2CCE => 0x2CCF, 0x2CD0 => 0x2CD1, 0x2CD2 => 0x2CD3, 0x2CD4 => 0x2CD5, 0x2CD6 => 0x2CD7, 0x2CD8 => 0x2CD9, 0x2CDA => 0x2CDB, 0x2CDC => 0x2CDD, 0x2CDE => 0x2CDF, 0x2CE0 => 0x2CE1, 0x2CE2 => 0x2CE3, 0x2CEB => 0x2CEC, 0x2CED => 0x2CEE, 0x2CF2 => 0x2CF3, 0xA640 => 0xA641, 0xA642 => 0xA643, 0xA644 => 0xA645, 0xA646 => 0xA647, 0xA648 => 0xA649, 0xA64A => 0xA64B, 0xA64C => 0xA64D, 0xA64E => 0xA64F, 0xA650 => 0xA651, 0xA652 => 0xA653, 0xA654 => 0xA655, 0xA656 => 0xA657, 0xA658 => 0xA659, 0xA65A => 0xA65B, 0xA65C => 0xA65D, 0xA65E => 0xA65F, 0xA660 => 0xA661, 0xA662 => 0xA663, 0xA664 => 0xA665, 0xA666 => 0xA667, 0xA668 => 0xA669, 0xA66A => 0xA66B, 0xA66C => 0xA66D, 0xA680 => 0xA681, 0xA682 => 0xA683, 0xA684 => 0xA685, 0xA686 => 0xA687, 0xA688 => 0xA689, 0xA68A => 0xA68B, 0xA68C => 0xA68D, 0xA68E => 0xA68F, 0xA690 => 0xA691, 0xA692 => 0xA693, 0xA694 => 0xA695, 0xA696 => 0xA697, 0xA698 => 0xA699, 0xA69A => 0xA69B, 0xA722 => 0xA723, 0xA724 => 0xA725, 0xA726 => 0xA727, 0xA728 => 0xA729, 0xA72A => 0xA72B, 0xA72C => 0xA72D, 0xA72E => 0xA72F, 0xA732 => 0xA733, 0xA734 => 0xA735, 0xA736 => 0xA737, 0xA738 => 0xA739, 0xA73A => 0xA73B, 0xA73C => 0xA73D, 0xA73E => 0xA73F, 0xA740 => 0xA741, 0xA742 => 0xA743, 0xA744 => 0xA745, 0xA746 => 0xA747, 0xA748 => 0xA749, 0xA74A => 0xA74B, 0xA74C => 0xA74D, 0xA74E => 0xA74F, 0xA750 => 0xA751, 0xA752 => 0xA753, 0xA754 => 0xA755, 0xA756 => 0xA757, 0xA758 => 0xA759, 0xA75A => 0xA75B, 0xA75C => 0xA75D, 0xA75E => 0xA75F, 0xA760 => 0xA761, 0xA762 => 0xA763, 0xA764 => 0xA765, 0xA766 => 0xA767, 0xA768 => 0xA769, 0xA76A => 0xA76B, 0xA76C => 0xA76D, 0xA76E => 0xA76F, 0xA779 => 0xA77A, 0xA77B => 0xA77C, 0xA77D => 0x1D79, 0xA77E => 0xA77F, 0xA780 => 0xA781, 0xA782 => 0xA783, 0xA784 => 0xA785, 0xA786 => 0xA787, 0xA78B => 0xA78C, 0xA78D => 0x265, 0xA790 => 0xA791, 0xA792 => 0xA793, 0xA796 => 0xA797, 0xA798 => 0xA799, 0xA79A => 0xA79B, 0xA79C => 0xA79D, 0xA79E => 0xA79F, 0xA7A0 => 0xA7A1, 0xA7A2 => 0xA7A3, 0xA7A4 => 0xA7A5, 0xA7A6 => 0xA7A7, 0xA7A8 => 0xA7A9, 0xA7AA => 0x266, 0xA7AB => 0x25C, 0xA7AC => 0x261, 0xA7AD => 0x26C, 0xA7AE => 0x26A, 0xA7B0 => 0x29E, 0xA7B1 => 0x287, 0xA7B2 => 0x29D, 0xA7B3 => 0xAB53, 0xA7B4 => 0xA7B5, 0xA7B6 => 0xA7B7, 0xA7B8 => 0xA7B9, 0xA7BA => 0xA7BB, 0xA7BC => 0xA7BD, 0xA7BE => 0xA7BF, 0xA7C2 => 0xA7C3, 0xA7C4 => 0xA794, 0xA7C5 => 0x282, 0xA7C6 => 0x1D8E, 0xA7C7 => 0xA7C8, 0xA7C9 => 0xA7CA, 0xA7F5 => 0xA7F6, 0xFF21 => 0xFF41, 0xFF22 => 0xFF42, 0xFF23 => 0xFF43, 0xFF24 => 0xFF44, 0xFF25 => 0xFF45, 0xFF26 => 0xFF46, 0xFF27 => 0xFF47, 0xFF28 => 0xFF48, 0xFF29 => 0xFF49, 0xFF2A => 0xFF4A, 0xFF2B => 0xFF4B, 0xFF2C => 0xFF4C, 0xFF2D => 0xFF4D, 0xFF2E => 0xFF4E, 0xFF2F => 0xFF4F, 0xFF30 => 0xFF50, 0xFF31 => 0xFF51, 0xFF32 => 0xFF52, 0xFF33 => 0xFF53, 0xFF34 => 0xFF54, 0xFF35 => 0xFF55, 0xFF36 => 0xFF56, 0xFF37 => 0xFF57, 0xFF38 => 0xFF58, 0xFF39 => 0xFF59, 0xFF3A => 0xFF5A, 0x10400 => 0x10428, 0x10401 => 0x10429, 0x10402 => 0x1042A, 0x10403 => 0x1042B, 0x10404 => 0x1042C, 0x10405 => 0x1042D, 0x10406 => 0x1042E, 0x10407 => 0x1042F, 0x10408 => 0x10430, 0x10409 => 0x10431, 0x1040A => 0x10432, 0x1040B => 0x10433, 0x1040C => 0x10434, 0x1040D => 0x10435, 0x1040E => 0x10436, 0x1040F => 0x10437, 0x10410 => 0x10438, 0x10411 => 0x10439, 0x10412 => 0x1043A, 0x10413 => 0x1043B, 0x10414 => 0x1043C, 0x10415 => 0x1043D, 0x10416 => 0x1043E, 0x10417 => 0x1043F, 0x10418 => 0x10440, 0x10419 => 0x10441, 0x1041A => 0x10442, 0x1041B => 0x10443, 0x1041C => 0x10444, 0x1041D => 0x10445, 0x1041E => 0x10446, 0x1041F => 0x10447, 0x10420 => 0x10448, 0x10421 => 0x10449, 0x10422 => 0x1044A, 0x10423 => 0x1044B, 0x10424 => 0x1044C, 0x10425 => 0x1044D, 0x10426 => 0x1044E, 0x10427 => 0x1044F, 0x104B0 => 0x104D8, 0x104B1 => 0x104D9, 0x104B2 => 0x104DA, 0x104B3 => 0x104DB, 0x104B4 => 0x104DC, 0x104B5 => 0x104DD, 0x104B6 => 0x104DE, 0x104B7 => 0x104DF, 0x104B8 => 0x104E0, 0x104B9 => 0x104E1, 0x104BA => 0x104E2, 0x104BB => 0x104E3, 0x104BC => 0x104E4, 0x104BD => 0x104E5, 0x104BE => 0x104E6, 0x104BF => 0x104E7, 0x104C0 => 0x104E8, 0x104C1 => 0x104E9, 0x104C2 => 0x104EA, 0x104C3 => 0x104EB, 0x104C4 => 0x104EC, 0x104C5 => 0x104ED, 0x104C6 => 0x104EE, 0x104C7 => 0x104EF, 0x104C8 => 0x104F0, 0x104C9 => 0x104F1, 0x104CA => 0x104F2, 0x104CB => 0x104F3, 0x104CC => 0x104F4, 0x104CD => 0x104F5, 0x104CE => 0x104F6, 0x104CF => 0x104F7, 0x104D0 => 0x104F8, 0x104D1 => 0x104F9, 0x104D2 => 0x104FA, 0x104D3 => 0x104FB, 0x10C80 => 0x10CC0, 0x10C81 => 0x10CC1, 0x10C82 => 0x10CC2, 0x10C83 => 0x10CC3, 0x10C84 => 0x10CC4, 0x10C85 => 0x10CC5, 0x10C86 => 0x10CC6, 0x10C87 => 0x10CC7, 0x10C88 => 0x10CC8, 0x10C89 => 0x10CC9, 0x10C8A => 0x10CCA, 0x10C8B => 0x10CCB, 0x10C8C => 0x10CCC, 0x10C8D => 0x10CCD, 0x10C8E => 0x10CCE, 0x10C8F => 0x10CCF, 0x10C90 => 0x10CD0, 0x10C91 => 0x10CD1, 0x10C92 => 0x10CD2, 0x10C93 => 0x10CD3, 0x10C94 => 0x10CD4, 0x10C95 => 0x10CD5, 0x10C96 => 0x10CD6, 0x10C97 => 0x10CD7, 0x10C98 => 0x10CD8, 0x10C99 => 0x10CD9, 0x10C9A => 0x10CDA, 0x10C9B => 0x10CDB, 0x10C9C => 0x10CDC, 0x10C9D => 0x10CDD, 0x10C9E => 0x10CDE, 0x10C9F => 0x10CDF, 0x10CA0 => 0x10CE0, 0x10CA1 => 0x10CE1, 0x10CA2 => 0x10CE2, 0x10CA3 => 0x10CE3, 0x10CA4 => 0x10CE4, 0x10CA5 => 0x10CE5, 0x10CA6 => 0x10CE6, 0x10CA7 => 0x10CE7, 0x10CA8 => 0x10CE8, 0x10CA9 => 0x10CE9, 0x10CAA => 0x10CEA, 0x10CAB => 0x10CEB, 0x10CAC => 0x10CEC, 0x10CAD => 0x10CED, 0x10CAE => 0x10CEE, 0x10CAF => 0x10CEF, 0x10CB0 => 0x10CF0, 0x10CB1 => 0x10CF1, 0x10CB2 => 0x10CF2, 0x118A0 => 0x118C0, 0x118A1 => 0x118C1, 0x118A2 => 0x118C2, 0x118A3 => 0x118C3, 0x118A4 => 0x118C4, 0x118A5 => 0x118C5, 0x118A6 => 0x118C6, 0x118A7 => 0x118C7, 0x118A8 => 0x118C8, 0x118A9 => 0x118C9, 0x118AA => 0x118CA, 0x118AB => 0x118CB, 0x118AC => 0x118CC, 0x118AD => 0x118CD, 0x118AE => 0x118CE, 0x118AF => 0x118CF, 0x118B0 => 0x118D0, 0x118B1 => 0x118D1, 0x118B2 => 0x118D2, 0x118B3 => 0x118D3, 0x118B4 => 0x118D4, 0x118B5 => 0x118D5, 0x118B6 => 0x118D6, 0x118B7 => 0x118D7, 0x118B8 => 0x118D8, 0x118B9 => 0x118D9, 0x118BA => 0x118DA, 0x118BB => 0x118DB, 0x118BC => 0x118DC, 0x118BD => 0x118DD, 0x118BE => 0x118DE, 0x118BF => 0x118DF, 0x16E40 => 0x16E60, 0x16E41 => 0x16E61, 0x16E42 => 0x16E62, 0x16E43 => 0x16E63, 0x16E44 => 0x16E64, 0x16E45 => 0x16E65, 0x16E46 => 0x16E66, 0x16E47 => 0x16E67, 0x16E48 => 0x16E68, 0x16E49 => 0x16E69, 0x16E4A => 0x16E6A, 0x16E4B => 0x16E6B, 0x16E4C => 0x16E6C, 0x16E4D => 0x16E6D, 0x16E4E => 0x16E6E, 0x16E4F => 0x16E6F, 0x16E50 => 0x16E70, 0x16E51 => 0x16E71, 0x16E52 => 0x16E72, 0x16E53 => 0x16E73, 0x16E54 => 0x16E74, 0x16E55 => 0x16E75, 0x16E56 => 0x16E76, 0x16E57 => 0x16E77, 0x16E58 => 0x16E78, 0x16E59 => 0x16E79, 0x16E5A => 0x16E7A, 0x16E5B => 0x16E7B, 0x16E5C => 0x16E7C, 0x16E5D => 0x16E7D, 0x16E5E => 0x16E7E, 0x16E5F => 0x16E7F, 0x1E900 => 0x1E922, 0x1E901 => 0x1E923, 0x1E902 => 0x1E924, 0x1E903 => 0x1E925, 0x1E904 => 0x1E926, 0x1E905 => 0x1E927, 0x1E906 => 0x1E928, 0x1E907 => 0x1E929, 0x1E908 => 0x1E92A, 0x1E909 => 0x1E92B, 0x1E90A => 0x1E92C, 0x1E90B => 0x1E92D, 0x1E90C => 0x1E92E, 0x1E90D => 0x1E92F, 0x1E90E => 0x1E930, 0x1E90F => 0x1E931, 0x1E910 => 0x1E932, 0x1E911 => 0x1E933, 0x1E912 => 0x1E934, 0x1E913 => 0x1E935, 0x1E914 => 0x1E936, 0x1E915 => 0x1E937, 0x1E916 => 0x1E938, 0x1E917 => 0x1E939, 0x1E918 => 0x1E93A, 0x1E919 => 0x1E93B, 0x1E91A => 0x1E93C, 0x1E91B => 0x1E93D, 0x1E91C => 0x1E93E, 0x1E91D => 0x1E93F, 0x1E91E => 0x1E940, 0x1E91F => 0x1E941, 0x1E920 => 0x1E942, 0x1E921 => 0x1E943, ]; string/res/ascii.php 0000777 00000026301 15252016106 0010453 0 ustar 00 <?php return [ 0xA9 => 0x28, 0xAA => 0x61, 0xB0 => 0x30, 0xB2 => 0x32, 0xB3 => 0x33, 0xB5 => 0x75, 0xB9 => 0x31, 0xBA => 0x6F, 0xC0 => 0x41, 0xC1 => 0x41, 0xC2 => 0x41, 0xC3 => 0x41, 0xC4 => 0x41, 0xC5 => 0x41, 0xC6 => 0x41, 0xC7 => 0x43, 0xC8 => 0x45, 0xC9 => 0x45, 0xCA => 0x45, 0xCB => 0x45, 0xCC => 0x49, 0xCD => 0x49, 0xCE => 0x49, 0xCF => 0x49, 0xD0 => 0x44, 0xD1 => 0x4E, 0xD2 => 0x4F, 0xD3 => 0x4F, 0xD4 => 0x4F, 0xD5 => 0x4F, 0xD6 => 0x4F, 0xD8 => 0x4F, 0xD9 => 0x55, 0xDA => 0x55, 0xDB => 0x55, 0xDC => 0x55, 0xDD => 0x59, 0xDE => 0x54, 0xDF => 0x73, 0xE0 => 0x61, 0xE1 => 0x61, 0xE2 => 0x61, 0xE3 => 0x61, 0xE4 => 0x61, 0xE5 => 0x61, 0xE6 => 0x61, 0xE7 => 0x63, 0xE8 => 0x65, 0xE9 => 0x65, 0xEA => 0x65, 0xEB => 0x65, 0xEC => 0x69, 0xED => 0x69, 0xEE => 0x69, 0xEF => 0x69, 0xF0 => 0x64, 0xF1 => 0x6E, 0xF2 => 0x6F, 0xF3 => 0x6F, 0xF4 => 0x6F, 0xF5 => 0x6F, 0xF6 => 0x6F, 0xF8 => 0x6F, 0xF9 => 0x75, 0xFA => 0x75, 0xFB => 0x75, 0xFC => 0x75, 0xFD => 0x79, 0xFE => 0x74, 0xFF => 0x79, 0x100 => 0x41, 0x101 => 0x61, 0x102 => 0x41, 0x103 => 0x61, 0x104 => 0x41, 0x105 => 0x61, 0x106 => 0x43, 0x107 => 0x63, 0x108 => 0x43, 0x109 => 0x63, 0x10A => 0x43, 0x10B => 0x63, 0x10C => 0x43, 0x10D => 0x63, 0x10E => 0x44, 0x10F => 0x64, 0x110 => 0x44, 0x111 => 0x64, 0x112 => 0x45, 0x113 => 0x65, 0x114 => 0x45, 0x115 => 0x65, 0x116 => 0x45, 0x117 => 0x65, 0x118 => 0x45, 0x119 => 0x65, 0x11A => 0x45, 0x11B => 0x65, 0x11C => 0x47, 0x11D => 0x67, 0x11E => 0x47, 0x11F => 0x67, 0x120 => 0x47, 0x121 => 0x67, 0x122 => 0x47, 0x123 => 0x67, 0x124 => 0x48, 0x125 => 0x68, 0x126 => 0x48, 0x127 => 0x68, 0x128 => 0x49, 0x129 => 0x69, 0x12A => 0x49, 0x12B => 0x69, 0x12C => 0x49, 0x12D => 0x69, 0x12E => 0x49, 0x12F => 0x69, 0x130 => 0x49, 0x131 => 0x69, 0x132 => 0x49, 0x133 => 0x69, 0x134 => 0x4A, 0x135 => 0x6A, 0x136 => 0x6B, 0x137 => 0x6B, 0x138 => 0x6B, 0x139 => 0x4C, 0x13A => 0x6C, 0x13B => 0x4C, 0x13C => 0x6C, 0x13D => 0x4C, 0x13E => 0x6C, 0x13F => 0x4C, 0x140 => 0x6C, 0x141 => 0x4C, 0x142 => 0x6C, 0x143 => 0x4E, 0x144 => 0x6E, 0x145 => 0x4E, 0x146 => 0x6E, 0x147 => 0x4E, 0x148 => 0x6E, 0x149 => 0x6E, 0x14A => 0x4E, 0x14B => 0x6E, 0x14C => 0x4F, 0x14D => 0x6F, 0x14E => 0x4F, 0x14F => 0x6F, 0x150 => 0x4F, 0x151 => 0x6F, 0x152 => 0x4F, 0x153 => 0x6F, 0x154 => 0x52, 0x155 => 0x72, 0x156 => 0x52, 0x157 => 0x72, 0x158 => 0x52, 0x159 => 0x72, 0x15A => 0x53, 0x15B => 0x73, 0x15C => 0x53, 0x15D => 0x73, 0x15E => 0x53, 0x15F => 0x73, 0x160 => 0x53, 0x161 => 0x73, 0x162 => 0x54, 0x163 => 0x74, 0x164 => 0x54, 0x165 => 0x74, 0x166 => 0x54, 0x167 => 0x74, 0x168 => 0x55, 0x169 => 0x75, 0x16A => 0x55, 0x16B => 0x75, 0x16C => 0x55, 0x16D => 0x75, 0x16E => 0x55, 0x16F => 0x75, 0x170 => 0x55, 0x171 => 0x75, 0x172 => 0x55, 0x173 => 0x75, 0x174 => 0x57, 0x175 => 0x77, 0x176 => 0x59, 0x177 => 0x79, 0x178 => 0x59, 0x179 => 0x5A, 0x17A => 0x7A, 0x17B => 0x5A, 0x17C => 0x7A, 0x17D => 0x5A, 0x17E => 0x7A, 0x17F => 0x73, 0x189 => 0x44, 0x18A => 0x44, 0x18B => 0x44, 0x18C => 0x64, 0x18F => 0x45, 0x192 => 0x66, 0x1A0 => 0x4F, 0x1A1 => 0x6F, 0x1AF => 0x55, 0x1B0 => 0x75, 0x1CD => 0x41, 0x1CE => 0x61, 0x1CF => 0x49, 0x1D0 => 0x69, 0x1D1 => 0x4F, 0x1D2 => 0x6F, 0x1D3 => 0x55, 0x1D4 => 0x75, 0x1D5 => 0x55, 0x1D6 => 0x75, 0x1D7 => 0x55, 0x1D8 => 0x75, 0x1D9 => 0x55, 0x1DA => 0x75, 0x1DB => 0x55, 0x1DC => 0x75, 0x1FA => 0x41, 0x1FB => 0x61, 0x1FC => 0x41, 0x1FD => 0x61, 0x1FE => 0x4F, 0x1FF => 0x6F, 0x218 => 0x53, 0x219 => 0x73, 0x21A => 0x54, 0x21B => 0x74, 0x221 => 0x64, 0x256 => 0x64, 0x257 => 0x64, 0x259 => 0x65, 0x386 => 0x41, 0x388 => 0x45, 0x389 => 0x48, 0x38A => 0x49, 0x38C => 0x4F, 0x38E => 0x59, 0x38F => 0x57, 0x390 => 0x69, 0x391 => 0x41, 0x392 => 0x42, 0x393 => 0x47, 0x394 => 0x44, 0x395 => 0x45, 0x396 => 0x5A, 0x397 => 0x48, 0x398 => 0x4F, 0x399 => 0x49, 0x39A => 0x4B, 0x39B => 0x4C, 0x39C => 0x4D, 0x39D => 0x4E, 0x39E => 0x58, 0x39F => 0x4F, 0x3A0 => 0x50, 0x3A1 => 0x52, 0x3A3 => 0x53, 0x3A4 => 0x54, 0x3A5 => 0x59, 0x3A6 => 0x46, 0x3A7 => 0x58, 0x3A8 => 0x50, 0x3A9 => 0x57, 0x3AA => 0x49, 0x3AB => 0x59, 0x3AC => 0x61, 0x3AD => 0x65, 0x3AE => 0x68, 0x3AF => 0x69, 0x3B0 => 0x79, 0x3B1 => 0x61, 0x3B2 => 0x62, 0x3B3 => 0x67, 0x3B4 => 0x64, 0x3B5 => 0x65, 0x3B6 => 0x7A, 0x3B7 => 0x68, 0x3B8 => 0x6F, 0x3B9 => 0x69, 0x3BA => 0x6B, 0x3BB => 0x6C, 0x3BC => 0x6D, 0x3BD => 0x6E, 0x3BE => 0x78, 0x3BF => 0x6F, 0x3C0 => 0x70, 0x3C1 => 0x72, 0x3C2 => 0x73, 0x3C3 => 0x73, 0x3C4 => 0x74, 0x3C5 => 0x79, 0x3C6 => 0x66, 0x3C7 => 0x78, 0x3C8 => 0x70, 0x3C9 => 0x77, 0x3CA => 0x69, 0x3CB => 0x79, 0x3CC => 0x6F, 0x3CD => 0x79, 0x3CE => 0x77, 0x3D0 => 0x76, 0x3D1 => 0x74, 0x3D2 => 0x49, 0x401 => 0x45, 0x402 => 0x44, 0x404 => 0x45, 0x406 => 0x49, 0x407 => 0x49, 0x408 => 0x6A, 0x409 => 0x4C, 0x40A => 0x4E, 0x40F => 0x44, 0x410 => 0x41, 0x411 => 0x42, 0x412 => 0x56, 0x413 => 0x47, 0x414 => 0x44, 0x415 => 0x45, 0x416 => 0x5A, 0x417 => 0x5A, 0x418 => 0x49, 0x419 => 0x59, 0x41A => 0x4B, 0x41B => 0x4C, 0x41C => 0x4D, 0x41D => 0x4E, 0x41E => 0x4F, 0x41F => 0x50, 0x420 => 0x52, 0x421 => 0x53, 0x422 => 0x54, 0x423 => 0x55, 0x424 => 0x46, 0x425 => 0x4B, 0x426 => 0x54, 0x427 => 0x43, 0x428 => 0x53, 0x429 => 0x53, 0x42A => 0x62, 0x42B => 0x59, 0x42C => 0x62, 0x42D => 0x45, 0x42E => 0x59, 0x42F => 0x59, 0x430 => 0x61, 0x431 => 0x62, 0x432 => 0x76, 0x433 => 0x67, 0x434 => 0x64, 0x435 => 0x65, 0x436 => 0x7A, 0x437 => 0x7A, 0x438 => 0x69, 0x439 => 0x79, 0x43A => 0x6B, 0x43B => 0x6C, 0x43C => 0x6D, 0x43D => 0x6E, 0x43E => 0x6F, 0x43F => 0x70, 0x440 => 0x72, 0x441 => 0x73, 0x442 => 0x74, 0x443 => 0x75, 0x444 => 0x66, 0x445 => 0x6B, 0x446 => 0x74, 0x447 => 0x63, 0x448 => 0x73, 0x449 => 0x73, 0x44B => 0x79, 0x44D => 0x65, 0x44E => 0x79, 0x44F => 0x79, 0x451 => 0x65, 0x452 => 0x64, 0x454 => 0x65, 0x456 => 0x69, 0x457 => 0x69, 0x458 => 0x6A, 0x459 => 0x6C, 0x45A => 0x6E, 0x45F => 0x64, 0x490 => 0x47, 0x491 => 0x67, 0x4E8 => 0x4F, 0x622 => 0x61, 0x623 => 0x61, 0x624 => 0x6F, 0x625 => 0x65, 0x626 => 0x65, 0x627 => 0x61, 0x628 => 0x62, 0x62A => 0x74, 0x62B => 0x74, 0x62C => 0x6A, 0x62D => 0x68, 0x62E => 0x6B, 0x62F => 0x64, 0x630 => 0x74, 0x631 => 0x72, 0x632 => 0x7A, 0x633 => 0x73, 0x634 => 0x73, 0x635 => 0x73, 0x636 => 0x64, 0x637 => 0x74, 0x638 => 0x74, 0x639 => 0x61, 0x63A => 0x67, 0x641 => 0x66, 0x642 => 0x6B, 0x643 => 0x6B, 0x644 => 0x6C, 0x645 => 0x6D, 0x646 => 0x6E, 0x647 => 0x68, 0x648 => 0x6F, 0x64A => 0x79, 0x664 => 0x34, 0x665 => 0x35, 0x666 => 0x36, 0x67E => 0x70, 0x686 => 0x63, 0x698 => 0x7A, 0x6A9 => 0x6B, 0x6AF => 0x67, 0x6CC => 0x69, 0x6F0 => 0x30, 0x6F1 => 0x31, 0x6F2 => 0x32, 0x6F3 => 0x33, 0x6F4 => 0x34, 0x6F5 => 0x35, 0x6F6 => 0x36, 0x6F7 => 0x37, 0x6F8 => 0x38, 0x6F9 => 0x39, 0x905 => 0x61, 0x906 => 0x61, 0x907 => 0x69, 0x908 => 0x69, 0x909 => 0x75, 0x90A => 0x75, 0x90D => 0x65, 0x90F => 0x65, 0x910 => 0x61, 0x911 => 0x6F, 0x912 => 0x6F, 0x913 => 0x6F, 0x92C => 0x42, 0x932 => 0x4C, 0x1000 => 0x6B, 0x1002 => 0x67, 0x1005 => 0x73, 0x1007 => 0x7A, 0x1009 => 0x75, 0x100A => 0x69, 0x100B => 0x74, 0x100D => 0x64, 0x1010 => 0x74, 0x1012 => 0x64, 0x1014 => 0x6E, 0x1015 => 0x70, 0x1017 => 0x62, 0x1019 => 0x6D, 0x101A => 0x79, 0x101C => 0x6C, 0x101D => 0x77, 0x101F => 0x68, 0x1021 => 0x61, 0x1023 => 0x69, 0x1027 => 0x65, 0x102B => 0x61, 0x102C => 0x61, 0x102D => 0x6F, 0x102E => 0x69, 0x102F => 0x75, 0x1030 => 0x75, 0x1031 => 0x65, 0x1032 => 0x65, 0x103D => 0x77, 0x103E => 0x68, 0x10D0 => 0x61, 0x10D1 => 0x62, 0x10D2 => 0x67, 0x10D3 => 0x64, 0x10D4 => 0x65, 0x10D5 => 0x76, 0x10D6 => 0x7A, 0x10D7 => 0x74, 0x10D8 => 0x69, 0x10D9 => 0x6B, 0x10DA => 0x6C, 0x10DB => 0x6D, 0x10DC => 0x6E, 0x10DD => 0x6F, 0x10DE => 0x70, 0x10DF => 0x7A, 0x10E0 => 0x72, 0x10E1 => 0x73, 0x10E2 => 0x74, 0x10E3 => 0x75, 0x10E4 => 0x66, 0x10E5 => 0x6B, 0x10E6 => 0x67, 0x10E7 => 0x71, 0x10E8 => 0x73, 0x10E9 => 0x63, 0x10EA => 0x74, 0x10EB => 0x64, 0x10EC => 0x74, 0x10ED => 0x63, 0x10EE => 0x6B, 0x10EF => 0x6A, 0x10F0 => 0x68, 0x1D05 => 0x44, 0x1D06 => 0x44, 0x1D6D => 0x64, 0x1D81 => 0x64, 0x1D91 => 0x64, 0x1E9E => 0x53, 0x1EA0 => 0x41, 0x1EA1 => 0x61, 0x1EA2 => 0x41, 0x1EA3 => 0x61, 0x1EA4 => 0x41, 0x1EA5 => 0x61, 0x1EA6 => 0x41, 0x1EA7 => 0x61, 0x1EA8 => 0x41, 0x1EA9 => 0x61, 0x1EAA => 0x41, 0x1EAB => 0x61, 0x1EAC => 0x41, 0x1EAD => 0x61, 0x1EAE => 0x41, 0x1EAF => 0x61, 0x1EB0 => 0x41, 0x1EB1 => 0x61, 0x1EB2 => 0x41, 0x1EB3 => 0x61, 0x1EB4 => 0x41, 0x1EB5 => 0x61, 0x1EB6 => 0x41, 0x1EB7 => 0x61, 0x1EB8 => 0x45, 0x1EB9 => 0x65, 0x1EBA => 0x45, 0x1EBB => 0x65, 0x1EBC => 0x45, 0x1EBD => 0x65, 0x1EBE => 0x45, 0x1EBF => 0x65, 0x1EC0 => 0x45, 0x1EC1 => 0x65, 0x1EC2 => 0x45, 0x1EC3 => 0x65, 0x1EC4 => 0x45, 0x1EC5 => 0x65, 0x1EC6 => 0x45, 0x1EC7 => 0x65, 0x1EC8 => 0x49, 0x1EC9 => 0x69, 0x1ECA => 0x49, 0x1ECB => 0x69, 0x1ECC => 0x4F, 0x1ECD => 0x6F, 0x1ECE => 0x4F, 0x1ECF => 0x6F, 0x1ED0 => 0x4F, 0x1ED1 => 0x6F, 0x1ED2 => 0x4F, 0x1ED3 => 0x6F, 0x1ED4 => 0x4F, 0x1ED5 => 0x6F, 0x1ED6 => 0x4F, 0x1ED7 => 0x6F, 0x1ED8 => 0x4F, 0x1ED9 => 0x6F, 0x1EDA => 0x4F, 0x1EDB => 0x6F, 0x1EDC => 0x4F, 0x1EDD => 0x6F, 0x1EDE => 0x4F, 0x1EDF => 0x6F, 0x1EE0 => 0x4F, 0x1EE1 => 0x6F, 0x1EE2 => 0x4F, 0x1EE3 => 0x6F, 0x1EE4 => 0x55, 0x1EE5 => 0x75, 0x1EE6 => 0x55, 0x1EE7 => 0x75, 0x1EE8 => 0x55, 0x1EE9 => 0x75, 0x1EEA => 0x55, 0x1EEB => 0x75, 0x1EEC => 0x55, 0x1EED => 0x75, 0x1EEE => 0x55, 0x1EEF => 0x75, 0x1EF0 => 0x55, 0x1EF1 => 0x75, 0x1EF2 => 0x59, 0x1EF3 => 0x79, 0x1EF4 => 0x59, 0x1EF5 => 0x79, 0x1EF6 => 0x59, 0x1EF7 => 0x79, 0x1EF8 => 0x59, 0x1EF9 => 0x79, 0x1F00 => 0x61, 0x1F01 => 0x61, 0x1F02 => 0x61, 0x1F03 => 0x61, 0x1F04 => 0x61, 0x1F05 => 0x61, 0x1F06 => 0x61, 0x1F07 => 0x61, 0x1F08 => 0x41, 0x1F09 => 0x41, 0x1F0A => 0x41, 0x1F0B => 0x41, 0x1F0C => 0x41, 0x1F0D => 0x41, 0x1F0E => 0x41, 0x1F0F => 0x41, 0x1F10 => 0x65, 0x1F11 => 0x65, 0x1F12 => 0x65, 0x1F13 => 0x65, 0x1F14 => 0x65, 0x1F15 => 0x65, 0x1F18 => 0x45, 0x1F19 => 0x45, 0x1F1A => 0x45, 0x1F1B => 0x45, 0x1F1C => 0x45, 0x1F1D => 0x45, 0x1F30 => 0x69, 0x1F31 => 0x69, 0x1F32 => 0x69, 0x1F33 => 0x69, 0x1F34 => 0x69, 0x1F35 => 0x69, 0x1F36 => 0x69, 0x1F37 => 0x69, 0x1F38 => 0x49, 0x1F39 => 0x49, 0x1F3B => 0x49, 0x1F3C => 0x49, 0x1F3D => 0x49, 0x1F3E => 0x49, 0x1F3F => 0x49, 0x1F40 => 0x6F, 0x1F41 => 0x6F, 0x1F42 => 0x6F, 0x1F43 => 0x6F, 0x1F44 => 0x6F, 0x1F45 => 0x6F, 0x1F48 => 0x4F, 0x1F49 => 0x4F, 0x1F4A => 0x4F, 0x1F4B => 0x4F, 0x1F4C => 0x4F, 0x1F4D => 0x4F, 0x1F70 => 0x61, 0x1F72 => 0x65, 0x1F76 => 0x69, 0x1F78 => 0x6F, 0x1F80 => 0x61, 0x1F81 => 0x61, 0x1F82 => 0x61, 0x1F83 => 0x61, 0x1F84 => 0x61, 0x1F85 => 0x61, 0x1F86 => 0x61, 0x1F87 => 0x61, 0x1F88 => 0x41, 0x1F89 => 0x41, 0x1F8A => 0x41, 0x1F8B => 0x41, 0x1F8C => 0x41, 0x1F8D => 0x41, 0x1F8E => 0x41, 0x1F8F => 0x41, 0x1FB0 => 0x61, 0x1FB1 => 0x61, 0x1FB2 => 0x61, 0x1FB3 => 0x61, 0x1FB4 => 0x61, 0x1FB6 => 0x61, 0x1FB7 => 0x61, 0x1FB8 => 0x41, 0x1FB9 => 0x41, 0x1FBA => 0x41, 0x1FBC => 0x41, 0x1FC8 => 0x45, 0x1FD0 => 0x69, 0x1FD1 => 0x69, 0x1FD2 => 0x69, 0x1FD6 => 0x69, 0x1FD7 => 0x69, 0x1FD8 => 0x49, 0x1FD9 => 0x49, 0x1FDA => 0x49, 0x1FE8 => 0x59, 0x1FE9 => 0x59, 0x1FEA => 0x59, 0x1FF8 => 0x4F, 0x2000 => 0x20, 0x2001 => 0x20, 0x2002 => 0x20, 0x2003 => 0x20, 0x2004 => 0x20, 0x2005 => 0x20, 0x2006 => 0x20, 0x2007 => 0x20, 0x2008 => 0x20, 0x2009 => 0x20, 0x200A => 0x20, 0x202F => 0x20, 0x205F => 0x20, 0x2074 => 0x34, 0x2075 => 0x35, 0x2076 => 0x36, 0x2077 => 0x37, 0x2078 => 0x38, 0x2079 => 0x39, 0x2080 => 0x30, 0x2081 => 0x31, 0x2082 => 0x32, 0x2083 => 0x33, 0x2084 => 0x34, 0x2085 => 0x35, 0x2086 => 0x36, 0x2087 => 0x37, 0x2088 => 0x38, 0x2089 => 0x39, 0x3000 => 0x20, ]; string/res/fold.php 0000777 00000060357 15252016106 0010320 0 ustar 00 <?php return [ 0x41 => 0x61, 0x42 => 0x62, 0x43 => 0x63, 0x44 => 0x64, 0x45 => 0x65, 0x46 => 0x66, 0x47 => 0x67, 0x48 => 0x68, 0x49 => 0x69, 0x4A => 0x6A, 0x4B => 0x6B, 0x4C => 0x6C, 0x4D => 0x6D, 0x4E => 0x6E, 0x4F => 0x6F, 0x50 => 0x70, 0x51 => 0x71, 0x52 => 0x72, 0x53 => 0x73, 0x54 => 0x74, 0x55 => 0x75, 0x56 => 0x76, 0x57 => 0x77, 0x58 => 0x78, 0x59 => 0x79, 0x5A => 0x7A, 0xB5 => 0x3BC, 0xC0 => 0xE0, 0xC1 => 0xE1, 0xC2 => 0xE2, 0xC3 => 0xE3, 0xC4 => 0xE4, 0xC5 => 0xE5, 0xC6 => 0xE6, 0xC7 => 0xE7, 0xC8 => 0xE8, 0xC9 => 0xE9, 0xCA => 0xEA, 0xCB => 0xEB, 0xCC => 0xEC, 0xCD => 0xED, 0xCE => 0xEE, 0xCF => 0xEF, 0xD0 => 0xF0, 0xD1 => 0xF1, 0xD2 => 0xF2, 0xD3 => 0xF3, 0xD4 => 0xF4, 0xD5 => 0xF5, 0xD6 => 0xF6, 0xD8 => 0xF8, 0xD9 => 0xF9, 0xDA => 0xFA, 0xDB => 0xFB, 0xDC => 0xFC, 0xDD => 0xFD, 0xDE => 0xFE, 0x100 => 0x101, 0x102 => 0x103, 0x104 => 0x105, 0x106 => 0x107, 0x108 => 0x109, 0x10A => 0x10B, 0x10C => 0x10D, 0x10E => 0x10F, 0x110 => 0x111, 0x112 => 0x113, 0x114 => 0x115, 0x116 => 0x117, 0x118 => 0x119, 0x11A => 0x11B, 0x11C => 0x11D, 0x11E => 0x11F, 0x120 => 0x121, 0x122 => 0x123, 0x124 => 0x125, 0x126 => 0x127, 0x128 => 0x129, 0x12A => 0x12B, 0x12C => 0x12D, 0x12E => 0x12F, 0x132 => 0x133, 0x134 => 0x135, 0x136 => 0x137, 0x139 => 0x13A, 0x13B => 0x13C, 0x13D => 0x13E, 0x13F => 0x140, 0x141 => 0x142, 0x143 => 0x144, 0x145 => 0x146, 0x147 => 0x148, 0x14A => 0x14B, 0x14C => 0x14D, 0x14E => 0x14F, 0x150 => 0x151, 0x152 => 0x153, 0x154 => 0x155, 0x156 => 0x157, 0x158 => 0x159, 0x15A => 0x15B, 0x15C => 0x15D, 0x15E => 0x15F, 0x160 => 0x161, 0x162 => 0x163, 0x164 => 0x165, 0x166 => 0x167, 0x168 => 0x169, 0x16A => 0x16B, 0x16C => 0x16D, 0x16E => 0x16F, 0x170 => 0x171, 0x172 => 0x173, 0x174 => 0x175, 0x176 => 0x177, 0x178 => 0xFF, 0x179 => 0x17A, 0x17B => 0x17C, 0x17D => 0x17E, 0x17F => 0x73, 0x181 => 0x253, 0x182 => 0x183, 0x184 => 0x185, 0x186 => 0x254, 0x187 => 0x188, 0x189 => 0x256, 0x18A => 0x257, 0x18B => 0x18C, 0x18E => 0x1DD, 0x18F => 0x259, 0x190 => 0x25B, 0x191 => 0x192, 0x193 => 0x260, 0x194 => 0x263, 0x196 => 0x269, 0x197 => 0x268, 0x198 => 0x199, 0x19C => 0x26F, 0x19D => 0x272, 0x19F => 0x275, 0x1A0 => 0x1A1, 0x1A2 => 0x1A3, 0x1A4 => 0x1A5, 0x1A6 => 0x280, 0x1A7 => 0x1A8, 0x1A9 => 0x283, 0x1AC => 0x1AD, 0x1AE => 0x288, 0x1AF => 0x1B0, 0x1B1 => 0x28A, 0x1B2 => 0x28B, 0x1B3 => 0x1B4, 0x1B5 => 0x1B6, 0x1B7 => 0x292, 0x1B8 => 0x1B9, 0x1BC => 0x1BD, 0x1C4 => 0x1C6, 0x1C5 => 0x1C6, 0x1C7 => 0x1C9, 0x1C8 => 0x1C9, 0x1CA => 0x1CC, 0x1CB => 0x1CC, 0x1CD => 0x1CE, 0x1CF => 0x1D0, 0x1D1 => 0x1D2, 0x1D3 => 0x1D4, 0x1D5 => 0x1D6, 0x1D7 => 0x1D8, 0x1D9 => 0x1DA, 0x1DB => 0x1DC, 0x1DE => 0x1DF, 0x1E0 => 0x1E1, 0x1E2 => 0x1E3, 0x1E4 => 0x1E5, 0x1E6 => 0x1E7, 0x1E8 => 0x1E9, 0x1EA => 0x1EB, 0x1EC => 0x1ED, 0x1EE => 0x1EF, 0x1F1 => 0x1F3, 0x1F2 => 0x1F3, 0x1F4 => 0x1F5, 0x1F6 => 0x195, 0x1F7 => 0x1BF, 0x1F8 => 0x1F9, 0x1FA => 0x1FB, 0x1FC => 0x1FD, 0x1FE => 0x1FF, 0x200 => 0x201, 0x202 => 0x203, 0x204 => 0x205, 0x206 => 0x207, 0x208 => 0x209, 0x20A => 0x20B, 0x20C => 0x20D, 0x20E => 0x20F, 0x210 => 0x211, 0x212 => 0x213, 0x214 => 0x215, 0x216 => 0x217, 0x218 => 0x219, 0x21A => 0x21B, 0x21C => 0x21D, 0x21E => 0x21F, 0x220 => 0x19E, 0x222 => 0x223, 0x224 => 0x225, 0x226 => 0x227, 0x228 => 0x229, 0x22A => 0x22B, 0x22C => 0x22D, 0x22E => 0x22F, 0x230 => 0x231, 0x232 => 0x233, 0x23A => 0x2C65, 0x23B => 0x23C, 0x23D => 0x19A, 0x23E => 0x2C66, 0x241 => 0x242, 0x243 => 0x180, 0x244 => 0x289, 0x245 => 0x28C, 0x246 => 0x247, 0x248 => 0x249, 0x24A => 0x24B, 0x24C => 0x24D, 0x24E => 0x24F, 0x345 => 0x3B9, 0x370 => 0x371, 0x372 => 0x373, 0x376 => 0x377, 0x37F => 0x3F3, 0x386 => 0x3AC, 0x388 => 0x3AD, 0x389 => 0x3AE, 0x38A => 0x3AF, 0x38C => 0x3CC, 0x38E => 0x3CD, 0x38F => 0x3CE, 0x391 => 0x3B1, 0x392 => 0x3B2, 0x393 => 0x3B3, 0x394 => 0x3B4, 0x395 => 0x3B5, 0x396 => 0x3B6, 0x397 => 0x3B7, 0x398 => 0x3B8, 0x399 => 0x3B9, 0x39A => 0x3BA, 0x39B => 0x3BB, 0x39C => 0x3BC, 0x39D => 0x3BD, 0x39E => 0x3BE, 0x39F => 0x3BF, 0x3A0 => 0x3C0, 0x3A1 => 0x3C1, 0x3A3 => 0x3C3, 0x3A4 => 0x3C4, 0x3A5 => 0x3C5, 0x3A6 => 0x3C6, 0x3A7 => 0x3C7, 0x3A8 => 0x3C8, 0x3A9 => 0x3C9, 0x3AA => 0x3CA, 0x3AB => 0x3CB, 0x3C2 => 0x3C3, 0x3CF => 0x3D7, 0x3D0 => 0x3B2, 0x3D1 => 0x3B8, 0x3D5 => 0x3C6, 0x3D6 => 0x3C0, 0x3D8 => 0x3D9, 0x3DA => 0x3DB, 0x3DC => 0x3DD, 0x3DE => 0x3DF, 0x3E0 => 0x3E1, 0x3E2 => 0x3E3, 0x3E4 => 0x3E5, 0x3E6 => 0x3E7, 0x3E8 => 0x3E9, 0x3EA => 0x3EB, 0x3EC => 0x3ED, 0x3EE => 0x3EF, 0x3F0 => 0x3BA, 0x3F1 => 0x3C1, 0x3F4 => 0x3B8, 0x3F5 => 0x3B5, 0x3F7 => 0x3F8, 0x3F9 => 0x3F2, 0x3FA => 0x3FB, 0x3FD => 0x37B, 0x3FE => 0x37C, 0x3FF => 0x37D, 0x400 => 0x450, 0x401 => 0x451, 0x402 => 0x452, 0x403 => 0x453, 0x404 => 0x454, 0x405 => 0x455, 0x406 => 0x456, 0x407 => 0x457, 0x408 => 0x458, 0x409 => 0x459, 0x40A => 0x45A, 0x40B => 0x45B, 0x40C => 0x45C, 0x40D => 0x45D, 0x40E => 0x45E, 0x40F => 0x45F, 0x410 => 0x430, 0x411 => 0x431, 0x412 => 0x432, 0x413 => 0x433, 0x414 => 0x434, 0x415 => 0x435, 0x416 => 0x436, 0x417 => 0x437, 0x418 => 0x438, 0x419 => 0x439, 0x41A => 0x43A, 0x41B => 0x43B, 0x41C => 0x43C, 0x41D => 0x43D, 0x41E => 0x43E, 0x41F => 0x43F, 0x420 => 0x440, 0x421 => 0x441, 0x422 => 0x442, 0x423 => 0x443, 0x424 => 0x444, 0x425 => 0x445, 0x426 => 0x446, 0x427 => 0x447, 0x428 => 0x448, 0x429 => 0x449, 0x42A => 0x44A, 0x42B => 0x44B, 0x42C => 0x44C, 0x42D => 0x44D, 0x42E => 0x44E, 0x42F => 0x44F, 0x460 => 0x461, 0x462 => 0x463, 0x464 => 0x465, 0x466 => 0x467, 0x468 => 0x469, 0x46A => 0x46B, 0x46C => 0x46D, 0x46E => 0x46F, 0x470 => 0x471, 0x472 => 0x473, 0x474 => 0x475, 0x476 => 0x477, 0x478 => 0x479, 0x47A => 0x47B, 0x47C => 0x47D, 0x47E => 0x47F, 0x480 => 0x481, 0x48A => 0x48B, 0x48C => 0x48D, 0x48E => 0x48F, 0x490 => 0x491, 0x492 => 0x493, 0x494 => 0x495, 0x496 => 0x497, 0x498 => 0x499, 0x49A => 0x49B, 0x49C => 0x49D, 0x49E => 0x49F, 0x4A0 => 0x4A1, 0x4A2 => 0x4A3, 0x4A4 => 0x4A5, 0x4A6 => 0x4A7, 0x4A8 => 0x4A9, 0x4AA => 0x4AB, 0x4AC => 0x4AD, 0x4AE => 0x4AF, 0x4B0 => 0x4B1, 0x4B2 => 0x4B3, 0x4B4 => 0x4B5, 0x4B6 => 0x4B7, 0x4B8 => 0x4B9, 0x4BA => 0x4BB, 0x4BC => 0x4BD, 0x4BE => 0x4BF, 0x4C0 => 0x4CF, 0x4C1 => 0x4C2, 0x4C3 => 0x4C4, 0x4C5 => 0x4C6, 0x4C7 => 0x4C8, 0x4C9 => 0x4CA, 0x4CB => 0x4CC, 0x4CD => 0x4CE, 0x4D0 => 0x4D1, 0x4D2 => 0x4D3, 0x4D4 => 0x4D5, 0x4D6 => 0x4D7, 0x4D8 => 0x4D9, 0x4DA => 0x4DB, 0x4DC => 0x4DD, 0x4DE => 0x4DF, 0x4E0 => 0x4E1, 0x4E2 => 0x4E3, 0x4E4 => 0x4E5, 0x4E6 => 0x4E7, 0x4E8 => 0x4E9, 0x4EA => 0x4EB, 0x4EC => 0x4ED, 0x4EE => 0x4EF, 0x4F0 => 0x4F1, 0x4F2 => 0x4F3, 0x4F4 => 0x4F5, 0x4F6 => 0x4F7, 0x4F8 => 0x4F9, 0x4FA => 0x4FB, 0x4FC => 0x4FD, 0x4FE => 0x4FF, 0x500 => 0x501, 0x502 => 0x503, 0x504 => 0x505, 0x506 => 0x507, 0x508 => 0x509, 0x50A => 0x50B, 0x50C => 0x50D, 0x50E => 0x50F, 0x510 => 0x511, 0x512 => 0x513, 0x514 => 0x515, 0x516 => 0x517, 0x518 => 0x519, 0x51A => 0x51B, 0x51C => 0x51D, 0x51E => 0x51F, 0x520 => 0x521, 0x522 => 0x523, 0x524 => 0x525, 0x526 => 0x527, 0x528 => 0x529, 0x52A => 0x52B, 0x52C => 0x52D, 0x52E => 0x52F, 0x531 => 0x561, 0x532 => 0x562, 0x533 => 0x563, 0x534 => 0x564, 0x535 => 0x565, 0x536 => 0x566, 0x537 => 0x567, 0x538 => 0x568, 0x539 => 0x569, 0x53A => 0x56A, 0x53B => 0x56B, 0x53C => 0x56C, 0x53D => 0x56D, 0x53E => 0x56E, 0x53F => 0x56F, 0x540 => 0x570, 0x541 => 0x571, 0x542 => 0x572, 0x543 => 0x573, 0x544 => 0x574, 0x545 => 0x575, 0x546 => 0x576, 0x547 => 0x577, 0x548 => 0x578, 0x549 => 0x579, 0x54A => 0x57A, 0x54B => 0x57B, 0x54C => 0x57C, 0x54D => 0x57D, 0x54E => 0x57E, 0x54F => 0x57F, 0x550 => 0x580, 0x551 => 0x581, 0x552 => 0x582, 0x553 => 0x583, 0x554 => 0x584, 0x555 => 0x585, 0x556 => 0x586, 0x10A0 => 0x2D00, 0x10A1 => 0x2D01, 0x10A2 => 0x2D02, 0x10A3 => 0x2D03, 0x10A4 => 0x2D04, 0x10A5 => 0x2D05, 0x10A6 => 0x2D06, 0x10A7 => 0x2D07, 0x10A8 => 0x2D08, 0x10A9 => 0x2D09, 0x10AA => 0x2D0A, 0x10AB => 0x2D0B, 0x10AC => 0x2D0C, 0x10AD => 0x2D0D, 0x10AE => 0x2D0E, 0x10AF => 0x2D0F, 0x10B0 => 0x2D10, 0x10B1 => 0x2D11, 0x10B2 => 0x2D12, 0x10B3 => 0x2D13, 0x10B4 => 0x2D14, 0x10B5 => 0x2D15, 0x10B6 => 0x2D16, 0x10B7 => 0x2D17, 0x10B8 => 0x2D18, 0x10B9 => 0x2D19, 0x10BA => 0x2D1A, 0x10BB => 0x2D1B, 0x10BC => 0x2D1C, 0x10BD => 0x2D1D, 0x10BE => 0x2D1E, 0x10BF => 0x2D1F, 0x10C0 => 0x2D20, 0x10C1 => 0x2D21, 0x10C2 => 0x2D22, 0x10C3 => 0x2D23, 0x10C4 => 0x2D24, 0x10C5 => 0x2D25, 0x10C7 => 0x2D27, 0x10CD => 0x2D2D, 0x13F8 => 0x13F0, 0x13F9 => 0x13F1, 0x13FA => 0x13F2, 0x13FB => 0x13F3, 0x13FC => 0x13F4, 0x13FD => 0x13F5, 0x1C80 => 0x432, 0x1C81 => 0x434, 0x1C82 => 0x43E, 0x1C83 => 0x441, 0x1C84 => 0x442, 0x1C85 => 0x442, 0x1C86 => 0x44A, 0x1C87 => 0x463, 0x1C88 => 0xA64B, 0x1C90 => 0x10D0, 0x1C91 => 0x10D1, 0x1C92 => 0x10D2, 0x1C93 => 0x10D3, 0x1C94 => 0x10D4, 0x1C95 => 0x10D5, 0x1C96 => 0x10D6, 0x1C97 => 0x10D7, 0x1C98 => 0x10D8, 0x1C99 => 0x10D9, 0x1C9A => 0x10DA, 0x1C9B => 0x10DB, 0x1C9C => 0x10DC, 0x1C9D => 0x10DD, 0x1C9E => 0x10DE, 0x1C9F => 0x10DF, 0x1CA0 => 0x10E0, 0x1CA1 => 0x10E1, 0x1CA2 => 0x10E2, 0x1CA3 => 0x10E3, 0x1CA4 => 0x10E4, 0x1CA5 => 0x10E5, 0x1CA6 => 0x10E6, 0x1CA7 => 0x10E7, 0x1CA8 => 0x10E8, 0x1CA9 => 0x10E9, 0x1CAA => 0x10EA, 0x1CAB => 0x10EB, 0x1CAC => 0x10EC, 0x1CAD => 0x10ED, 0x1CAE => 0x10EE, 0x1CAF => 0x10EF, 0x1CB0 => 0x10F0, 0x1CB1 => 0x10F1, 0x1CB2 => 0x10F2, 0x1CB3 => 0x10F3, 0x1CB4 => 0x10F4, 0x1CB5 => 0x10F5, 0x1CB6 => 0x10F6, 0x1CB7 => 0x10F7, 0x1CB8 => 0x10F8, 0x1CB9 => 0x10F9, 0x1CBA => 0x10FA, 0x1CBD => 0x10FD, 0x1CBE => 0x10FE, 0x1CBF => 0x10FF, 0x1E00 => 0x1E01, 0x1E02 => 0x1E03, 0x1E04 => 0x1E05, 0x1E06 => 0x1E07, 0x1E08 => 0x1E09, 0x1E0A => 0x1E0B, 0x1E0C => 0x1E0D, 0x1E0E => 0x1E0F, 0x1E10 => 0x1E11, 0x1E12 => 0x1E13, 0x1E14 => 0x1E15, 0x1E16 => 0x1E17, 0x1E18 => 0x1E19, 0x1E1A => 0x1E1B, 0x1E1C => 0x1E1D, 0x1E1E => 0x1E1F, 0x1E20 => 0x1E21, 0x1E22 => 0x1E23, 0x1E24 => 0x1E25, 0x1E26 => 0x1E27, 0x1E28 => 0x1E29, 0x1E2A => 0x1E2B, 0x1E2C => 0x1E2D, 0x1E2E => 0x1E2F, 0x1E30 => 0x1E31, 0x1E32 => 0x1E33, 0x1E34 => 0x1E35, 0x1E36 => 0x1E37, 0x1E38 => 0x1E39, 0x1E3A => 0x1E3B, 0x1E3C => 0x1E3D, 0x1E3E => 0x1E3F, 0x1E40 => 0x1E41, 0x1E42 => 0x1E43, 0x1E44 => 0x1E45, 0x1E46 => 0x1E47, 0x1E48 => 0x1E49, 0x1E4A => 0x1E4B, 0x1E4C => 0x1E4D, 0x1E4E => 0x1E4F, 0x1E50 => 0x1E51, 0x1E52 => 0x1E53, 0x1E54 => 0x1E55, 0x1E56 => 0x1E57, 0x1E58 => 0x1E59, 0x1E5A => 0x1E5B, 0x1E5C => 0x1E5D, 0x1E5E => 0x1E5F, 0x1E60 => 0x1E61, 0x1E62 => 0x1E63, 0x1E64 => 0x1E65, 0x1E66 => 0x1E67, 0x1E68 => 0x1E69, 0x1E6A => 0x1E6B, 0x1E6C => 0x1E6D, 0x1E6E => 0x1E6F, 0x1E70 => 0x1E71, 0x1E72 => 0x1E73, 0x1E74 => 0x1E75, 0x1E76 => 0x1E77, 0x1E78 => 0x1E79, 0x1E7A => 0x1E7B, 0x1E7C => 0x1E7D, 0x1E7E => 0x1E7F, 0x1E80 => 0x1E81, 0x1E82 => 0x1E83, 0x1E84 => 0x1E85, 0x1E86 => 0x1E87, 0x1E88 => 0x1E89, 0x1E8A => 0x1E8B, 0x1E8C => 0x1E8D, 0x1E8E => 0x1E8F, 0x1E90 => 0x1E91, 0x1E92 => 0x1E93, 0x1E94 => 0x1E95, 0x1E9B => 0x1E61, 0x1E9E => 0xDF, 0x1EA0 => 0x1EA1, 0x1EA2 => 0x1EA3, 0x1EA4 => 0x1EA5, 0x1EA6 => 0x1EA7, 0x1EA8 => 0x1EA9, 0x1EAA => 0x1EAB, 0x1EAC => 0x1EAD, 0x1EAE => 0x1EAF, 0x1EB0 => 0x1EB1, 0x1EB2 => 0x1EB3, 0x1EB4 => 0x1EB5, 0x1EB6 => 0x1EB7, 0x1EB8 => 0x1EB9, 0x1EBA => 0x1EBB, 0x1EBC => 0x1EBD, 0x1EBE => 0x1EBF, 0x1EC0 => 0x1EC1, 0x1EC2 => 0x1EC3, 0x1EC4 => 0x1EC5, 0x1EC6 => 0x1EC7, 0x1EC8 => 0x1EC9, 0x1ECA => 0x1ECB, 0x1ECC => 0x1ECD, 0x1ECE => 0x1ECF, 0x1ED0 => 0x1ED1, 0x1ED2 => 0x1ED3, 0x1ED4 => 0x1ED5, 0x1ED6 => 0x1ED7, 0x1ED8 => 0x1ED9, 0x1EDA => 0x1EDB, 0x1EDC => 0x1EDD, 0x1EDE => 0x1EDF, 0x1EE0 => 0x1EE1, 0x1EE2 => 0x1EE3, 0x1EE4 => 0x1EE5, 0x1EE6 => 0x1EE7, 0x1EE8 => 0x1EE9, 0x1EEA => 0x1EEB, 0x1EEC => 0x1EED, 0x1EEE => 0x1EEF, 0x1EF0 => 0x1EF1, 0x1EF2 => 0x1EF3, 0x1EF4 => 0x1EF5, 0x1EF6 => 0x1EF7, 0x1EF8 => 0x1EF9, 0x1EFA => 0x1EFB, 0x1EFC => 0x1EFD, 0x1EFE => 0x1EFF, 0x1F08 => 0x1F00, 0x1F09 => 0x1F01, 0x1F0A => 0x1F02, 0x1F0B => 0x1F03, 0x1F0C => 0x1F04, 0x1F0D => 0x1F05, 0x1F0E => 0x1F06, 0x1F0F => 0x1F07, 0x1F18 => 0x1F10, 0x1F19 => 0x1F11, 0x1F1A => 0x1F12, 0x1F1B => 0x1F13, 0x1F1C => 0x1F14, 0x1F1D => 0x1F15, 0x1F28 => 0x1F20, 0x1F29 => 0x1F21, 0x1F2A => 0x1F22, 0x1F2B => 0x1F23, 0x1F2C => 0x1F24, 0x1F2D => 0x1F25, 0x1F2E => 0x1F26, 0x1F2F => 0x1F27, 0x1F38 => 0x1F30, 0x1F39 => 0x1F31, 0x1F3A => 0x1F32, 0x1F3B => 0x1F33, 0x1F3C => 0x1F34, 0x1F3D => 0x1F35, 0x1F3E => 0x1F36, 0x1F3F => 0x1F37, 0x1F48 => 0x1F40, 0x1F49 => 0x1F41, 0x1F4A => 0x1F42, 0x1F4B => 0x1F43, 0x1F4C => 0x1F44, 0x1F4D => 0x1F45, 0x1F59 => 0x1F51, 0x1F5B => 0x1F53, 0x1F5D => 0x1F55, 0x1F5F => 0x1F57, 0x1F68 => 0x1F60, 0x1F69 => 0x1F61, 0x1F6A => 0x1F62, 0x1F6B => 0x1F63, 0x1F6C => 0x1F64, 0x1F6D => 0x1F65, 0x1F6E => 0x1F66, 0x1F6F => 0x1F67, 0x1F88 => 0x1F80, 0x1F89 => 0x1F81, 0x1F8A => 0x1F82, 0x1F8B => 0x1F83, 0x1F8C => 0x1F84, 0x1F8D => 0x1F85, 0x1F8E => 0x1F86, 0x1F8F => 0x1F87, 0x1F98 => 0x1F90, 0x1F99 => 0x1F91, 0x1F9A => 0x1F92, 0x1F9B => 0x1F93, 0x1F9C => 0x1F94, 0x1F9D => 0x1F95, 0x1F9E => 0x1F96, 0x1F9F => 0x1F97, 0x1FA8 => 0x1FA0, 0x1FA9 => 0x1FA1, 0x1FAA => 0x1FA2, 0x1FAB => 0x1FA3, 0x1FAC => 0x1FA4, 0x1FAD => 0x1FA5, 0x1FAE => 0x1FA6, 0x1FAF => 0x1FA7, 0x1FB8 => 0x1FB0, 0x1FB9 => 0x1FB1, 0x1FBA => 0x1F70, 0x1FBB => 0x1F71, 0x1FBC => 0x1FB3, 0x1FBE => 0x3B9, 0x1FC8 => 0x1F72, 0x1FC9 => 0x1F73, 0x1FCA => 0x1F74, 0x1FCB => 0x1F75, 0x1FCC => 0x1FC3, 0x1FD8 => 0x1FD0, 0x1FD9 => 0x1FD1, 0x1FDA => 0x1F76, 0x1FDB => 0x1F77, 0x1FE8 => 0x1FE0, 0x1FE9 => 0x1FE1, 0x1FEA => 0x1F7A, 0x1FEB => 0x1F7B, 0x1FEC => 0x1FE5, 0x1FF8 => 0x1F78, 0x1FF9 => 0x1F79, 0x1FFA => 0x1F7C, 0x1FFB => 0x1F7D, 0x1FFC => 0x1FF3, 0x2126 => 0x3C9, 0x212A => 0x6B, 0x212B => 0xE5, 0x2132 => 0x214E, 0x2160 => 0x2170, 0x2161 => 0x2171, 0x2162 => 0x2172, 0x2163 => 0x2173, 0x2164 => 0x2174, 0x2165 => 0x2175, 0x2166 => 0x2176, 0x2167 => 0x2177, 0x2168 => 0x2178, 0x2169 => 0x2179, 0x216A => 0x217A, 0x216B => 0x217B, 0x216C => 0x217C, 0x216D => 0x217D, 0x216E => 0x217E, 0x216F => 0x217F, 0x2183 => 0x2184, 0x24B6 => 0x24D0, 0x24B7 => 0x24D1, 0x24B8 => 0x24D2, 0x24B9 => 0x24D3, 0x24BA => 0x24D4, 0x24BB => 0x24D5, 0x24BC => 0x24D6, 0x24BD => 0x24D7, 0x24BE => 0x24D8, 0x24BF => 0x24D9, 0x24C0 => 0x24DA, 0x24C1 => 0x24DB, 0x24C2 => 0x24DC, 0x24C3 => 0x24DD, 0x24C4 => 0x24DE, 0x24C5 => 0x24DF, 0x24C6 => 0x24E0, 0x24C7 => 0x24E1, 0x24C8 => 0x24E2, 0x24C9 => 0x24E3, 0x24CA => 0x24E4, 0x24CB => 0x24E5, 0x24CC => 0x24E6, 0x24CD => 0x24E7, 0x24CE => 0x24E8, 0x24CF => 0x24E9, 0x2C00 => 0x2C30, 0x2C01 => 0x2C31, 0x2C02 => 0x2C32, 0x2C03 => 0x2C33, 0x2C04 => 0x2C34, 0x2C05 => 0x2C35, 0x2C06 => 0x2C36, 0x2C07 => 0x2C37, 0x2C08 => 0x2C38, 0x2C09 => 0x2C39, 0x2C0A => 0x2C3A, 0x2C0B => 0x2C3B, 0x2C0C => 0x2C3C, 0x2C0D => 0x2C3D, 0x2C0E => 0x2C3E, 0x2C0F => 0x2C3F, 0x2C10 => 0x2C40, 0x2C11 => 0x2C41, 0x2C12 => 0x2C42, 0x2C13 => 0x2C43, 0x2C14 => 0x2C44, 0x2C15 => 0x2C45, 0x2C16 => 0x2C46, 0x2C17 => 0x2C47, 0x2C18 => 0x2C48, 0x2C19 => 0x2C49, 0x2C1A => 0x2C4A, 0x2C1B => 0x2C4B, 0x2C1C => 0x2C4C, 0x2C1D => 0x2C4D, 0x2C1E => 0x2C4E, 0x2C1F => 0x2C4F, 0x2C20 => 0x2C50, 0x2C21 => 0x2C51, 0x2C22 => 0x2C52, 0x2C23 => 0x2C53, 0x2C24 => 0x2C54, 0x2C25 => 0x2C55, 0x2C26 => 0x2C56, 0x2C27 => 0x2C57, 0x2C28 => 0x2C58, 0x2C29 => 0x2C59, 0x2C2A => 0x2C5A, 0x2C2B => 0x2C5B, 0x2C2C => 0x2C5C, 0x2C2D => 0x2C5D, 0x2C2E => 0x2C5E, 0x2C60 => 0x2C61, 0x2C62 => 0x26B, 0x2C63 => 0x1D7D, 0x2C64 => 0x27D, 0x2C67 => 0x2C68, 0x2C69 => 0x2C6A, 0x2C6B => 0x2C6C, 0x2C6D => 0x251, 0x2C6E => 0x271, 0x2C6F => 0x250, 0x2C70 => 0x252, 0x2C72 => 0x2C73, 0x2C75 => 0x2C76, 0x2C7E => 0x23F, 0x2C7F => 0x240, 0x2C80 => 0x2C81, 0x2C82 => 0x2C83, 0x2C84 => 0x2C85, 0x2C86 => 0x2C87, 0x2C88 => 0x2C89, 0x2C8A => 0x2C8B, 0x2C8C => 0x2C8D, 0x2C8E => 0x2C8F, 0x2C90 => 0x2C91, 0x2C92 => 0x2C93, 0x2C94 => 0x2C95, 0x2C96 => 0x2C97, 0x2C98 => 0x2C99, 0x2C9A => 0x2C9B, 0x2C9C => 0x2C9D, 0x2C9E => 0x2C9F, 0x2CA0 => 0x2CA1, 0x2CA2 => 0x2CA3, 0x2CA4 => 0x2CA5, 0x2CA6 => 0x2CA7, 0x2CA8 => 0x2CA9, 0x2CAA => 0x2CAB, 0x2CAC => 0x2CAD, 0x2CAE => 0x2CAF, 0x2CB0 => 0x2CB1, 0x2CB2 => 0x2CB3, 0x2CB4 => 0x2CB5, 0x2CB6 => 0x2CB7, 0x2CB8 => 0x2CB9, 0x2CBA => 0x2CBB, 0x2CBC => 0x2CBD, 0x2CBE => 0x2CBF, 0x2CC0 => 0x2CC1, 0x2CC2 => 0x2CC3, 0x2CC4 => 0x2CC5, 0x2CC6 => 0x2CC7, 0x2CC8 => 0x2CC9, 0x2CCA => 0x2CCB, 0x2CCC => 0x2CCD, 0x2CCE => 0x2CCF, 0x2CD0 => 0x2CD1, 0x2CD2 => 0x2CD3, 0x2CD4 => 0x2CD5, 0x2CD6 => 0x2CD7, 0x2CD8 => 0x2CD9, 0x2CDA => 0x2CDB, 0x2CDC => 0x2CDD, 0x2CDE => 0x2CDF, 0x2CE0 => 0x2CE1, 0x2CE2 => 0x2CE3, 0x2CEB => 0x2CEC, 0x2CED => 0x2CEE, 0x2CF2 => 0x2CF3, 0xA640 => 0xA641, 0xA642 => 0xA643, 0xA644 => 0xA645, 0xA646 => 0xA647, 0xA648 => 0xA649, 0xA64A => 0xA64B, 0xA64C => 0xA64D, 0xA64E => 0xA64F, 0xA650 => 0xA651, 0xA652 => 0xA653, 0xA654 => 0xA655, 0xA656 => 0xA657, 0xA658 => 0xA659, 0xA65A => 0xA65B, 0xA65C => 0xA65D, 0xA65E => 0xA65F, 0xA660 => 0xA661, 0xA662 => 0xA663, 0xA664 => 0xA665, 0xA666 => 0xA667, 0xA668 => 0xA669, 0xA66A => 0xA66B, 0xA66C => 0xA66D, 0xA680 => 0xA681, 0xA682 => 0xA683, 0xA684 => 0xA685, 0xA686 => 0xA687, 0xA688 => 0xA689, 0xA68A => 0xA68B, 0xA68C => 0xA68D, 0xA68E => 0xA68F, 0xA690 => 0xA691, 0xA692 => 0xA693, 0xA694 => 0xA695, 0xA696 => 0xA697, 0xA698 => 0xA699, 0xA69A => 0xA69B, 0xA722 => 0xA723, 0xA724 => 0xA725, 0xA726 => 0xA727, 0xA728 => 0xA729, 0xA72A => 0xA72B, 0xA72C => 0xA72D, 0xA72E => 0xA72F, 0xA732 => 0xA733, 0xA734 => 0xA735, 0xA736 => 0xA737, 0xA738 => 0xA739, 0xA73A => 0xA73B, 0xA73C => 0xA73D, 0xA73E => 0xA73F, 0xA740 => 0xA741, 0xA742 => 0xA743, 0xA744 => 0xA745, 0xA746 => 0xA747, 0xA748 => 0xA749, 0xA74A => 0xA74B, 0xA74C => 0xA74D, 0xA74E => 0xA74F, 0xA750 => 0xA751, 0xA752 => 0xA753, 0xA754 => 0xA755, 0xA756 => 0xA757, 0xA758 => 0xA759, 0xA75A => 0xA75B, 0xA75C => 0xA75D, 0xA75E => 0xA75F, 0xA760 => 0xA761, 0xA762 => 0xA763, 0xA764 => 0xA765, 0xA766 => 0xA767, 0xA768 => 0xA769, 0xA76A => 0xA76B, 0xA76C => 0xA76D, 0xA76E => 0xA76F, 0xA779 => 0xA77A, 0xA77B => 0xA77C, 0xA77D => 0x1D79, 0xA77E => 0xA77F, 0xA780 => 0xA781, 0xA782 => 0xA783, 0xA784 => 0xA785, 0xA786 => 0xA787, 0xA78B => 0xA78C, 0xA78D => 0x265, 0xA790 => 0xA791, 0xA792 => 0xA793, 0xA796 => 0xA797, 0xA798 => 0xA799, 0xA79A => 0xA79B, 0xA79C => 0xA79D, 0xA79E => 0xA79F, 0xA7A0 => 0xA7A1, 0xA7A2 => 0xA7A3, 0xA7A4 => 0xA7A5, 0xA7A6 => 0xA7A7, 0xA7A8 => 0xA7A9, 0xA7AA => 0x266, 0xA7AB => 0x25C, 0xA7AC => 0x261, 0xA7AD => 0x26C, 0xA7AE => 0x26A, 0xA7B0 => 0x29E, 0xA7B1 => 0x287, 0xA7B2 => 0x29D, 0xA7B3 => 0xAB53, 0xA7B4 => 0xA7B5, 0xA7B6 => 0xA7B7, 0xA7B8 => 0xA7B9, 0xA7BA => 0xA7BB, 0xA7BC => 0xA7BD, 0xA7BE => 0xA7BF, 0xA7C2 => 0xA7C3, 0xA7C4 => 0xA794, 0xA7C5 => 0x282, 0xA7C6 => 0x1D8E, 0xA7C7 => 0xA7C8, 0xA7C9 => 0xA7CA, 0xA7F5 => 0xA7F6, 0xAB70 => 0x13A0, 0xAB71 => 0x13A1, 0xAB72 => 0x13A2, 0xAB73 => 0x13A3, 0xAB74 => 0x13A4, 0xAB75 => 0x13A5, 0xAB76 => 0x13A6, 0xAB77 => 0x13A7, 0xAB78 => 0x13A8, 0xAB79 => 0x13A9, 0xAB7A => 0x13AA, 0xAB7B => 0x13AB, 0xAB7C => 0x13AC, 0xAB7D => 0x13AD, 0xAB7E => 0x13AE, 0xAB7F => 0x13AF, 0xAB80 => 0x13B0, 0xAB81 => 0x13B1, 0xAB82 => 0x13B2, 0xAB83 => 0x13B3, 0xAB84 => 0x13B4, 0xAB85 => 0x13B5, 0xAB86 => 0x13B6, 0xAB87 => 0x13B7, 0xAB88 => 0x13B8, 0xAB89 => 0x13B9, 0xAB8A => 0x13BA, 0xAB8B => 0x13BB, 0xAB8C => 0x13BC, 0xAB8D => 0x13BD, 0xAB8E => 0x13BE, 0xAB8F => 0x13BF, 0xAB90 => 0x13C0, 0xAB91 => 0x13C1, 0xAB92 => 0x13C2, 0xAB93 => 0x13C3, 0xAB94 => 0x13C4, 0xAB95 => 0x13C5, 0xAB96 => 0x13C6, 0xAB97 => 0x13C7, 0xAB98 => 0x13C8, 0xAB99 => 0x13C9, 0xAB9A => 0x13CA, 0xAB9B => 0x13CB, 0xAB9C => 0x13CC, 0xAB9D => 0x13CD, 0xAB9E => 0x13CE, 0xAB9F => 0x13CF, 0xABA0 => 0x13D0, 0xABA1 => 0x13D1, 0xABA2 => 0x13D2, 0xABA3 => 0x13D3, 0xABA4 => 0x13D4, 0xABA5 => 0x13D5, 0xABA6 => 0x13D6, 0xABA7 => 0x13D7, 0xABA8 => 0x13D8, 0xABA9 => 0x13D9, 0xABAA => 0x13DA, 0xABAB => 0x13DB, 0xABAC => 0x13DC, 0xABAD => 0x13DD, 0xABAE => 0x13DE, 0xABAF => 0x13DF, 0xABB0 => 0x13E0, 0xABB1 => 0x13E1, 0xABB2 => 0x13E2, 0xABB3 => 0x13E3, 0xABB4 => 0x13E4, 0xABB5 => 0x13E5, 0xABB6 => 0x13E6, 0xABB7 => 0x13E7, 0xABB8 => 0x13E8, 0xABB9 => 0x13E9, 0xABBA => 0x13EA, 0xABBB => 0x13EB, 0xABBC => 0x13EC, 0xABBD => 0x13ED, 0xABBE => 0x13EE, 0xABBF => 0x13EF, 0xFF21 => 0xFF41, 0xFF22 => 0xFF42, 0xFF23 => 0xFF43, 0xFF24 => 0xFF44, 0xFF25 => 0xFF45, 0xFF26 => 0xFF46, 0xFF27 => 0xFF47, 0xFF28 => 0xFF48, 0xFF29 => 0xFF49, 0xFF2A => 0xFF4A, 0xFF2B => 0xFF4B, 0xFF2C => 0xFF4C, 0xFF2D => 0xFF4D, 0xFF2E => 0xFF4E, 0xFF2F => 0xFF4F, 0xFF30 => 0xFF50, 0xFF31 => 0xFF51, 0xFF32 => 0xFF52, 0xFF33 => 0xFF53, 0xFF34 => 0xFF54, 0xFF35 => 0xFF55, 0xFF36 => 0xFF56, 0xFF37 => 0xFF57, 0xFF38 => 0xFF58, 0xFF39 => 0xFF59, 0xFF3A => 0xFF5A, 0x10400 => 0x10428, 0x10401 => 0x10429, 0x10402 => 0x1042A, 0x10403 => 0x1042B, 0x10404 => 0x1042C, 0x10405 => 0x1042D, 0x10406 => 0x1042E, 0x10407 => 0x1042F, 0x10408 => 0x10430, 0x10409 => 0x10431, 0x1040A => 0x10432, 0x1040B => 0x10433, 0x1040C => 0x10434, 0x1040D => 0x10435, 0x1040E => 0x10436, 0x1040F => 0x10437, 0x10410 => 0x10438, 0x10411 => 0x10439, 0x10412 => 0x1043A, 0x10413 => 0x1043B, 0x10414 => 0x1043C, 0x10415 => 0x1043D, 0x10416 => 0x1043E, 0x10417 => 0x1043F, 0x10418 => 0x10440, 0x10419 => 0x10441, 0x1041A => 0x10442, 0x1041B => 0x10443, 0x1041C => 0x10444, 0x1041D => 0x10445, 0x1041E => 0x10446, 0x1041F => 0x10447, 0x10420 => 0x10448, 0x10421 => 0x10449, 0x10422 => 0x1044A, 0x10423 => 0x1044B, 0x10424 => 0x1044C, 0x10425 => 0x1044D, 0x10426 => 0x1044E, 0x10427 => 0x1044F, 0x104B0 => 0x104D8, 0x104B1 => 0x104D9, 0x104B2 => 0x104DA, 0x104B3 => 0x104DB, 0x104B4 => 0x104DC, 0x104B5 => 0x104DD, 0x104B6 => 0x104DE, 0x104B7 => 0x104DF, 0x104B8 => 0x104E0, 0x104B9 => 0x104E1, 0x104BA => 0x104E2, 0x104BB => 0x104E3, 0x104BC => 0x104E4, 0x104BD => 0x104E5, 0x104BE => 0x104E6, 0x104BF => 0x104E7, 0x104C0 => 0x104E8, 0x104C1 => 0x104E9, 0x104C2 => 0x104EA, 0x104C3 => 0x104EB, 0x104C4 => 0x104EC, 0x104C5 => 0x104ED, 0x104C6 => 0x104EE, 0x104C7 => 0x104EF, 0x104C8 => 0x104F0, 0x104C9 => 0x104F1, 0x104CA => 0x104F2, 0x104CB => 0x104F3, 0x104CC => 0x104F4, 0x104CD => 0x104F5, 0x104CE => 0x104F6, 0x104CF => 0x104F7, 0x104D0 => 0x104F8, 0x104D1 => 0x104F9, 0x104D2 => 0x104FA, 0x104D3 => 0x104FB, 0x10C80 => 0x10CC0, 0x10C81 => 0x10CC1, 0x10C82 => 0x10CC2, 0x10C83 => 0x10CC3, 0x10C84 => 0x10CC4, 0x10C85 => 0x10CC5, 0x10C86 => 0x10CC6, 0x10C87 => 0x10CC7, 0x10C88 => 0x10CC8, 0x10C89 => 0x10CC9, 0x10C8A => 0x10CCA, 0x10C8B => 0x10CCB, 0x10C8C => 0x10CCC, 0x10C8D => 0x10CCD, 0x10C8E => 0x10CCE, 0x10C8F => 0x10CCF, 0x10C90 => 0x10CD0, 0x10C91 => 0x10CD1, 0x10C92 => 0x10CD2, 0x10C93 => 0x10CD3, 0x10C94 => 0x10CD4, 0x10C95 => 0x10CD5, 0x10C96 => 0x10CD6, 0x10C97 => 0x10CD7, 0x10C98 => 0x10CD8, 0x10C99 => 0x10CD9, 0x10C9A => 0x10CDA, 0x10C9B => 0x10CDB, 0x10C9C => 0x10CDC, 0x10C9D => 0x10CDD, 0x10C9E => 0x10CDE, 0x10C9F => 0x10CDF, 0x10CA0 => 0x10CE0, 0x10CA1 => 0x10CE1, 0x10CA2 => 0x10CE2, 0x10CA3 => 0x10CE3, 0x10CA4 => 0x10CE4, 0x10CA5 => 0x10CE5, 0x10CA6 => 0x10CE6, 0x10CA7 => 0x10CE7, 0x10CA8 => 0x10CE8, 0x10CA9 => 0x10CE9, 0x10CAA => 0x10CEA, 0x10CAB => 0x10CEB, 0x10CAC => 0x10CEC, 0x10CAD => 0x10CED, 0x10CAE => 0x10CEE, 0x10CAF => 0x10CEF, 0x10CB0 => 0x10CF0, 0x10CB1 => 0x10CF1, 0x10CB2 => 0x10CF2, 0x118A0 => 0x118C0, 0x118A1 => 0x118C1, 0x118A2 => 0x118C2, 0x118A3 => 0x118C3, 0x118A4 => 0x118C4, 0x118A5 => 0x118C5, 0x118A6 => 0x118C6, 0x118A7 => 0x118C7, 0x118A8 => 0x118C8, 0x118A9 => 0x118C9, 0x118AA => 0x118CA, 0x118AB => 0x118CB, 0x118AC => 0x118CC, 0x118AD => 0x118CD, 0x118AE => 0x118CE, 0x118AF => 0x118CF, 0x118B0 => 0x118D0, 0x118B1 => 0x118D1, 0x118B2 => 0x118D2, 0x118B3 => 0x118D3, 0x118B4 => 0x118D4, 0x118B5 => 0x118D5, 0x118B6 => 0x118D6, 0x118B7 => 0x118D7, 0x118B8 => 0x118D8, 0x118B9 => 0x118D9, 0x118BA => 0x118DA, 0x118BB => 0x118DB, 0x118BC => 0x118DC, 0x118BD => 0x118DD, 0x118BE => 0x118DE, 0x118BF => 0x118DF, 0x16E40 => 0x16E60, 0x16E41 => 0x16E61, 0x16E42 => 0x16E62, 0x16E43 => 0x16E63, 0x16E44 => 0x16E64, 0x16E45 => 0x16E65, 0x16E46 => 0x16E66, 0x16E47 => 0x16E67, 0x16E48 => 0x16E68, 0x16E49 => 0x16E69, 0x16E4A => 0x16E6A, 0x16E4B => 0x16E6B, 0x16E4C => 0x16E6C, 0x16E4D => 0x16E6D, 0x16E4E => 0x16E6E, 0x16E4F => 0x16E6F, 0x16E50 => 0x16E70, 0x16E51 => 0x16E71, 0x16E52 => 0x16E72, 0x16E53 => 0x16E73, 0x16E54 => 0x16E74, 0x16E55 => 0x16E75, 0x16E56 => 0x16E76, 0x16E57 => 0x16E77, 0x16E58 => 0x16E78, 0x16E59 => 0x16E79, 0x16E5A => 0x16E7A, 0x16E5B => 0x16E7B, 0x16E5C => 0x16E7C, 0x16E5D => 0x16E7D, 0x16E5E => 0x16E7E, 0x16E5F => 0x16E7F, 0x1E900 => 0x1E922, 0x1E901 => 0x1E923, 0x1E902 => 0x1E924, 0x1E903 => 0x1E925, 0x1E904 => 0x1E926, 0x1E905 => 0x1E927, 0x1E906 => 0x1E928, 0x1E907 => 0x1E929, 0x1E908 => 0x1E92A, 0x1E909 => 0x1E92B, 0x1E90A => 0x1E92C, 0x1E90B => 0x1E92D, 0x1E90C => 0x1E92E, 0x1E90D => 0x1E92F, 0x1E90E => 0x1E930, 0x1E90F => 0x1E931, 0x1E910 => 0x1E932, 0x1E911 => 0x1E933, 0x1E912 => 0x1E934, 0x1E913 => 0x1E935, 0x1E914 => 0x1E936, 0x1E915 => 0x1E937, 0x1E916 => 0x1E938, 0x1E917 => 0x1E939, 0x1E918 => 0x1E93A, 0x1E919 => 0x1E93B, 0x1E91A => 0x1E93C, 0x1E91B => 0x1E93D, 0x1E91C => 0x1E93E, 0x1E91D => 0x1E93F, 0x1E91E => 0x1E940, 0x1E91F => 0x1E941, 0x1E920 => 0x1E942, 0x1E921 => 0x1E943, ]; string/composer.json 0000777 00000001742 15252016106 0010605 0 ustar 00 { "name": "opis/string", "description": "Multibyte strings as objects", "keywords": ["opis", "string", "utf-8", "multi-byte", "string manipulation"], "homepage": "https://opis.io/string", "license": "Apache-2.0", "authors": [ { "name": "Marius Sarca", "email": "marius.sarca@gmail.com" }, { "name": "Sorin Sarca", "email": "sarca_sorin@hotmail.com" } ], "require": { "php": "^7.4 || ^8.0", "ext-json": "*", "ext-iconv": "*" }, "require-dev": { "phpunit/phpunit": "^9.0" }, "autoload": { "psr-4": { "Opis\\String\\": "src/" } }, "autoload-dev": { "psr-4": { "Opis\\String\\Test\\": "tests/" } }, "scripts": { "tests": "./vendor/bin/phpunit --verbose --color" }, "extra": { "branch-alias": { "dev-master": "2.x-dev" } } } string/LICENSE 0000777 00000023675 15252016106 0007101 0 ustar 00 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS string/NOTICE 0000777 00000000462 15252016106 0006765 0 ustar 00 Opis String Copyright 2018-2021 Zindex Software This product includes software developed at Zindex Software (http://zindex.software). This software was originally developed by Marius Sarca and Sorin Sarca (Copyright 2016-2018). The copyright info was changed with the permission of the original authors. uri/src/PunycodeException.php 0000777 00000001532 15252016106 0012316 0 ustar 00 <?php /* ============================================================================ * Copyright 2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\Uri; use RuntimeException; class PunycodeException extends RuntimeException { } uri/src/UriTemplate.php 0000777 00000035271 15252016106 0011113 0 ustar 00 <?php /* ============================================================================ * Copyright 2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\Uri; use Opis\String\UnicodeString; class UriTemplate { /** @var string */ protected const TEMPLATE_VARSPEC_REGEX = '~^(?<varname>[a-zA-Z0-9\_\%\.]+)(?:(?<explode>\*)?|\:(?<prefix>\d+))?$~'; /** @var string */ protected const TEMPLATE_REGEX = <<<'REGEX' ~\{ (?<operator>[+#./;&=,!@|\?])? (?<varlist> (?:(?P>varspec),)* (?<varspec>(?: [a-zA-Z0-9\_\%\.]+ (?:\*|\:\d+)? )) ) \}~x REGEX; /** @var array */ protected const TEMPLATE_TABLE = [ '' => [ 'first' => '', 'sep' => ',', 'named' => false, 'ifemp' => '', 'allow' => false, ], '+' => [ 'first' => '', 'sep' => ',', 'named' => false, 'ifemp' => '', 'allow' => true, ], '.' => [ 'first' => '.', 'sep' => '.', 'named' => false, 'ifemp' => '', 'allow' => false, ], '/' => [ 'first' => '/', 'sep' => '/', 'named' => false, 'ifemp' => '', 'allow' => false, ], ';' => [ 'first' => ';', 'sep' => ';', 'named' => true, 'ifemp' => '', 'allow' => false, ], '?' => [ 'first' => '?', 'sep' => '&', 'named' => true, 'ifemp' => '=', 'allow' => false, ], '&' => [ 'first' => '&', 'sep' => '&', 'named' => true, 'ifemp' => '=', 'allow' => false, ], '#' => [ 'first' => '#', 'sep' => ',', 'named' => false, 'ifemp' => '', 'allow' => true, ], ]; protected string $uri; /** @var bool|null|array */ protected $parsed = false; /** * UriTemplate constructor. * @param string $uri_template */ public function __construct(string $uri_template) { $this->uri = $uri_template; } /** * @param array $vars * @return string */ public function resolve(array $vars): string { if ($this->parsed === false) { $this->parsed = $this->parse($this->uri); } if ($this->parsed === null || !$vars) { return $this->uri; } $data = ''; $vars = $this->prepareVars($vars); foreach ($this->parsed as $item) { if (!is_array($item)) { $data .= $item; continue; } $data .= $this->parseTemplateExpression( self::TEMPLATE_TABLE[$item['operator']], $this->resolveVars($item['vars'], $vars) ); } return $data; } /** * @return bool */ public function hasPlaceholders(): bool { if ($this->parsed === false) { $this->parse($this->uri); } return $this->parsed !== null; } /** * @param string $uri * @return array|null */ protected function parse(string $uri): ?array { $placeholders = null; preg_match_all(self::TEMPLATE_REGEX, $uri, $placeholders, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); if (!$placeholders) { return null; } $dataIndex = -1; $data = []; $hasVars = false; $nextOffset = 0; foreach ($placeholders as &$p) { $offset = $p[0][1]; if ($nextOffset < $offset) { $data[] = substr($uri, $nextOffset, $offset - $nextOffset); $dataIndex++; } $matched = $p[0][0]; $nextOffset = $offset + strlen($matched); $operator = $p['operator'][0] ?? null; if ($operator === null || !isset(self::TEMPLATE_TABLE[$operator])) { if ($dataIndex >= 0 && is_string($data[$dataIndex])) { $data[$dataIndex] .= $matched; } else { $data[] = $matched; $dataIndex++; } continue; } $varList = $p['varlist'][0] ?? ''; $varList = $varList === '' ? [] : explode(',', $varList); $p = null; $varData = []; foreach ($varList as $var) { if (!preg_match(self::TEMPLATE_VARSPEC_REGEX, $var, $spec)) { continue; } $varData[] = [ 'name' => $spec['varname'], 'explode' => isset($spec['explode']) && $spec['explode'] === '*', 'prefix' => isset($spec['prefix']) ? (int)$spec['prefix'] : 0, ]; unset($var, $spec); } if ($varData) { $hasVars = true; $data[] = [ 'operator' => $operator, 'vars' => $varData, ]; $dataIndex++; } else { if ($dataIndex >= 0 && is_string($data[$dataIndex])) { $data[$dataIndex] .= $matched; } else { $data[] = $matched; $dataIndex++; } } unset($varData, $varList, $operator); } if (!$hasVars) { return null; } $matched = substr($uri, $nextOffset); if ($matched !== false && $matched !== '') { if ($dataIndex >= 0 && is_string($data[$dataIndex])) { $data[$dataIndex] .= $matched; } else { $data[] = $matched; } } return $data; } /** * Convert assoc arrays to objects * @param array $vars * @return array */ protected function prepareVars(array $vars): array { foreach ($vars as &$value) { if (is_scalar($value)) { if (!is_string($value)) { $value = (string)$value; } continue; } if (!is_array($value)) { continue; } $len = count($value); for ($i = 0; $i < $len; $i++) { if (!array_key_exists($i, $value)) { $value = (object)$value; break; } } } return $vars; } /** * @param array $vars * @param array $data * @return array */ protected function resolveVars(array $vars, array $data): array { $resolved = []; foreach ($vars as $info) { $name = $info['name']; if (!isset($data[$name])) { continue; } $resolved[] = $info + ['value' => &$data[$name]]; } return $resolved; } /** * @param array $table * @param array $data * @return string */ protected function parseTemplateExpression(array $table, array $data): string { $result = []; foreach ($data as $var) { $str = ""; if (is_string($var['value'])) { if ($table['named']) { $str .= $var['name']; if ($var['value'] === '') { $str .= $table['ifemp']; } else { $str .= '='; } } if ($var['prefix']) { $str .= $this->encodeTemplateString(self::prefix($var['value'], $var['prefix']), $table['allow']); } else { $str .= $this->encodeTemplateString($var['value'], $table['allow']); } } elseif ($var['explode']) { $list = []; if ($table['named']) { if (is_array($var['value'])) { foreach ($var['value'] as $v) { if (is_null($v) || !is_scalar($v)) { continue; } $v = $this->encodeTemplateString((string)$v, $table['allow']); if ($v === '') { $list[] = $var['name'] . $table['ifemp']; } else { $list[] = $var['name'] . '=' . $v; } } } elseif (is_object($var['value'])) { foreach ($var['value'] as $prop => $v) { if (is_null($v) || !is_scalar($v)) { continue; } $v = $this->encodeTemplateString((string)$v, $table['allow']); $prop = $this->encodeTemplateString((string)$prop, $table['allow']); if ($v === '') { $list[] = $prop . $table['ifemp']; } else { $list[] = $prop . '=' . $v; } } } } else { if (is_array($var['value'])) { foreach ($var['value'] as $v) { if (is_null($v) || !is_scalar($v)) { continue; } $list[] = $this->encodeTemplateString($v, $table['allow']); } } elseif (is_object($var['value'])) { foreach ($var['value'] as $prop => $v) { if (is_null($v) || !is_scalar($v)) { continue; } $v = $this->encodeTemplateString((string)$v, $table['allow']); $prop = $this->encodeTemplateString((string)$prop, $table['allow']); $list[] = $prop . '=' . $v; } } } if ($list) { $str .= implode($table['sep'], $list); } unset($list); } else { if ($table['named']) { $str .= $var['name']; if ($var['value'] === '') { $str .= $table['ifemp']; } else { $str .= '='; } } $list = []; if (is_array($var['value'])) { foreach ($var['value'] as $v) { $list[] = $this->encodeTemplateString($v, $table['allow']); } } elseif (is_object($var['value'])) { foreach ($var['value'] as $prop => $v) { $list[] = $this->encodeTemplateString((string)$prop, $table['allow']); $list[] = $this->encodeTemplateString((string)$v, $table['allow']); } } if ($list) { $str .= implode(',', $list); } unset($list); } if ($str !== '') { $result[] = $str; } } if (!$result) { return ''; } $result = implode($table['sep'], $result); if ($result !== '') { $result = $table['first'] . $result; } return $result; } /** * @param string $data * @param bool $reserved * @return string */ protected function encodeTemplateString(string $data, bool $reserved): string { $skip = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~'; if ($reserved) { $skip .= ':/?#[]@!$&\'()*+,;='; } $result = ''; $temp = ''; for ($i = 0, $len = strlen($data); $i < $len; $i++) { if (strpos($skip, $data[$i]) !== false) { if ($temp !== '') { $result .= Uri::encodeComponent($temp); $temp = ''; } $result .= $data[$i]; continue; } if ($reserved && $data[$i] === '%') { if (isset($data[$i + 1]) && isset($data[$i + 2]) && strpos('ABCDEF0123456789', $data[$i + 1]) !== false && strpos('ABCDEF0123456789', $data[$i + 2]) !== false) { if ($temp !== '') { $result .= Uri::encodeComponent($temp); } $result .= '%' . $data[$i + 1] . $data[$i + 2]; $i += 3; continue; } } $temp .= $data[$i]; } if ($temp !== '') { $result .= Uri::encodeComponent($temp); } return $result; } /** * @return string */ public function value(): string { return $this->uri; } public function __toString(): string { return $this->uri; } /** * @param string $uri * @return bool */ public static function isTemplate(string $uri): bool { $open = substr_count($uri, '{'); if ($open === 0) { return false; } $close = substr_count($uri, '}'); if ($open !== $close) { return false; } return (bool)preg_match(self::TEMPLATE_REGEX, $uri); } /** * @param string $str * @param int $len * @return string */ protected static function prefix(string $str, int $len): string { if ($len === 0) { return ''; } if ($len >= strlen($str)) { // Prefix is longer than string length return $str; } return (string)UnicodeString::from($str)->substring(0, $len); } } uri/src/Punycode.php 0000777 00000017450 15252016106 0010445 0 ustar 00 <?php /* ============================================================================ * Copyright 2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\Uri; use Opis\String\UnicodeString; final class Punycode { private const BASE = 36; private const TMIN = 1; private const TMAX = 26; private const SKEW = 38; private const DAMP = 700; private const INITIAL_BIAS = 72; private const INITIAL_N = 0x80; private const PREFIX = 'xn--'; private const PREFIX_LEN = 4; private const DELIMITER = 0x2D; private const MAX_INT = 0x7FFFFFFF; private const NON_ASCII = '#[^\0-\x7E]#'; public static function encode(string $input): string { return implode('.', array_map([self::class, 'encodePart'], explode('.', $input))); } public static function decode(string $input): string { return implode('.', array_map([self::class, 'decodePart'], explode('.', $input))); } public static function normalize(string $input): string { return implode('.', array_map([self::class, 'normalizePart'], explode('.', $input))); } public static function encodePart(string $input): string { if (!preg_match(self::NON_ASCII, $input)) { return $input; } $input = UnicodeString::getCodePointsFromString($input, UnicodeString::LOWER_CASE); $input_len = count($input); $output = array_filter($input, static function (int $code): bool { return $code < 0x80; }); if ($output) { $output = array_values($output); } $delta = 0; $n = self::INITIAL_N; $bias = self::INITIAL_BIAS; $handled = $basic_length = count($output); if ($basic_length) { $output[] = self::DELIMITER; } while ($handled < $input_len) { $m = self::MAX_INT; for ($i = 0; $i < $input_len; $i++) { if ($input[$i] >= $n && $input[$i] < $m) { $m = $input[$i]; } } if (($m - $n) > intdiv(self::MAX_INT - $delta, $handled + 1)) { throw new PunycodeException("Punycode overflow"); } $delta += ($m - $n) * ($handled + 1); $n = $m; for ($i = 0; $i < $input_len; $i++) { if ($input[$i] < $n && (++$delta === 0)) { throw new PunycodeException("Punycode overflow"); } if ($input[$i] === $n) { $q = $delta; for ($k = self::BASE; ; $k += self::BASE) { $t = self::threshold($k, $bias); if ($q < $t) { break; } $base_minus_t = self::BASE - $t; $q -= $t; $output[] = self::encodeDigit($t + ($q % $base_minus_t)); $q = intdiv($q, $base_minus_t); } $output[] = self::encodeDigit($q); $bias = self::adapt($delta, $handled + 1, $handled === $basic_length); $delta = 0; $handled++; } } $delta++; $n++; } return self::PREFIX . UnicodeString::getStringFromCodePoints($output); } public static function decodePart(string $input): string { if (stripos($input, self::PREFIX) !== 0) { return $input; } $input = UnicodeString::getCodePointsFromString(substr($input, self::PREFIX_LEN), UnicodeString::LOWER_CASE); $input_len = count($input); $pos = array_keys($input, self::DELIMITER, true); if ($pos) { $pos = end($pos); } else { $pos = -1; } /** @var int $pos */ if ($pos === -1) { $output = []; $pos = $output_len = 0; } else { $output = array_slice($input, 0, ++$pos); $output_len = $pos; for ($i = 0; $i < $pos; $i++) { if ($output[$i] >= 0x80) { throw new PunycodeException("Non-basic code point is not allowed: {$output[$i]}"); } } } $i = 0; $n = self::INITIAL_N; $bias = self::INITIAL_BIAS; while ($pos < $input_len) { $old_i = $i; for ($w = 1, $k = self::BASE; ; $k += self::BASE) { if ($pos >= $input_len) { throw new PunycodeException("Punycode bad input"); } $digit = self::decodeDigit($input[$pos++]); if ($digit >= self::BASE || $digit > intdiv(self::MAX_INT - $i, $w)) { throw new PunycodeException("Punycode overflow"); } $i += $digit * $w; $t = self::threshold($k, $bias); if ($digit < $t) { break; } $t = self::BASE - $t; if ($w > intdiv(self::MAX_INT, $t)) { throw new PunycodeException("Punycode overflow"); } $w *= $t; } $output_len++; if (intdiv($i, $output_len) > self::MAX_INT - $n) { throw new PunycodeException("Punycode overflow"); } $n += intdiv($i, $output_len); $bias = self::adapt($i - $old_i, $output_len, $old_i === 0); $i %= $output_len; array_splice($output, $i, 0, $n); $i++; } return UnicodeString::getStringFromCodePoints($output); } public static function normalizePart(string $input): string { $input = strtolower($input); if (strpos($input, self::DELIMITER) === 0) { self::decodePart($input); // just validate return $input; } return self::encodePart($input); } private static function encodeDigit(int $digit): int { return $digit + 0x16 + ($digit < 0x1A ? 0x4B: 0x00); } private static function decodeDigit(int $code): int { if ($code < 0x3A) { return $code - 0x16; } if ($code < 0x5B) { return $code - 0x41; } if ($code < 0x7B) { return $code - 0x61; } return self::BASE; } private static function threshold(int $k, int $bias): int { $d = $k - $bias; if ($d <= self::TMIN) { return self::TMIN; } if ($d >= self::TMAX) { return self::TMAX; } return $d; } private static function adapt(int $delta, int $num_points, bool $first_time = false): int { $delta = intdiv($delta, $first_time ? self::DAMP : 2); $delta += intdiv($delta, $num_points); $k = 0; $base_tmin_diff = self::BASE - self::TMIN; $lim = $base_tmin_diff * self::TMAX / 2; while ($delta > $lim) { $delta = intdiv($delta, $base_tmin_diff); $k += self::BASE; } $k += intdiv(($base_tmin_diff + 1) * $delta, $delta + self::SKEW); return $k; } } uri/src/Uri.php 0000777 00000063263 15252016106 0007421 0 ustar 00 <?php /* ============================================================================ * Copyright 2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\Uri; use Opis\String\UnicodeString; class Uri { protected const URI_REGEX = '`^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$`'; protected const SCHEME_REGEX = '`^[a-z][a-z0-9-+.]*$`i'; protected const USER_OR_PASS_REGEX = '`^(?:(?:%[a-f0-9]{2})+|[a-z0-9-._~!$&\'()*+,:;=]+)*$`i'; protected const USERINFO_REGEX = '`^(?<user>[^:]+)(?::(?<pass>.*))?$`'; protected const HOST_LABEL_REGEX = '`^(?:(?:%[a-f0-9]{2})+|[a-z0-9-]+)*$`i'; protected const AUTHORITY_REGEX = '`^(?:(?<userinfo>[^@]+)\@)?(?<host>(\[[a-f0-9:]+\]|[^:]+))(?::(?<port>\d+))?$`i'; protected const PATH_REGEX = '`^(?:(?:%[a-f0-9]{2})+|[a-z0-9-._~!$&\'()*+,;=:@/]+)*$`i'; protected const QUERY_OR_FRAGMENT_REGEX = '`^(?:(?:%[a-f0-9]{2})+|[a-z0-9-._~!$&\'"()\[\]*+,;=:@?/%]+)*$`i'; protected array $components; protected ?string $str = null; /** * @param array $components An array of normalized components */ public function __construct(array $components) { $this->components = $components + [ 'scheme' => null, 'user' => null, 'pass' => null, 'host' => null, 'port' => null, 'path' => null, 'query' => null, 'fragment' => null, ]; } /** * @return string|null */ public function scheme(): ?string { return $this->components['scheme']; } /** * @return string|null */ public function user(): ?string { return $this->components['user']; } /** * @return string|null */ public function pass(): ?string { return $this->components['pass']; } /** * @return string|null */ public function userInfo(): ?string { if ($this->components['user'] === null) { return null; } if ($this->components['pass'] === null) { return $this->components['user']; } return $this->components['user'] . ':' . $this->components['pass']; } /** * @return string|null */ public function host(): ?string { return $this->components['host']; } /** * @return int|null */ public function port(): ?int { return $this->components['port']; } /** * @return string|null */ public function authority(): ?string { if ($this->components['host'] === null) { return null; } $authority = $this->userInfo(); if ($authority !== null) { $authority .= '@'; } $authority .= $this->components['host']; if ($this->components['port'] !== null) { $authority .= ':' . $this->components['port']; } return $authority; } /** * @return string|null */ public function path(): ?string { return $this->components['path']; } /** * @return string|null */ public function query(): ?string { return $this->components['query']; } /** * @return string|null */ public function fragment(): ?string { return $this->components['fragment']; } /** * @return array|null[] */ public function components(): array { return $this->components; } /** * @return bool */ public function isAbsolute(): bool { return $this->components['scheme'] !== null; } /** * Use this URI as base to resolve the reference * @param static|string|array $ref * @param bool $normalize * @return $this|null */ public function resolveRef($ref, bool $normalize = false): ?self { $ref = self::resolveComponents($ref); if ($ref === null) { return $this; } return new static(self::mergeComponents($ref, $this->components, $normalize)); } /** * Resolve this URI reference using a base URI * @param static|string|array $base * @param bool $normalize * @return static */ public function resolve($base, bool $normalize = false): self { if ($this->isAbsolute()) { return $this; } $base = self::resolveComponents($base); if ($base === null) { return $this; } return new static(self::mergeComponents($this->components, $base, $normalize)); } /** * @return string */ public function __toString(): string { if ($this->str !== null) { return $this->str; } $str = ''; if ($this->components['scheme'] !== null) { $str .= $this->components['scheme'] . ':'; } if ($this->components['host'] !== null) { $str .= '//' . $this->authority(); } $str .= $this->components['path']; if ($this->components['query'] !== null) { $str .= '?' . $this->components['query']; } if ($this->components['fragment'] !== null) { $str .= '#' . $this->components['fragment']; } return $this->str = $str; } /** * @param string $uri * @param bool $normalize * @return static|null */ public static function create(string $uri, bool $normalize = false): ?self { $comp = self::parseComponents($uri); if (!$comp) { return null; } if ($normalize) { $comp = self::normalizeComponents($comp); } return new static($comp); } /** * Checks if the scheme contains valid chars * @param string $scheme * @return bool */ public static function isValidScheme(string $scheme): bool { return (bool)preg_match(self::SCHEME_REGEX, $scheme); } /** * Checks if user contains valid chars * @param string $user * @return bool */ public static function isValidUser(string $user): bool { return (bool)preg_match(self::USER_OR_PASS_REGEX, $user); } /** * Checks if pass contains valid chars * @param string $pass * @return bool */ public static function isValidPass(string $pass): bool { return (bool)preg_match(self::USER_OR_PASS_REGEX, $pass); } /** * @param string $userInfo * @return bool */ public static function isValidUserInfo(string $userInfo): bool { /** @var array|string $userInfo */ if (!preg_match(self::USERINFO_REGEX, $userInfo, $userInfo)) { return false; } if (!self::isValidUser($userInfo['user'])) { return false; } if (isset($userInfo['pass'])) { return self::isValidPass($userInfo['pass']); } return true; } /** * Checks if host is valid * @param string $host * @return bool */ public static function isValidHost(string $host): bool { // min and max length if ($host === '' || isset($host[253])) { return false; } // check ipv6 if ($host[0] === '[') { if ($host[-1] !== ']') { return false; } return filter_var( substr($host, 1, -1), \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6 ) !== false; } // check ipv4 if (preg_match('`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\$`', $host)) { return \filter_var($host, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4) !== false; } foreach (explode('.', $host) as $host) { // empty or too long label if ($host === '' || isset($host[63])) { return false; } if ($host[0] === '-' || $host[-1] === '-') { return false; } if (!preg_match(self::HOST_LABEL_REGEX, $host)) { return false; } } return true; } /** * Checks if the port is valid * @param int $port * @return bool */ public static function isValidPort(int $port): bool { return $port >= 0 && $port <= 65535; } /** * Checks if authority contains valid chars * @param string $authority * @return bool */ public static function isValidAuthority(string $authority): bool { if ($authority === '') { return true; } /** @var array|string $authority */ if (!preg_match(self::AUTHORITY_REGEX, $authority, $authority)) { return false; } if (isset($authority['port']) && !self::isValidPort((int)$authority['port'])) { return false; } if (isset($authority['userinfo']) && !self::isValidUserInfo($authority['userinfo'])) { return false; } return self::isValidHost($authority['host']); } /** * Checks if the path contains valid chars * @param string $path * @return bool */ public static function isValidPath(string $path): bool { return $path === '' || (bool)preg_match(self::PATH_REGEX, $path); } /** * Checks if the query string contains valid chars * @param string $query * @return bool */ public static function isValidQuery(string $query): bool { return $query === '' || (bool)preg_match(self::QUERY_OR_FRAGMENT_REGEX, $query); } /** * Checks if the fragment contains valid chars * @param string $fragment * @return bool */ public static function isValidFragment(string $fragment): bool { return $fragment === '' || (bool)preg_match(self::QUERY_OR_FRAGMENT_REGEX, $fragment); } /** * @param string $uri * @param bool $expand_authority * @param bool $validate * @return array|null */ public static function parseComponents(string $uri, bool $expand_authority = true, bool $validate = true): ?array { if (!preg_match(self::URI_REGEX, $uri, $uri)) { return null; } $comp = []; // scheme if (isset($uri[2]) && $uri[2] !== '') { if ($validate && !self::isValidScheme($uri[2])) { return null; } $comp['scheme'] = $uri[2]; } // authority if (isset($uri[4]) && isset($uri[3][0])) { if ($uri[4] === '') { if ($expand_authority) { $comp['host'] = ''; } else { $comp['authority'] = ''; } } elseif ($expand_authority) { $au = self::parseAuthorityComponents($uri[4], $validate); if ($au === null) { return null; } $comp += $au; unset($au); } else { if ($validate && !self::isValidAuthority($uri[4])) { return null; } $comp['authority'] = $uri[4]; } } // path if (isset($uri[5])) { if ($validate && !self::isValidPath($uri[5])) { return null; } $comp['path'] = $uri[5]; // not a relative uri, remove dot segments if (isset($comp['scheme']) || isset($comp['authority']) || isset($comp['host'])) { $comp['path'] = self::removeDotSegmentsFromPath($comp['path']); } } // query if (isset($uri[7]) && isset($uri[6][0])) { if ($validate && !self::isValidQuery($uri[7])) { return null; } $comp['query'] = $uri[7]; } // fragment if (isset($uri[9]) && isset($uri[8][0])) { if ($validate && !self::isValidFragment($uri[9])) { return null; } $comp['fragment'] = $uri[9]; } return $comp; } /** * @param self|string|array $uri * @return array|null */ public static function resolveComponents($uri): ?array { if ($uri instanceof self) { return $uri->components; } if (is_string($uri)) { return self::parseComponents($uri); } if (is_array($uri)) { if (isset($uri['host'])) { unset($uri['authority']); } elseif (isset($uri['authority'])) { $au = self::parseAuthorityComponents($uri['authority']); unset($uri['authority']); if ($au !== null) { unset($uri['user'], $uri['pass'], $uri['host'], $uri['port']); $uri += $au; } } return $uri; } return null; } /** * @param string $authority * @param bool $validate * @return array|null */ public static function parseAuthorityComponents(string $authority, bool $validate = true): ?array { /** @var array|string $authority */ if (!preg_match(self::AUTHORITY_REGEX, $authority, $authority)) { return null; } $comp = []; // userinfo if (isset($authority['userinfo']) && $authority['userinfo'] !== '') { if (!preg_match(self::USERINFO_REGEX, $authority['userinfo'], $ui)) { return null; } // user if ($validate && !self::isValidUser($ui['user'])) { return null; } $comp['user'] = $ui['user']; // pass if (isset($ui['pass']) && $ui['pass'] !== '') { if ($validate && !self::isValidPass($ui['pass'])) { return null; } $comp['pass'] = $ui['pass']; } unset($ui); } // host if ($validate && !self::isValidHost($authority['host'])) { return null; } $comp['host'] = $authority['host']; // port if (isset($authority['port'])) { $authority['port'] = (int)$authority['port']; if (!self::isValidPort($authority['port'])) { return null; } $comp['port'] = $authority['port']; } return $comp; } /** * @param array $ref * @param array $base * @param bool $normalize * @return array */ public static function mergeComponents(array $ref, array $base, bool $normalize = false): array { if (isset($ref['scheme'])) { $dest = $ref; } else { $dest = []; $dest['scheme'] = $base['scheme'] ?? null; if (isset($ref['authority']) || isset($ref['host'])) { $dest += $ref; } else { if (isset($base['authority'])) { $dest['authority'] = $base['authority']; } else { $dest['user'] = $base['user'] ?? null; $dest['pass'] = $base['pass'] ?? null; $dest['host'] = $base['host'] ?? null; $dest['port'] = $base['port'] ?? null; } if (!isset($ref['path'])) { $ref['path'] = ''; } if (!isset($base['path'])) { $base['path'] = ''; } if ($ref['path'] === '') { $dest['path'] = $base['path']; $dest['query'] = $ref['query'] ?? $base['query'] ?? null; } else { if ($ref['path'][0] === '/') { $dest['path'] = $ref['path']; } else { if ((isset($base['authority']) || isset($base['host'])) && $base['path'] === '') { $dest['path'] = '/' . $ref['path']; } else { $dest['path'] = $base['path']; if ($dest['path'] !== '') { $pos = strrpos($dest['path'], '/'); if ($pos === false) { $dest['path'] = ''; } else { $dest['path'] = substr($dest['path'], 0, $pos); } unset($pos); } $dest['path'] .= '/' . $ref['path']; } } $dest['query'] = $ref['query'] ?? null; } } } $dest['fragment'] = $ref['fragment'] ?? null; if ($normalize) { return self::normalizeComponents($dest); } if (isset($dest['path'])) { $dest['path'] = self::removeDotSegmentsFromPath($dest['path']); } return $dest; } public static function normalizeComponents(array $components): array { if (isset($components['scheme'])) { $components['scheme'] = strtolower($components['scheme']); // Remove default port if (isset($components['port']) && self::getSchemePort($components['scheme']) === $components['port']) { $components['port'] = null; } } if (isset($components['host'])) { $components['host'] = strtolower($components['host']); } if (isset($components['path'])) { $components['path'] = self::removeDotSegmentsFromPath($components['path']); } if (isset($components['query'])) { $components['query'] = self::normalizeQueryString($components['query']); } return $components; } /** * Removes dot segments from path * @param string $path * @return string */ public static function removeDotSegmentsFromPath(string $path): string { // Fast check common simple paths if ($path === '' || $path === '/') { return $path; } $output = ''; $last_slash = 0; $len = strlen($path); $i = 0; while ($i < $len) { if ($path[$i] === '.') { $j = $i + 1; // search for . if ($j >= $len) { break; } // search for ./ if ($path[$j] === '/') { $i = $j + 1; continue; } // search for ../ if ($path[$j] === '.') { $k = $j + 1; if ($k >= $len) { break; } if ($path[$k] === '/') { $i = $k + 1; continue; } } } elseif ($path[$i] === '/') { $j = $i + 1; if ($j >= $len) { $output .= '/'; break; } // search for /. if ($path[$j] === '.') { $k = $j + 1; if ($k >= $len) { $output .= '/'; break; } // search for /./ if ($path[$k] === '/') { $i = $k; continue; } // search for /.. if ($path[$k] === '.') { $n = $k + 1; if ($n >= $len) { // keep the slash $output = substr($output, 0, $last_slash + 1); break; } // search for /../ if ($path[$n] === '/') { $output = substr($output, 0, $last_slash); $last_slash = (int)strrpos($output, '/'); $i = $n; continue; } } } } $pos = strpos($path, '/', $i + 1); if ($pos === false) { $output .= substr($path, $i); break; } $last_slash = strlen($output); $output .= substr($path, $i, $pos - $i); $i = $pos; } return $output; } /** * @param string|null $query * @return array */ public static function parseQueryString(?string $query): array { if ($query === null) { return []; } $list = []; foreach (explode('&', $query) as $name) { $value = null; if (($pos = strpos($name, '=')) !== false) { $value = self::decodeComponent(substr($name, $pos + 1)); $name = self::decodeComponent(substr($name, 0, $pos)); } else { $name = self::decodeComponent($name); } $list[$name] = $value; } return $list; } /** * @param array $qs * @param string|null $prefix * @param string $separator * @param bool $sort * @return string */ public static function buildQueryString(array $qs, ?string $prefix = null, string $separator = '&', bool $sort = false): string { $isIndexed = static function (array $array): bool { for ($i = 0, $max = count($array); $i < $max; $i++) { if (!array_key_exists($i, $array)) { return false; } } return true; }; $f = static function (array $arr, ?string $prefix = null) use (&$f, &$isIndexed): iterable { $indexed = $prefix !== null && $isIndexed($arr); foreach ($arr as $key => $value) { if ($prefix !== null) { $key = $prefix . ($indexed ? "[]" : "[{$key}]"); } if (is_array($value)) { yield from $f($value, $key); } else { yield $key => $value; } } }; $data = []; foreach ($f($qs, $prefix) as $key => $value) { $item = is_string($key) ? self::encodeComponent($key) : $key; if ($value !== null) { $item .= '='; $item .= is_string($value) ? self::encodeComponent($value) : $value; } if ($item === '' || $item === '=') { continue; } $data[] = $item; } if (!$data) { return ''; } if ($sort) { sort($data); } return implode($separator, $data); } /** * @param string $query * @return string */ public static function normalizeQueryString(string $query): string { return static::buildQueryString(self::parseQueryString($query), null, '&', true); } public static function decodeComponent(string $component): string { return rawurldecode($component); } public static function encodeComponent(string $component, ?array $skip = null): string { if (!$skip) { return rawurlencode($component); } $str = ''; foreach (UnicodeString::walkString($component) as [$cp, $chars]) { if ($cp < 0x80) { if ($cp === 0x2D || $cp === 0x2E || $cp === 0x5F || $cp === 0x7E || ($cp >= 0x41 && $cp <= 0x5A) || ($cp >= 0x61 && $cp <= 0x7A) || ($cp >= 0x30 && $cp <= 0x39) || in_array($cp, $skip, true) ) { $str .= chr($cp); } else { $str .= '%' . strtoupper(dechex($cp)); } } else { $i = 0; while (isset($chars[$i])) { $str .= '%' . strtoupper(dechex($chars[$i++])); } } } return $str; } public static function setSchemePort(string $scheme, ?int $port): void { $scheme = strtolower($scheme); if ($port === null) { unset(self::$KNOWN_PORTS[$scheme]); } else { self::$KNOWN_PORTS[$scheme] = $port; } } public static function getSchemePort(string $scheme): ?int { return self::$KNOWN_PORTS[strtolower($scheme)] ?? null; } protected static array $KNOWN_PORTS = [ 'ftp' => 21, 'ssh' => 22, 'telnet' => 23, 'smtp' => 25, 'tftp' => 69, 'http' => 80, 'pop' => 110, 'sftp' => 115, 'imap' => 143, 'irc' => 194, 'ldap' => 389, 'https' => 443, 'ldaps' => 636, 'telnets' => 992, 'imaps' => 993, 'ircs' => 994, 'pops' => 995, ]; } uri/composer.json 0000777 00000002023 15252016106 0010067 0 ustar 00 { "name": "opis/uri", "description": "Build, parse and validate URIs and URI-templates", "keywords": ["uri", "uri template", "url", "parse url", "uri components", "validate uri", "punycode"], "homepage": "https://opis.io", "license": "Apache-2.0", "authors": [ { "name": "Marius Sarca", "email": "marius.sarca@gmail.com" }, { "name": "Sorin Sarca", "email": "sarca_sorin@hotmail.com" } ], "require": { "php": "^7.4 || ^8.0", "opis/string": "^2.0" }, "require-dev": { "phpunit/phpunit": "^9" }, "autoload": { "psr-4": { "Opis\\Uri\\": "src/" } }, "autoload-dev": { "psr-4": { "Opis\\Uri\\Test\\": "tests/" } }, "config": { "preferred-install": "dist" }, "prefer-stable": true, "minimum-stability": "dev", "extra": { "branch-alias": { "dev-master": "1.x-dev" } } } uri/LICENSE 0000777 00000023675 15252016106 0006372 0 ustar 00 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS uri/autoload.php 0000777 00000002626 15252016106 0007677 0 ustar 00 <?php /* =========================================================================== * Copyright 2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ spl_autoload_register(function ($class) { $class = ltrim($class, '\\'); $dir = __DIR__ . '/src'; $namespace = 'Opis\Uri'; if (strpos($class, $namespace) === 0) { $class = substr($class, strlen($namespace)); $path = ''; if (($pos = strripos($class, '\\')) !== FALSE) { $path = str_replace('\\', '/', substr($class, 0, $pos)) . '/'; $class = substr($class, $pos + 1); } $path .= str_replace('_', '/', $class) . '.php'; $dir .= '/' . $path; if (is_file($dir)) { include $dir; return true; } return false; } return false; }); json-schema/autoload.php 0000777 00000001247 15252016106 0011305 0 ustar 00 <?php spl_autoload_register(function ($class) { $class = ltrim($class, '\\'); $dir = __DIR__ . '/src'; $namespace = 'Opis\JsonSchema'; if (strpos($class, $namespace) === 0) { $class = substr($class, strlen($namespace)); $path = ''; if (($pos = strripos($class, '\\')) !== false) { $path = str_replace('\\', '/', substr($class, 0, $pos)) . '/'; $class = substr($class, $pos + 1); } $path .= str_replace('_', '/', $class) . '.php'; $dir .= '/' . $path; if (is_file($dir)) { include $dir; return true; } return false; } return false; }); json-schema/NOTICE 0000777 00000000467 15252016106 0007673 0 ustar 00 Opis Json Schema Copyright 2018-2021 Zindex Software This product includes software developed at Zindex Software (http://zindex.software). This software was originally developed by Marius Sarca and Sorin Sarca (Copyright 2017-2018). The copyright info was changed with the permission of the original authors. json-schema/src/Format.php 0000777 00000001614 15252016106 0011512 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; interface Format { /** * @param $data * @return bool */ public function validate($data): bool; } json-schema/src/ContentEncoding.php 0000777 00000001726 15252016106 0013347 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; interface ContentEncoding { /** * @param string $value * @param string $type * @return null|string */ public function decode(string $value, string $type): ?string; } json-schema/src/Filters/FormatExistsFilter.php 0000777 00000003176 15252016106 0015475 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Filters; use Opis\JsonSchema\{ValidationContext, Filter, Schema}; class FormatExistsFilter implements Filter { /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema, array $args = []): bool { $format = $args['format'] ?? $context->currentData(); if (!is_string($format)) { return false; } $type = null; if (isset($args['type'])) { if (!is_string($args['type'])) { return false; } $type = $args['type']; } $resolver = $context->loader()->parser()->getFormatResolver(); if (!$resolver) { return false; } if ($type === null) { return (bool)$resolver->resolveAll($format); } return (bool)$resolver->resolve($format, $type); } } json-schema/src/Filters/GlobalVarExistsFilter.php 0000777 00000002632 15252016106 0016112 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Filters; use Opis\JsonSchema\{ValidationContext, Filter, Schema}; class GlobalVarExistsFilter implements Filter { /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema, array $args = []): bool { $var = $args['var'] ?? $context->currentData(); if (!is_string($var)) { return false; } $globals = $context->globals(); if (!array_key_exists($var, $globals)) { return false; } if (array_key_exists('value', $args)) { return $globals[$var] == $args['value']; } return true; } } json-schema/src/Filters/CommonFilters.php 0000777 00000002533 15252016106 0014454 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Filters; final class CommonFilters { public static function Regex(string $value, array $args): bool { if (!isset($args['pattern']) || !is_string($args['pattern'])) { return false; } return (bool)preg_match($args['pattern'], $value); } public static function Equals($value, array $args): bool { if (!array_key_exists('value', $args)) { return false; } if ($args['strict'] ?? false) { return $value === $args['value']; } return $value == $args['value']; } } json-schema/src/Filters/FilterExistsFilter.php 0000777 00000003176 15252016106 0015472 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Filters; use Opis\JsonSchema\{ValidationContext, Filter, Schema}; class FilterExistsFilter implements Filter { /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema, array $args = []): bool { $filter = $args['filter'] ?? $context->currentData(); if (!is_string($filter)) { return false; } $type = null; if (isset($args['type'])) { if (!is_string($args['type'])) { return false; } $type = $args['type']; } $resolver = $context->loader()->parser()->getFilterResolver(); if (!$resolver) { return false; } if ($type === null) { return (bool)$resolver->resolveAll($filter); } return (bool)$resolver->resolve($filter, $type); } } json-schema/src/Filters/DateTimeFilters.php 0000777 00000005726 15252016106 0014727 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Filters; use DateTime, DateTimeZone; final class DateTimeFilters { public static function MinDate(string $date, array $args): bool { $min = $args['value']; $tz = $args['timezone'] ?? null; return self::CreateDate($date, $tz, false) >= self::CreateDate($min, $tz, false); } public static function MaxDate(string $date, array $args): bool { $max = $args['value']; $tz = $args['timezone'] ?? null; return self::CreateDate($date, $tz, false) <= self::CreateDate($max, $tz, false); } public static function NotDate(string $date, array $args): bool { $not = $args['value']; $tz = $args['timezone'] ?? null; if (!is_array($not)) { $not = [$not]; } $date = self::CreateDate($date, $tz, false); foreach ($not as $d) { if ($date == self::CreateDate($d, $tz, false)) { return false; } } return true; } public static function MinDateTime(string $date, array $args): bool { $min = $args['value']; $tz = $args['timezone'] ?? null; return self::CreateDate($date, $tz) >= self::CreateDate($min, $tz); } public static function MaxDateTime(string $date, array $args): bool { $max = $args['value']; $tz = $args['timezone'] ?? null; return self::CreateDate($date, $tz) <= self::CreateDate($max, $tz); } public static function MinTime(string $time, array $args): bool { $min = $args['value']; $prefix = '1970-01-01 '; return self::CreateDate($prefix . $time) >= self::CreateDate($prefix . $min); } public static function MaxTime(string $time, array $args): bool { $max = $args['value']; $prefix = '1970-01-01 '; return self::CreateDate($prefix . $time) <= self::CreateDate($prefix . $max); } private static function CreateDate(string $value, ?string $timezone = null, bool $time = true): DateTime { $date = new DateTime($value, $timezone ? new DateTimeZone($timezone) : null); if (!$time) { return $date->setTime(0, 0, 0, 0); } return $date; } } json-schema/src/Filters/SchemaExistsFilter.php 0000777 00000004734 15252016106 0015446 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Filters; use Opis\Uri\UriTemplate; use Opis\JsonSchema\{ValidationContext, Filter, Schema, Uri}; use Opis\JsonSchema\Variables\VariablesContainer; class SchemaExistsFilter implements Filter { /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema, array $args = []): bool { $ref = $args['ref'] ?? $context->currentData(); if (!is_string($ref)) { return false; } if (UriTemplate::isTemplate($ref)) { if (isset($args['vars']) && is_object($args['vars'])) { $vars = new VariablesContainer($args['vars'], false); $vars = $vars->resolve($context->rootData(), $context->currentDataPath()); if (!is_array($vars)) { $vars = (array)$vars; } $vars += $context->globals(); } else { $vars = $context->globals(); } $ref = (new UriTemplate($ref))->resolve($vars); unset($vars); } unset($args); return $this->refExists($ref, $context, $schema); } /** * @param string $ref * @param ValidationContext $context * @param Schema $schema * @return bool */ protected function refExists(string $ref, ValidationContext $context, Schema $schema): bool { if ($ref === '') { return false; } if ($ref === '#') { return true; } $info = $schema->info(); $id = Uri::merge($ref, $info->idBaseRoot(), true); if ($id === null) { return false; } return $context->loader()->loadSchemaById($id) !== null; } } json-schema/src/Filters/DataExistsFilter.php 0000777 00000002547 15252016106 0015117 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Filters; use Opis\JsonSchema\{ValidationContext, Filter, Schema, JsonPointer}; class DataExistsFilter implements Filter { /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema, array $args = []): bool { $ref = $args['ref'] ?? $context->currentData(); if (!is_string($ref)) { return false; } $ref = JsonPointer::parse($ref); if ($ref === null) { return false; } return $ref->data($context->rootData(), $context->currentDataPath(), $this) !== $this; } } json-schema/src/Filters/SlotExistsFilter.php 0000777 00000002302 15252016106 0015154 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Filters; use Opis\JsonSchema\{ValidationContext, Filter, Schema}; class SlotExistsFilter implements Filter { /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema, array $args = []): bool { $slot = $args['slot'] ?? $context->currentData(); if (!is_string($slot)) { return false; } return $context->slot($slot) !== null; } } json-schema/src/Errors/ValidationError.php 0000777 00000004345 15252016106 0014646 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Errors; use Opis\JsonSchema\Schema; use Opis\JsonSchema\Info\DataInfo; class ValidationError { protected string $keyword; protected Schema $schema; protected DataInfo $data; protected array $args; protected string $message; /** @var ValidationError[] */ protected array $subErrors; /** * @param string $keyword * @param Schema $schema * @param DataInfo $data * @param string $message * @param array $args * @param ValidationError[] $subErrors */ public function __construct( string $keyword, Schema $schema, DataInfo $data, string $message, array $args = [], array $subErrors = [] ) { $this->keyword = $keyword; $this->schema = $schema; $this->data = $data; $this->message = $message; $this->args = $args; $this->subErrors = $subErrors; } public function keyword(): string { return $this->keyword; } public function schema(): Schema { return $this->schema; } public function data(): DataInfo { return $this->data; } public function args(): array { return $this->args; } public function message(): string { return $this->message; } public function subErrors(): array { return $this->subErrors; } public function __toString(): string { return $this->message; } } json-schema/src/Errors/ErrorContainer.php 0000777 00000005777 15252016106 0014510 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Errors; use Countable, Iterator; class ErrorContainer implements Countable, Iterator { protected int $maxErrors; /** @var ValidationError[] */ protected array $errors = []; /** * ErrorContainer constructor. * @param int $max_errors */ public function __construct(int $max_errors = 1) { if ($max_errors < 0) { $max_errors = PHP_INT_MAX; } elseif ($max_errors === 0) { $max_errors = 1; } $this->maxErrors = $max_errors; } /** * @return int */ public function maxErrors(): int { return $this->maxErrors; } /** * @param ValidationError $error * @return ErrorContainer */ public function add(ValidationError $error): self { $this->errors[] = $error; return $this; } /** * @return ValidationError[] */ public function all(): array { return $this->errors; } /** * @return ValidationError|null */ public function first(): ?ValidationError { if (!$this->errors) { return null; } return reset($this->errors); } /** * @return bool */ public function isFull(): bool { return count($this->errors) >= $this->maxErrors; } /** * @return bool */ public function isEmpty(): bool { return !$this->errors; } /** * @inheritDoc */ public function count(): int { return count($this->errors); } /** * @inheritDoc */ public function current(): ?ValidationError { return current($this->errors) ?: null; } /** * @inheritDoc */ #[\ReturnTypeWillChange] public function next(): ?ValidationError { return next($this->errors) ?: null; } /** * @inheritDoc */ public function key(): ?int { return key($this->errors); } /** * @inheritDoc */ public function valid(): bool { return key($this->errors) !== null; } /** * @inheritDoc */ #[\ReturnTypeWillChange] public function rewind(): ?ValidationError { return reset($this->errors) ?: null; } } json-schema/src/Errors/CustomError.php 0000777 00000002111 15252016106 0014013 0 ustar 00 <?php /* ============================================================================ * Copyright 2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Errors; use Exception; class CustomError extends Exception { protected array $args; public function __construct(string $message, array $args = []) { parent::__construct($message); $this->args = $args; } public function getArgs(): array { return $this->args; } } json-schema/src/Errors/ErrorFormatter.php 0000777 00000030574 15252016106 0014522 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Errors; use Opis\JsonSchema\JsonPointer; class ErrorFormatter { /** * @param ValidationError $error * @param bool $multiple True if the same key can have multiple errors * @param ?callable(ValidationError,?string=null):mixed $formatter * @param ?callable(ValidationError):string $key_formatter * @return array */ public function format( ValidationError $error, bool $multiple = true, ?callable $formatter = null, ?callable $key_formatter = null ): array { if (!$formatter) { $formatter = [$this, 'formatErrorMessage']; } if (!$key_formatter) { $key_formatter = [$this, 'formatErrorKey']; } $list = []; /** * @var ValidationError $error * @var string $message */ foreach ($this->getErrors($error) as $error => $message) { $key = $key_formatter($error); if ($multiple) { if (!isset($list[$key])) { $list[$key] = []; } $list[$key][] = $formatter($error, $message); } else { if (!isset($list[$key])) { $list[$key] = $formatter($error, $message); } } } return $list; } /** * @param ValidationError|null $error * @param string $mode One of: flag, basic, detailed or verbose * @return array */ public function formatOutput(?ValidationError $error, string $mode = "flag"): array { if ($error === null) { return ['valid' => true]; } if ($mode === 'flag') { return ['valid' => false]; } if ($mode === 'basic') { return [ 'valid' => false, 'errors' => $this->formatFlat($error, [$this, 'formatOutputError']), ]; } if ($mode === 'detailed' || $mode === 'verbose') { $isVerbose = $mode === 'verbose'; return $this->getNestedErrors($error, function (ValidationError $error, ?array $subErrors = null) use ($isVerbose) { $info = $this->formatOutputError($error); $info['valid'] = false; if ($isVerbose) { $id = $error->schema()->info(); $id = $id->root() ?? $id->id(); if ($id) { $id = rtrim($id, '#'); } $info['absoluteKeywordLocation'] = $id . $info['keywordLocation']; } if ($subErrors) { $info['errors'] = $subErrors; if (!$isVerbose) { unset($info['error']); } } return $info; } ); } return ['valid' => false]; } /** * @param ValidationError $error * @param ?callable(ValidationError,?array):mixed $formatter * @return mixed */ public function formatNested(ValidationError $error, ?callable $formatter = null) { if (!$formatter) { $formatter = function (ValidationError $error, ?array $subErrors = null): array { $ret = [ 'message' => $this->formatErrorMessage($error), 'keyword' => $error->keyword(), 'path' => $this->formatErrorKey($error), ]; if ($subErrors) { $ret['errors'] = $subErrors; } return $ret; }; } return $this->getNestedErrors($error, $formatter); } /** * @param ValidationError $error * @param ?callable(ValidationError):mixed $formatter * @return array */ public function formatFlat(ValidationError $error, ?callable $formatter = null): array { if (!$formatter) { $formatter = [$this, 'formatErrorMessage']; } $list = []; foreach ($this->getFlatErrors($error) as $error) { $list[] = $formatter($error); } return $list; } /** * @param ValidationError $error * @param ?callable(ValidationError):mixed $formatter * @param ?callable(ValidationError):string $key_formatter * @return array */ public function formatKeyed( ValidationError $error, ?callable $formatter = null, ?callable $key_formatter = null ): array { if (!$formatter) { $formatter = [$this, 'formatErrorMessage']; } if (!$key_formatter) { $key_formatter = [$this, 'formatErrorKey']; } $list = []; foreach ($this->getLeafErrors($error) as $error) { $key = $key_formatter($error); if (!isset($list[$key])) { $list[$key] = []; } $list[$key][] = $formatter($error); } return $list; } /** * @param ValidationError $error * @param string|null $message The message to use, if null $error->message() is used * @return string */ public function formatErrorMessage(ValidationError $error, ?string $message = null): string { $message ??= $error->message(); $args = $this->getDefaultArgs($error) + $error->args(); if (!$args) { return $message; } return preg_replace_callback( '~{([^}]+)}~imu', static function (array $m) use ($args) { if (!array_key_exists($m[1], $args)) { return $m[0]; } $value = $args[$m[1]]; if (is_array($value)) { return implode(', ', $value); } return (string) $value; }, $message ); } public function formatErrorKey(ValidationError $error): string { return JsonPointer::pathToString($error->data()->fullPath()); } protected function getDefaultArgs(ValidationError $error): array { $data = $error->data(); $info = $error->schema()->info(); $path = $info->path(); $path[] = $error->keyword(); return [ 'data:type' => $data->type(), 'data:value' => $data->value(), 'data:path' => JsonPointer::pathToString($data->fullPath()), 'schema:id' => $info->id(), 'schema:root' => $info->root(), 'schema:base' => $info->base(), 'schema:draft' => $info->draft(), 'schema:keyword' => $error->keyword(), 'schema:path' => JsonPointer::pathToString($path), ]; } protected function formatOutputError(ValidationError $error): array { $path = $error->schema()->info()->path(); $path[] = $error->keyword(); return [ 'keywordLocation' => JsonPointer::pathToFragment($path), 'instanceLocation' => JsonPointer::pathToFragment($error->data()->fullPath()), 'error' => $this->formatErrorMessage($error), ]; } /** * @param ValidationError $error * @param callable(ValidationError,?array):mixed $formatter * @return mixed */ protected function getNestedErrors(ValidationError $error, callable $formatter) { if ($subErrors = $error->subErrors()) { foreach ($subErrors as &$subError) { $subError = $this->getNestedErrors($subError, $formatter); unset($subError); } } return $formatter($error, $subErrors); } /** * @param ValidationError $error * @return iterable|ValidationError[] */ protected function getFlatErrors(ValidationError $error): iterable { yield $error; foreach ($error->subErrors() as $subError) { yield from $this->getFlatErrors($subError); } } /** * @param ValidationError $error * @return iterable|ValidationError[] */ protected function getLeafErrors(ValidationError $error): iterable { if ($subErrors = $error->subErrors()) { foreach ($subErrors as $subError) { yield from $this->getLeafErrors($subError); } } else { yield $error; } } /** * @param ValidationError $error * @return iterable */ protected function getErrors(ValidationError $error): iterable { $data = $error->schema()->info()->data(); $map = null; $pMap = null; if (is_object($data)) { switch ($error->keyword()) { case 'required': if (isset($data->{'$error'}->required) && is_object($data->{'$error'}->required)) { $e = $data->{'$error'}->required; $found = false; foreach ($error->args()['missing'] as $prop) { if (isset($e->{$prop})) { yield $error => $e->{$prop}; $found = true; } } if ($found) { return; } if (isset($e->{'*'})) { yield $error => $e->{'*'}; return; } unset($e, $found, $prop); } break; case '$filters': if (($args = $error->args()) && isset($args['args']['$error'])) { yield $error => $args['args']['$error']; return; } unset($args); break; } if (isset($data->{'$error'})) { $map = $data->{'$error'}; if (is_string($map)) { // We have an global error yield $error => $map; return; } if (is_object($map)) { if (isset($map->{$error->keyword()})) { $pMap = $map->{'*'} ?? null; $map = $map->{$error->keyword()}; if (is_string($map)) { yield $error => $map; return; } } elseif (isset($map->{'*'})) { yield $error => $map->{'*'}; return; } } } } if (!is_object($map)) { $map = null; } $subErrors = $error->subErrors(); if (!$subErrors) { yield $error => $pMap ?? $error->message(); return; } if (!$map) { foreach ($subErrors as $subError) { yield from $this->getErrors($subError); } return; } foreach ($subErrors as $subError) { $path = $subError->data()->path(); if (count($path) !== 1) { yield from $this->getErrors($subError); } else { $path = $path[0]; if (isset($map->{$path})) { yield $subError => $map->{$path}; } elseif (isset($map->{'*'})) { yield $subError => $map->{'*'}; } else { yield from $this->getErrors($subError); } } } } } json-schema/src/ValidationResult.php 0000777 00000002612 15252016106 0013552 0 ustar 00 <?php /* ============================================================================ * Copyright 2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; use Opis\JsonSchema\Errors\ValidationError; class ValidationResult { protected ?ValidationError $error; public function __construct(?ValidationError $error) { $this->error = $error; } public function error(): ?ValidationError { return $this->error; } public function isValid(): bool { return $this->error === null; } public function hasError(): bool { return $this->error !== null; } public function __toString(): string { if ($this->error) { return $this->error->message(); } return ''; } } json-schema/src/Helper.php 0000777 00000022703 15252016106 0011503 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; final class Helper { /** @var string[] */ public const JSON_TYPES = ['string', 'number', 'boolean', 'null', 'object', 'array']; /** @var string[] */ public const JSON_SUBTYPES = ['integer' => 'number']; /** @var string[] */ protected const PHP_TYPE_MAP = [ 'NULL' => 'null', 'integer' => 'integer', 'double' => 'number', 'boolean' => 'boolean', 'array' => 'array', 'object' => 'object', 'string' => 'string', ]; /** * @param string $type * @return bool */ public static function isValidJsonType(string $type): bool { if (isset(self::JSON_SUBTYPES[$type])) { return true; } return in_array($type, self::JSON_TYPES, true); } /** * @param string $type * @return null|string */ public static function getJsonSuperType(string $type): ?string { return self::JSON_SUBTYPES[$type] ?? null; } /** * @param mixed $value * @param bool $use_subtypes * @return null|string */ public static function getJsonType($value, bool $use_subtypes = true): ?string { $type = self::PHP_TYPE_MAP[gettype($value)] ?? null; if ($type === null) { return null; } elseif ($type === 'array') { return self::isIndexedArray($value) ? 'array' : null; } if ($use_subtypes) { if ($type === 'number' && self::isMultipleOf($value, 1)) { return 'integer'; } } elseif ($type === 'integer') { return 'number'; } return $type; } /** * @param string $type * @param string|string[] $allowed * @return bool */ public static function jsonTypeMatches(string $type, $allowed): bool { if (!$allowed) { return false; } if (is_string($allowed)) { if ($type === $allowed) { return true; } return $allowed === self::getJsonSuperType($type); } if (is_array($allowed)) { if (in_array($type, $allowed, true)) { return true; } if ($type = self::getJsonSuperType($type)) { return in_array($type, $allowed, true); } } return false; } /** * @param mixed $value * @param string|string[] $type * @return bool */ public static function valueIsOfJsonType($value, $type): bool { $t = self::getJsonType($value); if ($t === null) { return false; } return self::jsonTypeMatches($t, $type); } /** * @param array $array * @return bool */ public static function isIndexedArray(array $array): bool { for ($i = 0, $max = count($array); $i < $max; $i++) { if (!array_key_exists($i, $array)) { return false; } } return true; } /** * Converts assoc-arrays to objects (recursive) * @param scalar|object|array|null $schema * @return scalar|object|array|null */ public static function convertAssocArrayToObject($schema) { if (is_null($schema) || is_scalar($schema)) { return $schema; } $keepArray = is_array($schema) && self::isIndexedArray($schema); $data = []; foreach ($schema as $key => $value) { $data[$key] = is_array($value) || is_object($value) ? self::convertAssocArrayToObject($value) : $value; } return $keepArray ? $data : (object) $data; } /** * @param mixed $a * @param mixed $b * @return bool */ public static function equals($a, $b): bool { if ($a === $b) { return true; } $type = self::getJsonType($a, false); if ($type === null || $type !== self::getJsonType($b, false)) { return false; } if ($type === 'number') { return $a == $b; } if ($type === "array") { $count = count($a); if ($count !== count($b)) { return false; } for ($i = 0; $i < $count; $i++) { if (!array_key_exists($i, $a) || !array_key_exists($i, $b)) { return false; } if (!self::equals($a[$i], $b[$i])) { return false; } } return true; } if ($type === "object") { $a = get_object_vars($a); if ($a === null) { return false; } $b = get_object_vars($b); if ($b === null) { return false; } if (count($a) !== count($b)) { return false; } foreach ($a as $prop => $value) { if (!array_key_exists($prop, $b)) { return false; } if (!self::equals($value, $b[$prop])) { return false; } } return true; } return false; } /** * @var bool|null True if bcmath extension is available */ private static ?bool $hasBCMath = null; /** * @var bool True to use bcmath */ public static bool $useBCMath = true; /** * @var int Number scale to used when using comparisons */ public static int $numberScale = 14; /** * @param $number * @param $divisor * @param int|null $scale * @return bool */ public static function isMultipleOf($number, $divisor, ?int $scale = null): bool { if (!$number || $number == $divisor) { return true; } if ($divisor == 0) { return $number == 0; } // maybe we get lucky if (!fmod($number, $divisor)) { return true; } $divisor = abs($divisor); if ($divisor == 1) { // should have been caught by the above fmod() return false; } $number = abs($number); if ($divisor > $number) { // number / divisor < 1 return false; } // Use global scale if null $scale ??= self::$numberScale; if ( !self::$useBCMath || !(self::$hasBCMath ??= extension_loaded('bcmath')) ) { // use an approximation return abs($number - round($number / $divisor) * $divisor) < (10 ** -$scale); } // use bcmath $number = number_format($number, $scale, '.', ''); $divisor = number_format($divisor, $scale, '.', ''); // number can be zero after formatting if (!(float)$divisor) { return $number === $divisor; } $x = bcdiv($number, $divisor, 0); $x = bcmul($divisor, $x, $scale); $x = bcsub($number, $x, $scale); return 0 === bccomp($x, 0, $scale); } /** * @param $value * @return mixed */ public static function cloneValue($value) { if ($value === null || is_scalar($value)) { return $value; } if (is_array($value)) { return array_map(self::class . '::cloneValue', $value); } if (is_object($value)) { return (object)array_map(self::class . '::cloneValue', get_object_vars($value)); } return null; } /** * @param string $pattern * @return bool */ public static function isValidPattern(string $pattern): bool { if (strpos($pattern, '\Z') !== false) { return false; } return @preg_match("\x07{$pattern}\x07u", '') !== false; } /** * @param string $pattern * @return string */ public static function patternToRegex(string $pattern): string { return "\x07{$pattern}\x07uD"; } /** * @param mixed $data * @return mixed */ public static function toJSON($data) { if ($data === null || is_scalar($data)) { return $data; } $map = []; $isArray = true; $index = 0; foreach ($data as $key => $value) { $map[$key] = self::toJSON($value); if ($isArray) { if ($index !== $key) { $isArray = false; } else { $index++; } } } if ($isArray) { if (!$map && is_object($data)) { return (object) $map; } return $map; } return (object) $map; } } json-schema/src/KeywordValidator.php 0000777 00000002122 15252016106 0013547 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; interface KeywordValidator extends SchemaValidator { /** * @return KeywordValidator|null */ public function next(): ?KeywordValidator; /** * @param KeywordValidator|null $next * @return KeywordValidator */ public function setNext(?KeywordValidator $next): KeywordValidator; } json-schema/src/Variables.php 0000777 00000001713 15252016106 0012172 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; interface Variables { /** * @param array|object $data * @param string[]|int[] $path * @return mixed */ public function resolve($data, array $path = []); } json-schema/src/Formats/IriFormats.php 0000777 00000007113 15252016106 0013754 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Formats; use Throwable; use Opis\JsonSchema\Uri; class IriFormats { private const SKIP = [0x23, 0x26, 0x2F, 0x3A, 0x3D, 0x3F, 0x40, 0x5B, 0x5C, 0x5D]; /** @var bool|null|callable */ private static $idn = false; /** * @param string $value * @return bool */ public static function iri(string $value): bool { if ($value === '') { return false; } try { $components = Uri::parseComponents(Uri::encodeComponent($value, self::SKIP), true, true); } catch (Throwable $e) { return false; } return isset($components['scheme']) && $components['scheme'] !== ''; } /** * @param string $value * @return bool */ public static function iriReference(string $value): bool { if ($value === '') { return true; } try { return Uri::parseComponents(Uri::encodeComponent($value, self::SKIP), true, true) !== null; } catch (Throwable $e) { return false; } } /** * @param string $value * @param callable|null $idn * @return bool */ public static function idnHostname(string $value, ?callable $idn = null): bool { $idn = $idn ?? static::idn(); if ($idn) { $value = $idn($value); if ($value === null) { return false; } } return Uri::isValidHost($value); } /** * @param string $value * @param callable|null $idn * @return bool */ public static function idnEmail(string $value, ?callable $idn = null): bool { $idn = $idn ?? static::idn(); if ($idn) { if (!preg_match('/^(?<name>.+)@(?<domain>.+)$/u', $value, $m)) { return false; } $m['name'] = $idn($m['name']); if ($m['name'] === null) { return false; } $m['domain'] = $idn($m['domain']); if ($m['domain'] === null) { return false; } $value = $m['name'] . '@' . $m['domain']; } return filter_var($value, FILTER_VALIDATE_EMAIL) !== false; } /** * @return callable|null */ public static function idn(): ?callable { if (static::$idn === false) { if (function_exists('idn_to_ascii')) { static::$idn = static function (string $value): ?string { /** @noinspection PhpComposerExtensionStubsInspection */ $value = idn_to_ascii($value, 0, INTL_IDNA_VARIANT_UTS46); return is_string($value) ? $value : null; }; } else { static::$idn = null; } } return static::$idn; } } json-schema/src/Formats/MiscFormats.php 0000777 00000003275 15252016106 0014131 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Formats; class MiscFormats { const UUID_REGEX = '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i'; /** * @param string $value * @return bool */ public static function ipv4(string $value): bool { return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false; } /** * @param string $value * @return bool */ public static function ipv6(string $value): bool { return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false; } /** * @param string $value * @return bool */ public static function email(string $value): bool { return filter_var($value, FILTER_VALIDATE_EMAIL) !== false; } /** * @param string $value * @return bool */ public static function uuid(string $value): bool { return (bool) preg_match(self::UUID_REGEX, $value); } } json-schema/src/Formats/DateTimeFormats.php 0000777 00000004450 15252016106 0014726 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Formats; class DateTimeFormats { const DATE_REGEX = '/^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/i'; const TIME_REGEX = '/^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))?$/i'; const DATE_TIME_REGEX = '/^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))?$/i'; const DURATION_REGEX = '/^P?((((\d+D)|((\d+M(\d+D)?)|(\d+Y(\d+M(\d+D)?)?)))(T((\d+S)|(\d+M(\d+S)?)|(\d+H(\d+M(\d+S)?)?)))?)|(T((\d+S)|(\d+M(\d+S)?)|(\d+H(\d+M(\d+S)?)?)))|(\d+W))$/i'; /** * @param string $value * @return bool */ public static function date(string $value): bool { if (preg_match(self::DATE_REGEX, $value, $m)) { return checkdate($m[2], $m[3], $m[1]); } return false; } /** * @param string $value * @return bool */ public static function time(string $value): bool { return (bool)preg_match(self::TIME_REGEX, $value); } /** * @param string $value * @return bool */ public static function dateTime(string $value): bool { if (preg_match(self::DATE_TIME_REGEX, $value, $m)) { return checkdate($m[2], $m[3], $m[1]); } return false; } /** * @param string $value * @return bool */ public static function duration(string $value): bool { return (bool) preg_match(self::DURATION_REGEX, $value); } } json-schema/src/Formats/UriFormats.php 0000777 00000003277 15252016106 0013777 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Formats; use Opis\JsonSchema\Uri; use Opis\Uri\UriTemplate; class UriFormats { /** * @param string $value * @return bool */ public static function uri(string $value): bool { if ($value === '') { return false; } $uri = Uri::parse($value); return $uri !== null && $uri->isAbsolute(); } /** * @param string $value * @return bool */ public static function uriReference(string $value): bool { if ($value === '') { return true; } return Uri::parse($value) !== null; } /** * @param string $value * @return bool */ public static function uriTemplate(string $value): bool { if ($value === '') { return true; } if (UriTemplate::isTemplate($value)) { return true; } return Uri::parse($value) !== null; } } json-schema/src/SchemaValidator.php 0000777 00000002010 15252016106 0013317 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; use Opis\JsonSchema\Errors\ValidationError; interface SchemaValidator { /** * @param ValidationContext $context * @return null|ValidationError */ public function validate(ValidationContext $context): ?ValidationError; } json-schema/src/Exceptions/UnresolvedContentEncodingException.php 0000777 00000002644 15252016106 0021416 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Exceptions; use Opis\JsonSchema\{ValidationContext, Schema}; class UnresolvedContentEncodingException extends UnresolvedException { protected string $encoding; /** * @param string $encoding * @param Schema $schema * @param ValidationContext $context */ public function __construct(string $encoding, Schema $schema, ValidationContext $context) { parent::__construct("Cannot resolve '{$encoding}' content encoding", $schema, $context); $this->encoding = $encoding; } /** * @return string */ public function getContentEncoding(): string { return $this->encoding; } } json-schema/src/Exceptions/UnresolvedContentMediaTypeException.php 0000777 00000002623 15252016106 0021546 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Exceptions; use Opis\JsonSchema\{ValidationContext, Schema}; class UnresolvedContentMediaTypeException extends UnresolvedException { protected string $media; /** * @param string $media * @param Schema $schema * @param ValidationContext $context */ public function __construct(string $media, Schema $schema, ValidationContext $context) { parent::__construct("Cannot resolve '{$media}' content media type", $schema, $context); $this->media = $media; } /** * @return string */ public function getContentMediaType(): string { return $this->media; } } json-schema/src/Exceptions/ParseException.php 0000777 00000002510 15252016106 0015330 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Exceptions; use RuntimeException; use Opis\JsonSchema\Info\SchemaInfo; class ParseException extends RuntimeException implements SchemaException { protected ?SchemaInfo $info = null; /** * @param string $message * @param SchemaInfo|null $info */ public function __construct(string $message, ?SchemaInfo $info = null) { parent::__construct($message, 0); $this->info = $info; } /** * @return SchemaInfo|null */ public function schemaInfo(): ?SchemaInfo { return $this->info; } } json-schema/src/Exceptions/InvalidKeywordException.php 0000777 00000002554 15252016106 0017221 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Exceptions; use Opis\JsonSchema\Info\SchemaInfo; class InvalidKeywordException extends ParseException { protected string $keyword; /** * InvalidKeywordException constructor. * @param string $message * @param string $keyword * @param SchemaInfo|null $info */ public function __construct(string $message, string $keyword, ?SchemaInfo $info = null) { parent::__construct($message, $info); $this->keyword = $keyword; } /** * @return string */ public function keyword(): string { return $this->keyword; } } json-schema/src/Exceptions/UnresolvedFilterException.php 0000777 00000003146 15252016106 0017560 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Exceptions; use Opis\JsonSchema\{ValidationContext, Schema}; class UnresolvedFilterException extends UnresolvedException { protected string $filter; protected string $type; /** * @param string $filter * @param string $type * @param Schema $schema * @param ValidationContext $context */ public function __construct(string $filter, string $type, Schema $schema, ValidationContext $context) { parent::__construct("Cannot resolve filter '{$filter}' for type '{$type}'", $schema, $context); $this->filter = $filter; $this->type = $type; } /** * @return string */ public function getFilter(): string { return $this->filter; } /** * @return string */ public function getType(): string { return $this->type; } } json-schema/src/Exceptions/DuplicateSchemaIdException.php 0000777 00000003000 15252016106 0017561 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Exceptions; use RuntimeException; use Opis\JsonSchema\Uri; class DuplicateSchemaIdException extends RuntimeException implements SchemaException { protected Uri $id; protected ?object $data = null; /** * DuplicateSchemaIdException constructor. * @param Uri $id * @param object|null $data */ public function __construct(Uri $id, ?object $data = null) { parent::__construct("Duplicate schema id: {$id}", 0); $this->id = $id; $this->data = $data; } /** * @return null|object */ public function getData(): ?object { return $this->data; } /** * @return Uri */ public function getId(): Uri { return $this->id; } }json-schema/src/Exceptions/UnresolvedReferenceException.php 0000777 00000002544 15252016106 0020232 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Exceptions; use Opis\JsonSchema\{ValidationContext, Schema}; class UnresolvedReferenceException extends UnresolvedException { protected string $ref; /** * @param string $ref * @param Schema $schema * @param ValidationContext $context */ public function __construct(string $ref, Schema $schema, ValidationContext $context) { parent::__construct("Unresolved reference: {$ref}", $schema, $context); $this->ref = $ref; } /** * @return string */ public function getRef(): string { return $this->ref; } } json-schema/src/Exceptions/UnresolvedException.php 0000777 00000003126 15252016106 0016410 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Exceptions; use RuntimeException; use Opis\JsonSchema\{ValidationContext, Schema}; class UnresolvedException extends RuntimeException implements SchemaException { protected Schema $schema; protected ValidationContext $context; /** * @param string $message * @param Schema $schema * @param ValidationContext $context */ public function __construct(string $message, Schema $schema, ValidationContext $context) { parent::__construct($message); $this->schema = $schema; $this->context = $context; } /** * @return Schema */ public function getSchema(): Schema { return $this->schema; } /** * @return ValidationContext */ public function getContext(): ValidationContext { return $this->context; } } json-schema/src/Exceptions/InvalidPragmaException.php 0000777 00000002566 15252016106 0017007 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Exceptions; use Opis\JsonSchema\Info\SchemaInfo; class InvalidPragmaException extends InvalidKeywordException { protected string $pragma; /** * InvalidPragmaException constructor. * @param string $message * @param string $pragma * @param SchemaInfo|null $info */ public function __construct(string $message, string $pragma, ?SchemaInfo $info = null) { parent::__construct($message, '$pragma', $info); $this->pragma = $pragma; } /** * @return string */ public function pragma(): string { return $this->pragma; } } json-schema/src/Exceptions/SchemaException.php 0000777 00000001540 15252016106 0015460 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Exceptions; use Throwable; interface SchemaException extends Throwable { } json-schema/src/Schema.php 0000777 00000001671 15252016106 0011465 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; use Opis\JsonSchema\Info\SchemaInfo; interface Schema extends SchemaValidator { /** * @return SchemaInfo */ public function info(): SchemaInfo; } json-schema/src/CompliantValidator.php 0000777 00000003600 15252016106 0014053 0 ustar 00 <?php /* ============================================================================ * Copyright 2021 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; class CompliantValidator extends Validator { protected const COMPLIANT_OPTIONS = [ 'allowFilters' => false, 'allowFormats' => true, 'allowMappers' => false, 'allowTemplates' => false, 'allowGlobals' => false, 'allowDefaults' => false, 'allowSlots' => false, 'allowKeywordValidators' => false, 'allowPragmas' => false, 'allowDataKeyword' => false, 'allowKeywordsAlongsideRef' => false, 'allowUnevaluated' => true, 'allowRelativeJsonPointerInRef' => false, 'allowExclusiveMinMaxAsBool' => false, 'keepDependenciesKeyword' => false, 'keepAdditionalItemsKeyword' => false, ]; public function __construct( ?SchemaLoader $loader = null, int $max_errors = 1, bool $stop_at_first_error = true ) { parent::__construct($loader, $max_errors, $stop_at_first_error); // Set parser options $parser = $this->parser(); foreach (static::COMPLIANT_OPTIONS as $name => $value) { $parser->setOption($name, $value); } } } json-schema/src/Uri.php 0000777 00000005001 15252016106 0011013 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; use JsonSerializable; use Opis\Uri\Uri as BaseUri; class Uri extends BaseUri implements JsonSerializable { /** * @var bool Set this to true and the qs will always be sorted */ protected static bool $useNormalizedComponents = false; public function __construct(array $components) { if (static::$useNormalizedComponents) { $components = self::normalizeComponents($components); } parent::__construct($components); } /** * @inheritDoc */ public function jsonSerialize(): string { return $this->__toString(); } /** * @param string $uri * @param bool $ensure_fragment * @return static|null */ public static function parse(string $uri, bool $ensure_fragment = false): ?self { if ($ensure_fragment && strpos($uri, '#') === false) { $uri .= '#'; } return self::create($uri); } /** * @param string|array|static $uri * @param string|array|static $base * @param bool $ensure_fragment * @return static|null */ public static function merge($uri, $base, bool $ensure_fragment = false): ?self { $uri = self::resolveComponents($uri); if ($uri === null) { return null; } if ($ensure_fragment && !isset($uri['fragment'])) { $uri['fragment'] = ''; } $base = self::resolveComponents($base); if (!$base) { return new self($uri); } return new self(self::mergeComponents($uri, $base)); } /** * @param bool $value */ public static function useNormalizedComponents(bool $value): void { self::$useNormalizedComponents = $value; } } json-schema/src/Keywords/MaxLengthKeyword.php 0000777 00000003125 15252016106 0015324 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class MaxLengthKeyword implements Keyword { use ErrorTrait; protected int $length; /** * @param int $length */ public function __construct(int $length) { $this->length = $length; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $length = $context->getStringLength(); if ($length <= $this->length) { return null; } return $this->error($schema, $context, 'maxLength', "Maximum string length is {max}, found {length}", [ 'max' => $this->length, 'length' => $length, ]); } } json-schema/src/Keywords/ErrorTrait.php 0000777 00000003503 15252016106 0014165 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\Info\DataInfo; use Opis\JsonSchema\{ValidationContext, Schema}; use Opis\JsonSchema\Errors\{ErrorContainer, ValidationError}; trait ErrorTrait { /** * @param Schema $schema * @param ValidationContext $context * @param string $keyword * @param string $message * @param array $args * @param ErrorContainer|ValidationError|ValidationError[]|null $errors * @return ValidationError */ protected function error( Schema $schema, ValidationContext $context, string $keyword, string $message, array $args = [], $errors = null ): ValidationError { if ($errors) { if ($errors instanceof ValidationError) { $errors = [$errors]; } elseif ($errors instanceof ErrorContainer) { $errors = $errors->all(); } } return new ValidationError($keyword, $schema, DataInfo::fromContext($context), $message, $args, is_array($errors) ? $errors : []); } } json-schema/src/Keywords/DefaultKeyword.php 0000777 00000003060 15252016106 0015017 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{Helper, ValidationContext, Keyword, Schema}; use Opis\JsonSchema\Errors\ValidationError; class DefaultKeyword implements Keyword { protected array $defaults; /** * @param array $defaults */ public function __construct(array $defaults) { $this->defaults = $defaults; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $data = $context->currentData(); if (is_object($data)) { foreach ($this->defaults as $name => $value) { if (!property_exists($data, $name)) { $data->{$name} = Helper::cloneValue($value); } } } return null; } } json-schema/src/Keywords/RecursiveRefKeyword.php 0000777 00000007764 15252016106 0016056 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\Exceptions\UnresolvedReferenceException; use Opis\JsonSchema\{Schema, ValidationContext, Variables, Uri}; class RecursiveRefKeyword extends AbstractRefKeyword { protected Uri $uri; /** @var bool|null|Schema */ protected $resolved = false; protected string $anchor; protected $anchorValue; public function __construct( Uri $uri, ?Variables $mapper, ?Variables $globals, ?array $slots = null, string $keyword = '$recursiveRef', string $anchor = '$recursiveAnchor', $anchorValue = true ) { parent::__construct($mapper, $globals, $slots, $keyword); $this->uri = $uri; $this->anchor = $anchor; $this->anchorValue = $anchorValue; } /** * @inheritDoc */ public function doValidate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->resolved === false) { $this->resolved = $context->loader()->loadSchemaById($this->uri); } if ($this->resolved === null) { throw new UnresolvedReferenceException((string)$this->uri, $schema, $context); } $new_context = $this->createContext($context, $schema); if (!$this->hasRecursiveAnchor($this->resolved)) { $this->setLastRefSchema($this->resolved); return $this->resolved->validate($new_context); } $ok_sender = $this->resolveSchema($context); if (!$ok_sender) { $this->setLastRefSchema($this->resolved); return $this->resolved->validate($new_context); } $this->setLastRefSchema($ok_sender); return $ok_sender->validate($new_context); } protected function resolveSchema(ValidationContext $context): ?Schema { $ok = null; $loader = $context->loader(); while ($context) { $sender = $context->sender(); if (!$sender) { break; } if (!$this->hasRecursiveAnchor($sender)) { if ($sender->info()->id()) { // id without recursiveAnchor break; } $sender = $loader->loadSchemaById($sender->info()->root()); if (!$sender || !$this->hasRecursiveAnchor($sender)) { // root without recursiveAnchor break; } } if ($sender->info()->id()) { // id with recursiveAnchor $ok = $sender; } else { // root with recursiveAnchor $ok = $loader->loadSchemaById($sender->info()->root()); } $context = $context->parent(); } return $ok; } protected function hasRecursiveAnchor(?Schema $schema): bool { if (!$schema) { return false; } $info = $schema->info(); if (!$info->isObject()) { return false; } $data = $info->data(); if (!property_exists($data, $this->anchor)) { return false; } return $data->{$this->anchor} === $this->anchorValue; } } json-schema/src/Keywords/MaxPropertiesKeywords.php 0000777 00000003150 15252016106 0016420 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class MaxPropertiesKeywords implements Keyword { use ErrorTrait; protected int $count; /** * @param int $count */ public function __construct(int $count) { $this->count = $count; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $count = count($context->getObjectProperties()); if ($count <= $this->count) { return null; } return $this->error($schema, $context, 'maxProperties', "Object must have at most {max} properties, {count} found", [ 'max' => $this->count, 'count' => $count, ]); } } json-schema/src/Keywords/UnevaluatedItemsKeyword.php 0000777 00000004515 15252016106 0016720 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\{Keyword, Schema, ValidationContext}; class UnevaluatedItemsKeyword implements Keyword { use OfTrait; use IterableDataValidationTrait; protected $value; public function __construct($value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $unevaluated = $context->getUnevaluatedItems(); if (!$unevaluated) { return null; } if ($this->value === true) { $context->addEvaluatedItems($unevaluated); return null; } if ($this->value === false) { return $this->error($schema, $context, 'unevaluatedItems', 'Unevaluated array items are not allowed: {indexes}', [ 'indexes' => $unevaluated, ]); } if (is_object($this->value) && !($this->value instanceof Schema)) { $this->value = $context->loader()->loadObjectSchema($this->value); } $object = $this->createArrayObject($context); $error = $this->validateIterableData($schema, $this->value, $context, $unevaluated, 'unevaluatedItems', 'All unevaluated array items must match schema: {indexes}', [ 'indexes' => $unevaluated, ], $object); if ($object && $object->count()) { $context->addEvaluatedItems($object->getArrayCopy()); } return $error; } } json-schema/src/Keywords/ContentMediaTypeKeyword.php 0000777 00000004646 15252016106 0016662 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema, ContentMediaType }; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\Resolvers\ContentMediaTypeResolver; use Opis\JsonSchema\Exceptions\UnresolvedContentMediaTypeException; class ContentMediaTypeKeyword implements Keyword { use ErrorTrait; protected string $name; /** @var bool|callable|ContentMediaType|null */ protected $media = false; protected ?ContentMediaTypeResolver $resolver; /** * @param string $name * @param null|ContentMediaTypeResolver $resolver */ public function __construct(string $name, ?ContentMediaTypeResolver $resolver) { $this->name = $name; $this->resolver = $resolver; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if (!$this->resolver) { return null; } if ($this->media === false) { $this->media = $this->resolver->resolve($this->name); } if ($this->media === null) { throw new UnresolvedContentMediaTypeException($this->name, $schema, $context); } $data = $context->getDecodedContent(); $ok = $this->media instanceof ContentMediaType ? $this->media->validate($data, $this->name) : ($this->media)($data, $this->name); if ($ok) { return null; } unset($data); return $this->error($schema, $context, 'contentMediaType', "The media type of the data must be '{media}'", [ 'media' => $this->name, ]); } } json-schema/src/Keywords/IterableDataValidationTrait.php 0000777 00000006416 15252016106 0017436 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use ArrayObject; use Opis\JsonSchema\{ValidationContext, Schema}; use Opis\JsonSchema\Errors\{ValidationError, ErrorContainer}; trait IterableDataValidationTrait { use ErrorTrait; /** * @param int $maxErrors * @return ErrorContainer */ protected function errorContainer(int $maxErrors = 1): ErrorContainer { return new ErrorContainer($maxErrors); } /** * @param Schema $schema * @param ValidationContext $context * @param iterable $iterator * @param ArrayObject|null $keys * @return ErrorContainer */ protected function iterateAndValidate( Schema $schema, ValidationContext $context, iterable $iterator, ?ArrayObject $keys = null ): ErrorContainer { $container = $this->errorContainer($context->maxErrors()); if ($keys) { foreach ($iterator as $key) { $context->pushDataPath($key); $error = $schema->validate($context); $context->popDataPath(); if ($error) { if (!$container->isFull()) { $container->add($error); } } else { $keys[] = $key; } } } else { foreach ($iterator as $key) { $context->pushDataPath($key); $error = $schema->validate($context); $context->popDataPath(); if ($error && $container->add($error)->isFull()) { break; } } } return $container; } /** * @param Schema $parentSchema * @param Schema $schema * @param ValidationContext $context * @param iterable $iterator * @param string $keyword * @param string $message * @param array $args * @param ArrayObject|null $visited_keys * @return ValidationError|null */ protected function validateIterableData( Schema $parentSchema, Schema $schema, ValidationContext $context, iterable $iterator, string $keyword, string $message, array $args = [], ?ArrayObject $visited_keys = null ): ?ValidationError { $errors = $this->iterateAndValidate($schema, $context, $iterator, $visited_keys); if ($errors->isEmpty()) { return null; } return $this->error($parentSchema, $context, $keyword, $message, $args, $errors); } } json-schema/src/Keywords/AnyOfKeyword.php 0000777 00000005104 15252016106 0014450 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class AnyOfKeyword implements Keyword { use OfTrait; use ErrorTrait; /** @var bool[]|object[] */ protected array $value; protected bool $alwaysValid; /** * @param bool[]|object[] $value */ public function __construct(array $value, bool $alwaysValid = false) { $this->value = $value; $this->alwaysValid = $alwaysValid; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $object = $this->createArrayObject($context); if ($this->alwaysValid && !$object) { return null; } $errors = []; $ok = false; foreach ($this->value as $index => $value) { if ($value === true) { $ok = true; if ($object) { continue; } return null; } if ($value === false) { continue; } if (is_object($value) && !($value instanceof Schema)) { $value = $this->value[$index] = $context->loader()->loadObjectSchema($value); } if ($error = $context->validateSchemaWithoutEvaluated($value, null, false, $object)) { $errors[] = $error; continue; } if (!$object) { return null; } $ok = true; } $this->addEvaluatedFromArrayObject($object, $context); if ($ok) { return null; } return $this->error($schema, $context, 'anyOf', 'The data should match at least one schema', [], $errors); } } json-schema/src/Keywords/ExclusiveMaximumDataKeyword.php 0000777 00000003376 15252016106 0017544 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class ExclusiveMaximumDataKeyword extends ExclusiveMaximumKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var float|int $number */ $number = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($number === $this || !(is_float($number) || is_int($number))) { return $this->error($schema, $context, 'exclusiveMaximum', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->number = $number; return parent::validate($context, $schema); } } json-schema/src/Keywords/PatternKeyword.php 0000777 00000003143 15252016106 0015052 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{Helper, ValidationContext, Keyword, Schema}; use Opis\JsonSchema\Errors\ValidationError; class PatternKeyword implements Keyword { use ErrorTrait; protected ?string $pattern; protected ?string $regex; /** * @param string $pattern */ public function __construct(string $pattern) { $this->pattern = $pattern; $this->regex = Helper::patternToRegex($pattern); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if (preg_match($this->regex, $context->currentData())) { return null; } return $this->error($schema, $context, 'pattern', "The string should match pattern: {pattern}", [ 'pattern' => $this->pattern, ]); } } json-schema/src/Keywords/MaxItemsDataKeyword.php 0000777 00000003321 15252016106 0015754 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class MaxItemsDataKeyword extends MaxItemsKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var int $count */ $count = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($count === $this || !is_int($count) || $count < 0) { return $this->error($schema, $context, 'maxItems', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->count = $count; return parent::validate($context, $schema); } } json-schema/src/Keywords/OfTrait.php 0000777 00000002776 15252016106 0013453 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use ArrayObject; use Opis\JsonSchema\ValidationContext; trait OfTrait { protected function createArrayObject(ValidationContext $context): ?ArrayObject { return $context->trackUnevaluated() ? new ArrayObject() : null; } protected function addEvaluatedFromArrayObject(?ArrayObject $object, ValidationContext $context): void { if (!$object || !$object->count()) { return; } foreach ($object as $value) { if (isset($value['properties'])) { $context->addEvaluatedProperties($value['properties']); } if (isset($value['items'])) { $context->addEvaluatedItems($value['items']); } } } } json-schema/src/Keywords/MaximumDataKeyword.php 0000777 00000003343 15252016106 0015646 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class MaximumDataKeyword extends MaximumKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var float|int $number */ $number = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($number === $this || !(is_float($number) || is_int($number))) { return $this->error($schema, $context, 'maximum', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->number = $number; return parent::validate($context, $schema); } } json-schema/src/Keywords/PropertiesKeyword.php 0000777 00000006147 15252016106 0015600 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class PropertiesKeyword implements Keyword { use IterableDataValidationTrait; protected array $properties; protected array $propertyKeys; /** * @param array $properties */ public function __construct(array $properties) { $this->properties = $properties; $this->propertyKeys = array_keys($properties); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if (!$this->properties) { return null; } $checked = []; $evaluated = []; $data = $context->currentData(); $errors = $this->errorContainer($context->maxErrors()); foreach ($this->properties as $name => $prop) { if (!property_exists($data, $name)) { continue; } $checked[] = $name; if ($prop === true) { $evaluated[] = $name; continue; } if ($prop === false) { $context->addEvaluatedProperties($evaluated); return $this->error($schema, $context, 'properties', "Property '{property}' is not allowed", [ 'property' => $name, ]); } if (is_object($prop) && !($prop instanceof Schema)) { $prop = $this->properties[$name] = $context->loader()->loadObjectSchema($prop); } $context->pushDataPath($name); $error = $prop->validate($context); $context->popDataPath(); if ($error) { $errors->add($error); if ($errors->isFull()) { break; } } else { $evaluated[] = $name; } } $context->addEvaluatedProperties($evaluated); if (!$errors->isEmpty()) { return $this->error($schema, $context, 'properties', "The properties must match schema: {properties}", [ 'properties' => array_values(array_diff($checked, $evaluated)) ], $errors); } unset($errors); $context->addCheckedProperties($checked); return null; } } json-schema/src/Keywords/MinLengthDataKeyword.php 0000777 00000003366 15252016106 0016123 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class MinLengthDataKeyword extends MinLengthKeyword { /** @var JsonPointer */ protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var int $length */ $length = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($length === $this || !is_int($length) || $length < 0) { return $this->error($schema, $context, 'minLength', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->length = $length; return parent::validate($context, $schema); } } json-schema/src/Keywords/ExclusiveMaximumKeyword.php 0000777 00000002762 15252016106 0016750 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Keyword, Schema}; use Opis\JsonSchema\Errors\ValidationError; class ExclusiveMaximumKeyword implements Keyword { use ErrorTrait; protected float $number; /** * @param float $number */ public function __construct(float $number) { $this->number = $number; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($context->currentData() < $this->number) { return null; } return $this->error($schema, $context, 'exclusiveMaximum', "Number must be lower than {max}", [ 'max' => $this->number, ]); } } json-schema/src/Keywords/UniqueItemsKeyword.php 0000777 00000003262 15252016106 0015707 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ Helper, ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class UniqueItemsKeyword implements Keyword { use ErrorTrait; /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $data = $context->currentData(); if (!$data) { return null; } $count = count($data); for ($i = 0; $i < $count - 1; $i++) { for ($j = $i + 1; $j < $count; $j++) { if (Helper::equals($data[$i], $data[$j])) { return $this->error($schema, $context, 'uniqueItems', 'Array must have unique items', [ 'duplicate' => $data[$i], 'indexes' => [$i, $j], ]); } } } return null; } } json-schema/src/Keywords/URIRefKeyword.php 0000777 00000003557 15252016106 0014542 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\Exceptions\UnresolvedReferenceException; use Opis\JsonSchema\{Schema, Uri, ValidationContext, Variables}; class URIRefKeyword extends AbstractRefKeyword { protected Uri $uri; /** @var bool|null|Schema */ protected $resolved = false; public function __construct( Uri $uri, ?Variables $mapper, ?Variables $globals, ?array $slots = null, string $keyword = '$ref' ) { parent::__construct($mapper, $globals, $slots, $keyword); $this->uri = $uri; } protected function doValidate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->resolved === false) { $this->resolved = $context->loader()->loadSchemaById($this->uri); } if ($this->resolved === null) { throw new UnresolvedReferenceException((string)$this->uri, $schema, $context); } $this->setLastRefSchema($this->resolved); return $this->resolved->validate($this->createContext($context, $schema)); } } json-schema/src/Keywords/AbstractRefKeyword.php 0000777 00000007337 15252016106 0015646 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{Errors\ValidationError, JsonPointer, Keyword, Schema, SchemaLoader, Uri, ValidationContext, Variables}; abstract class AbstractRefKeyword implements Keyword { use ErrorTrait; protected string $keyword; protected ?Variables $mapper; protected ?Variables $globals; protected ?array $slots = null; protected ?Uri $lastRefUri = null; /** * @param Variables|null $mapper * @param Variables|null $globals * @param array|null $slots * @param string $keyword */ protected function __construct(?Variables $mapper, ?Variables $globals, ?array $slots = null, string $keyword = '$ref') { $this->mapper = $mapper; $this->globals = $globals; $this->slots = $slots; $this->keyword = $keyword; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($error = $this->doValidate($context, $schema)) { $uri = $this->lastRefUri; $this->lastRefUri = null; return $this->error($schema, $context, $this->keyword, 'The data must match {keyword}', [ 'keyword' => $this->keyword, 'uri' => (string) $uri, ], $error); } $this->lastRefUri = null; return null; } abstract protected function doValidate(ValidationContext $context, Schema $schema): ?ValidationError; protected function setLastRefUri(?Uri $uri): void { $this->lastRefUri = $uri; } protected function setLastRefSchema(Schema $schema): void { $info = $schema->info(); if ($info->id()) { $this->lastRefUri = $info->id(); } else { $this->lastRefUri = Uri::merge(JsonPointer::pathToFragment($info->path()), $info->idBaseRoot()); } } /** * @param ValidationContext $context * @param Schema $schema * @return ValidationContext */ protected function createContext(ValidationContext $context, Schema $schema): ValidationContext { return $context->create($schema, $this->mapper, $this->globals, $this->slots); } /** * @param SchemaLoader $repo * @param JsonPointer $pointer * @param Uri $base * @param array|null $path * @return null|Schema */ protected function resolvePointer(SchemaLoader $repo, JsonPointer $pointer, Uri $base, ?array $path = null): ?Schema { if ($pointer->isAbsolute()) { $path = (string)$pointer; } else { if ($pointer->hasFragment()) { return null; } $path = $path ? $pointer->absolutePath($path) : $pointer->path(); if ($path === null) { return null; } $path = JsonPointer::pathToString($path); } return $repo->loadSchemaById(Uri::merge('#' . $path, $base)); } } json-schema/src/Keywords/DependenciesKeyword.php 0000777 00000006234 15252016106 0016027 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class DependenciesKeyword implements Keyword { use OfTrait; use ErrorTrait; /** @var array|object[]|string[][] */ protected array $value; /** * @param object[]|string[][] $value */ public function __construct(array $value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $data = $context->currentData(); $object = $this->createArrayObject($context); foreach ($this->value as $name => $value) { if ($value === true || !property_exists($data, $name)) { continue; } if ($value === false) { $this->addEvaluatedFromArrayObject($object, $context); return $this->error($schema, $context, 'dependencies', "Property '{property}' is not allowed", [ 'property' => $name, ]); } if (is_array($value)) { foreach ($value as $prop) { if (!property_exists($data, $prop)) { $this->addEvaluatedFromArrayObject($object, $context); return $this->error($schema, $context, 'dependencies', "Property '{missing}' property is required by property '{property}'", [ 'property' => $name, 'missing' => $prop, ]); } } continue; } if (is_object($value) && !($value instanceof Schema)) { $value = $this->value[$name] = $context->loader()->loadObjectSchema($value); } if ($error = $context->validateSchemaWithoutEvaluated($value, null, false, $object)) { $this->addEvaluatedFromArrayObject($object, $context); return $this->error($schema, $context, 'dependencies', "The object must match dependency schema defined on property '{property}'", [ 'property' => $name, ], $error); } } $this->addEvaluatedFromArrayObject($object, $context); return null; } } json-schema/src/Keywords/DependentRequiredKeyword.php 0000777 00000003636 15252016106 0017053 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class DependentRequiredKeyword implements Keyword { use ErrorTrait; /** @var string[][] */ protected array $value; /** * @param string[][] $value */ public function __construct(array $value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $data = $context->currentData(); foreach ($this->value as $name => $value) { if (!property_exists($data, $name)) { continue; } foreach ($value as $prop) { if (!property_exists($data, $prop)) { return $this->error($schema, $context, 'dependentRequired', "'{$prop}' property is required by '{$name}' property", [ 'property' => $name, 'missing' => $prop, ]); } } } return null; } } json-schema/src/Keywords/EnumDataKeyword.php 0000777 00000003356 15252016106 0015141 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class EnumDataKeyword extends EnumKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct([]); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $value = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($value === $this || !is_array($value) || empty($value)) { return $this->error($schema, $context, 'enum', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->enum = $this->listByType($value); $ret = parent::validate($context, $schema); $this->enum = null; return $ret; } } json-schema/src/Keywords/MinPropertiesKeyword.php 0000777 00000003150 15252016106 0016233 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class MinPropertiesKeyword implements Keyword { use ErrorTrait; protected int $count; /** * @param int $count */ public function __construct(int $count) { $this->count = $count; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $count = count($context->getObjectProperties()); if ($this->count <= $count) { return null; } return $this->error($schema, $context, 'minProperties', "Object must have at least {min} properties, {count} found", [ 'min' => $this->count, 'count' => $count, ]); } } json-schema/src/Keywords/TypeKeyword.php 0000777 00000003164 15252016106 0014361 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ Helper, ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class TypeKeyword implements Keyword { use ErrorTrait; /** @var string|string[] */ protected $type; /** * @param string|string[] $type */ public function __construct($type) { $this->type = $type; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $type = $context->currentDataType(); if ($type && Helper::jsonTypeMatches($type, $this->type)) { return null; } return $this->error($schema, $context, 'type', 'The data ({type}) must match the type: {expected}', [ 'expected' => $this->type, 'type' => $type, ]); } } json-schema/src/Keywords/UniqueItemsDataKeyword.php 0000777 00000003176 15252016106 0016505 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class UniqueItemsDataKeyword extends UniqueItemsKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $value = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($value === $this || !is_bool($value)) { return $this->error($schema, $context, 'uniqueItems', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } return $value ? parent::validate($context, $schema) : null; } } json-schema/src/Keywords/ExclusiveMinimumKeyword.php 0000777 00000002764 15252016106 0016750 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Keyword, Schema}; use Opis\JsonSchema\Errors\ValidationError; class ExclusiveMinimumKeyword implements Keyword { use ErrorTrait; protected float $number; /** * @param float $number */ public function __construct(float $number) { $this->number = $number; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($context->currentData() > $this->number) { return null; } return $this->error($schema, $context, 'exclusiveMinimum', "Number must be greater than {min}", [ 'min' => $this->number, ]); } } json-schema/src/Keywords/NotKeyword.php 0000777 00000003555 15252016106 0014204 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class NotKeyword implements Keyword { use ErrorTrait; /** @var bool|object|Schema */ protected $value; /** * @param bool|object $value */ public function __construct($value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->value === false) { return null; } if ($this->value === true) { return $this->error($schema, $context, 'not', "The data is never valid"); } if (is_object($this->value) && !($this->value instanceof Schema)) { $this->value = $context->loader()->loadObjectSchema($this->value); } $error = $context->validateSchemaWithoutEvaluated($this->value, 1); if ($error) { return null; } return $this->error($schema, $context, 'not', 'The data must not match schema'); } } json-schema/src/Keywords/ItemsKeyword.php 0000777 00000011605 15252016106 0014520 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class ItemsKeyword implements Keyword { use OfTrait; use IterableDataValidationTrait; /** @var bool|object|Schema|bool[]|object[]|Schema[] */ protected $value; protected int $count = -1; protected bool $alwaysValid; protected string $keyword; protected int $startIndex; /** * @param bool|object|Schema|bool[]|object[]|Schema[] $value * @param bool $alwaysValid * @param string $keyword * @param int $startIndex */ public function __construct($value, bool $alwaysValid = false, string $keyword = 'items', int $startIndex = 0) { $this->value = $value; $this->alwaysValid = $alwaysValid; if (is_array($value)) { $this->count = count($value); } $this->keyword = $keyword; $this->startIndex = $startIndex; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->alwaysValid || $this->value === true) { if ($this->count === -1) { $context->markAllAsEvaluatedItems(); } else { $context->markCountAsEvaluatedItems($this->count); } return null; } $count = count($context->currentData()); if ($this->startIndex >= $count) { // Already validated by other keyword return null; } if ($this->value === false) { if ($count === 0) { return null; } return $this->error($schema, $context, $this->keyword, 'Array must be empty'); } if ($this->count >= 0) { $errors = $this->errorContainer($context->maxErrors()); $max = min($count, $this->count); $evaluated = []; for ($i = $this->startIndex; $i < $max; $i++) { if ($this->value[$i] === true) { $evaluated[] = $i; continue; } if ($this->value[$i] === false) { $context->addEvaluatedItems($evaluated); return $this->error($schema, $context, $this->keyword, "Array item at index {index} is not allowed", [ 'index' => $i, ]); } if (is_object($this->value[$i]) && !($this->value[$i] instanceof Schema)) { $this->value[$i] = $context->loader()->loadObjectSchema($this->value[$i]); } $context->pushDataPath($i); $error = $this->value[$i]->validate($context); $context->popDataPath(); if ($error) { $errors->add($error); if ($errors->isFull()) { break; } } else { $evaluated[] = $i; } } $context->addEvaluatedItems($evaluated); if ($errors->isEmpty()) { return null; } return $this->error($schema, $context, $this->keyword, 'Array items must match corresponding schemas', [], $errors); } if (is_object($this->value) && !($this->value instanceof Schema)) { $this->value = $context->loader()->loadObjectSchema($this->value); } $object = $this->createArrayObject($context); $error = $this->validateIterableData($schema, $this->value, $context, $this->indexes($this->startIndex, $count), $this->keyword, 'All array items must match schema', [], $object); if ($object && $object->count()) { $context->addEvaluatedItems($object->getArrayCopy()); } return $error; } /** * @param int $start * @param int $max * @return iterable|int[] */ protected function indexes(int $start, int $max): iterable { for ($i = $start; $i < $max; $i++) { yield $i; } } } json-schema/src/Keywords/UnevaluatedPropertiesKeyword.php 0000777 00000004643 15252016106 0017775 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\{Keyword, Schema, ValidationContext}; class UnevaluatedPropertiesKeyword implements Keyword { use OfTrait; use IterableDataValidationTrait; /** @var bool|object|Schema */ protected $value; public function __construct($value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $unevaluated = $context->getUnevaluatedProperties(); if (!$unevaluated) { return null; } if ($this->value === true) { $context->addEvaluatedProperties($unevaluated); return null; } if ($this->value === false) { return $this->error($schema, $context, 'unevaluatedProperties', 'Unevaluated object properties not allowed: {properties}', [ 'properties' => $unevaluated, ]); } if (is_object($this->value) && !($this->value instanceof Schema)) { $this->value = $context->loader()->loadObjectSchema($this->value); } $object = $this->createArrayObject($context); $error = $this->validateIterableData($schema, $this->value, $context, $unevaluated, 'unevaluatedProperties', 'All unevaluated object properties must match schema: {properties}', [ 'properties' => $unevaluated, ], $object); if ($object && $object->count()) { $context->addEvaluatedProperties($object->getArrayCopy()); } return $error; } } json-schema/src/Keywords/FormatKeyword.php 0000777 00000004420 15252016106 0014664 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Format, Keyword, Schema }; use Opis\JsonSchema\Errors\{ValidationError, CustomError}; class FormatKeyword implements Keyword { use ErrorTrait; protected ?string $name; /** @var callable[]|Format[] */ protected ?array $types; /** * @param string $name * @param callable[]|Format[] $types */ public function __construct(string $name, array $types) { $this->name = $name; $this->types = $types; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $type = $context->currentDataType(); if (!isset($this->types[$type])) { return null; } $format = $this->types[$type]; try { if ($format instanceof Format) { $ok = $format->validate($context->currentData()); } else { $ok = $format($context->currentData()); } } catch (CustomError $error) { return $this->error($schema, $context, 'format', $error->getMessage(), $error->getArgs() + [ 'format' => $this->name, 'type' => $type, ]); } if ($ok) { return null; } return $this->error($schema, $context, 'format', "The data must match the '{format}' format", [ 'format' => $this->name, 'type' => $type, ]); } } json-schema/src/Keywords/MinLengthKeyword.php 0000777 00000003204 15252016106 0015320 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class MinLengthKeyword implements Keyword { use ErrorTrait; protected int $length; /** * @param int $length */ public function __construct(int $length) { $this->length = $length; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->length === 0) { return null; } $length = $context->getStringLength(); if ($length >= $this->length) { return null; } return $this->error($schema, $context, 'minLength', "Minimum string length is {min}, found {length}", [ 'min' => $this->length, 'length' => $length, ]); } } json-schema/src/Keywords/MinItemsDataKeyword.php 0000777 00000003321 15252016106 0015752 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class MinItemsDataKeyword extends MinItemsKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var int $count */ $count = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($count === $this || !is_int($count) || $count < 0) { return $this->error($schema, $context, 'minItems', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->count = $count; return parent::validate($context, $schema); } } json-schema/src/Keywords/IfThenElseKeyword.php 0000777 00000006064 15252016106 0015430 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class IfThenElseKeyword implements Keyword { use ErrorTrait; /** @var bool|object */ protected $if; /** @var bool|object */ protected $then; /** @var bool|object */ protected $else; /** * @param bool|object $if * @param bool|object $then * @param bool|object $else */ public function __construct($if, $then, $else) { $this->if = $if; $this->then = $then; $this->else = $else; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->if === true) { return $this->validateBranch('then', $context, $schema); } elseif ($this->if === false) { return $this->validateBranch('else', $context, $schema); } if (is_object($this->if) && !($this->if instanceof Schema)) { $this->if = $context->loader()->loadObjectSchema($this->if); } if ($context->validateSchemaWithoutEvaluated($this->if, null, true)) { return $this->validateBranch('else', $context, $schema); } return $this->validateBranch('then', $context, $schema); } /** * @param string $branch * @param ValidationContext $context * @param Schema $schema * @return ValidationError|null */ protected function validateBranch(string $branch, ValidationContext $context, Schema $schema): ?ValidationError { $value = $this->{$branch}; if ($value === true) { return null; } elseif ($value === false) { return $this->error($schema, $context, $branch, "The data is never valid on '{branch}' branch", [ 'branch' => $branch, ]); } if (is_object($value) && !($value instanceof Schema)) { $value = $this->{$branch} = $context->loader()->loadObjectSchema($value); } if ($error = $value->validate($context)) { return $this->error($schema, $context, $branch, "The data is not valid on '{branch}' branch", [ 'branch' => $branch, ], $error); } return null; } } json-schema/src/Keywords/OneOfKeyword.php 0000777 00000005770 15252016106 0014453 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class OneOfKeyword implements Keyword { use OfTrait; use ErrorTrait; /** @var bool[]|object[] */ protected array $value; /** * @param bool[]|object[] $value */ public function __construct(array $value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $count = 0; $matchedIndex = -1; $object = $this->createArrayObject($context); $errors = []; foreach ($this->value as $index => $value) { if ($value === false) { continue; } if ($value === true) { if (++$count > 1) { $this->addEvaluatedFromArrayObject($object, $context); return $this->error($schema, $context, 'oneOf', 'The data should match exactly one schema', [ 'matched' => [$matchedIndex, $index], ]); } $matchedIndex = $index; continue; } if (is_object($value) && !($value instanceof Schema)) { $value = $this->value[$index] = $context->loader()->loadObjectSchema($value); } $error = $context->validateSchemaWithoutEvaluated($value, null, false, $object); if ($error) { $errors[] = $error; } else { if (++$count > 1) { $this->addEvaluatedFromArrayObject($object, $context); return $this->error($schema, $context, 'oneOf', 'The data should match exactly one schema', [ 'matched' => [$matchedIndex, $index], ]); } $matchedIndex = $index; } } $this->addEvaluatedFromArrayObject($object, $context); if ($count === 1) { return null; } return $this->error($schema, $context, 'oneOf', 'The data should match exactly one schema', [ 'matched' => [], ], $errors); } } json-schema/src/Keywords/ContentSchemaKeyword.php 0000777 00000004041 15252016106 0016166 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Keyword, Schema}; use Opis\JsonSchema\Errors\ValidationError; class ContentSchemaKeyword implements Keyword { use ErrorTrait; /** @var bool|object */ protected $value; /** * @param object $value */ public function __construct(object $value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $data = json_decode($context->getDecodedContent(), false); if ($error = json_last_error() !== JSON_ERROR_NONE) { $message = json_last_error_msg(); return $this->error($schema, $context, 'contentSchema', "Invalid JSON content: {message}", [ 'error' => $error, 'message' => $message, ]); } if (is_object($this->value) && !($this->value instanceof Schema)) { $this->value = $context->loader()->loadObjectSchema($this->value); } if ($error = $this->value->validate($context->newInstance($data, $schema))) { return $this->error($schema, $context, 'contentSchema', "The JSON content must match schema", [], $error); } return null; } } json-schema/src/Keywords/PropertyNamesKeyword.php 0000777 00000004160 15252016106 0016245 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class PropertyNamesKeyword implements Keyword { use ErrorTrait; /** @var bool|object */ protected $value; /** * @param bool|object $value */ public function __construct($value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->value === true) { return null; } $props = $context->getObjectProperties(); if (!$props) { return null; } if ($this->value === false) { return $this->error($schema, $context, 'propertyNames', "No properties are allowed"); } if (is_object($this->value) && !($this->value instanceof Schema)) { $this->value = $context->loader()->loadObjectSchema($this->value); } foreach ($props as $prop) { if ($error = $this->value->validate($context->newInstance($prop, $schema))) { return $this->error($schema, $context, 'propertyNames', "Property '{property}' must match schema", [ 'property' => $prop, ], $error); } } return null; } } json-schema/src/Keywords/MultipleOfKeyword.php 0000777 00000003040 15252016106 0015511 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema, Helper }; use Opis\JsonSchema\Errors\ValidationError; class MultipleOfKeyword implements Keyword { use ErrorTrait; protected float $number; /** * @param float $number */ public function __construct(float $number) { $this->number = $number; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if (Helper::isMultipleOf($context->currentData(), $this->number)) { return null; } return $this->error($schema, $context, 'multipleOf', "Number must be a multiple of {divisor}", [ 'divisor' => $this->number, ]); } } json-schema/src/Keywords/AdditionalPropertiesKeyword.php 0000777 00000004677 15252016106 0017577 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class AdditionalPropertiesKeyword implements Keyword { use OfTrait; use IterableDataValidationTrait; /** @var bool|object|Schema */ protected $value; /** * @param bool|object|Schema $value */ public function __construct($value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->value === true) { $context->markAllAsEvaluatedProperties(); return null; } $props = $context->getUncheckedProperties(); if (!$props) { return null; } if ($this->value === false) { return $this->error($schema, $context, 'additionalProperties', 'Additional object properties are not allowed: {properties}', [ 'properties' => $props ]); } if (is_object($this->value) && !($this->value instanceof Schema)) { $this->value = $context->loader()->loadObjectSchema($this->value); } $object = $this->createArrayObject($context); $error = $this->validateIterableData($schema, $this->value, $context, $props, 'additionalProperties', 'All additional object properties must match schema: {properties}', [ 'properties' => $props ], $object); if ($object && $object->count()) { $context->addEvaluatedProperties($object->getArrayCopy()); } return $error; } } json-schema/src/Keywords/MinItemsKeyword.php 0000777 00000003122 15252016106 0015157 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class MinItemsKeyword implements Keyword { use ErrorTrait; protected int $count; /** * @param int $count */ public function __construct(int $count) { $this->count = $count; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $count = count($context->currentData()); if ($count >= $this->count) { return null; } return $this->error($schema, $context, "minItems", "Array should have at least {min} items, {count} found", [ 'min' => $this->count, 'count' => $count, ]); } } json-schema/src/Keywords/TemplateRefKeyword.php 0000777 00000007545 15252016106 0015657 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\Uri\UriTemplate; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\Exceptions\UnresolvedReferenceException; use Opis\JsonSchema\{JsonPointer, Schema, SchemaLoader, Uri, ValidationContext, Variables}; class TemplateRefKeyword extends AbstractRefKeyword { protected UriTemplate $template; protected ?Variables $vars = null; /** @var Schema[]|null[] */ protected ?array $cached = []; protected bool $allowRelativeJsonPointerInRef; public function __construct( UriTemplate $template, ?Variables $vars, ?Variables $mapper = null, ?Variables $globals = null, ?array $slots = null, string $keyword = '$ref', bool $allowRelativeJsonPointerInRef = true ) { parent::__construct($mapper, $globals, $slots, $keyword); $this->template = $template; $this->vars = $vars; $this->allowRelativeJsonPointerInRef = $allowRelativeJsonPointerInRef; } protected function doValidate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->vars) { $vars = $this->vars->resolve($context->rootData(), $context->currentDataPath()); if (!is_array($vars)) { $vars = (array)$vars; } $vars += $context->globals(); } else { $vars = $context->globals(); } $ref = $this->template->resolve($vars); $key = isset($ref[32]) ? md5($ref) : $ref; if (!array_key_exists($key, $this->cached)) { $this->cached[$key] = $this->resolveRef($ref, $context->loader(), $schema); } $resolved = $this->cached[$key]; unset($key); if (!$resolved) { throw new UnresolvedReferenceException($ref, $schema, $context); } return $resolved->validate($this->createContext($context, $schema)); } /** * @param string $ref * @param SchemaLoader $repo * @param Schema $schema * @return null|Schema */ protected function resolveRef(string $ref, SchemaLoader $repo, Schema $schema): ?Schema { if ($ref === '') { return null; } $baseUri = $schema->info()->idBaseRoot(); if ($ref === '#') { return $repo->loadSchemaById($baseUri); } // Check if is pointer if ($ref[0] === '#') { if ($pointer = JsonPointer::parse(substr($ref, 1))) { if ($pointer->isAbsolute()) { return $this->resolvePointer($repo, $pointer, $baseUri); } unset($pointer); } } elseif ($this->allowRelativeJsonPointerInRef && ($pointer = JsonPointer::parse($ref))) { if ($pointer->isRelative()) { return $this->resolvePointer($repo, $pointer, $baseUri, $schema->info()->path()); } unset($pointer); } $ref = Uri::merge($ref, $baseUri, true); if ($ref === null || !$ref->isAbsolute()) { return null; } return $repo->loadSchemaById($ref); } } json-schema/src/Keywords/MaxItemsKeyword.php 0000777 00000003121 15252016106 0015160 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class MaxItemsKeyword implements Keyword { use ErrorTrait; protected int $count; /** * @param int $count */ public function __construct(int $count) { $this->count = $count; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $count = count($context->currentData()); if ($count <= $this->count) { return null; } return $this->error($schema, $context, "maxItems", "Array should have at most {max} items, {count} found", [ 'max' => $this->count, 'count' => $count, ]); } } json-schema/src/Keywords/MaxPropertiesDataKeyword.php 0000777 00000003324 15252016106 0017032 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class MaxPropertiesDataKeyword extends MaxPropertiesKeywords { protected JsonPointer $value; /** * @inheritDoc */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var int $count */ $count = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($count === $this || !is_int($count) || $count < 0) { return $this->error($schema, $context, 'maxProperties', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->count = $count; return parent::validate($context, $schema); } } json-schema/src/Keywords/MinPropertiesDataKeyword.php 0000777 00000003340 15252016106 0017026 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class MinPropertiesDataKeyword extends MinPropertiesKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var int $count */ $count = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($count === $this || !is_int($count) || $count < 0) { return $this->error($schema, $context, 'minProperties', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->count = $count; return parent::validate($context, $schema); } } json-schema/src/Keywords/PointerRefKeyword.php 0000777 00000003700 15252016106 0015511 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\Exceptions\UnresolvedReferenceException; use Opis\JsonSchema\{JsonPointer, Schema, ValidationContext, Variables}; class PointerRefKeyword extends AbstractRefKeyword { protected JsonPointer $pointer; /** @var bool|null|Schema */ protected $resolved = false; public function __construct( JsonPointer $pointer, ?Variables $mapper, ?Variables $globals, ?array $slots = null, string $keyword = '$ref' ) { parent::__construct($mapper, $globals, $slots, $keyword); $this->pointer = $pointer; } protected function doValidate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->resolved === false) { $info = $schema->info(); $this->resolved = $this->resolvePointer($context->loader(), $this->pointer, $info->idBaseRoot(), $info->path()); } if ($this->resolved === null) { throw new UnresolvedReferenceException((string)$this->pointer, $schema, $context); } return $this->resolved->validate($this->createContext($context, $schema)); } } json-schema/src/Keywords/FiltersKeyword.php 0000777 00000005515 15252016106 0015052 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ Filter, ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\{ValidationError, CustomError}; use Opis\JsonSchema\Exceptions\UnresolvedFilterException; class FiltersKeyword implements Keyword { use ErrorTrait; /** @var array|object[] */ protected array $filters; /** * @param object[] $filters */ public function __construct(array $filters) { $this->filters = $filters; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $type = $context->currentDataType(); foreach ($this->filters as $filter) { if (!isset($filter->types[$type])) { throw new UnresolvedFilterException($filter->name, $type, $schema, $context); } $func = $filter->types[$type]; if ($filter->args) { $args = (array)$filter->args->resolve($context->rootData(), $context->currentDataPath()); $args += $context->globals(); } else { $args = $context->globals(); } try { if ($func instanceof Filter) { $ok = $func->validate($context, $schema, $args); } else { $ok = $func($context->currentData(), $args); } } catch (CustomError $error) { return $this->error($schema, $context, '$filters', $error->getMessage(), $error->getArgs() + [ 'filter' => $filter->name, 'type' => $type, 'args' => $args, ]); } if ($ok) { unset($func, $args, $ok); continue; } return $this->error($schema, $context, '$filters', "Filter '{filter}' ({type}) was not passed", [ 'filter' => $filter->name, 'type' => $type, 'args' => $args, ]); } return null; } } json-schema/src/Keywords/AllOfKeyword.php 0000777 00000004577 15252016106 0014446 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class AllOfKeyword implements Keyword { use OfTrait; use ErrorTrait; /** @var bool[]|object[] */ protected array $value; /** * @param bool[]|object[] $value */ public function __construct(array $value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $object = $this->createArrayObject($context); foreach ($this->value as $index => $value) { if ($value === true) { continue; } if ($value === false) { $this->addEvaluatedFromArrayObject($object, $context); return $this->error($schema, $context, 'allOf', 'The data should match all schemas', [ 'index' => $index, ]); } if (is_object($value) && !($value instanceof Schema)) { $value = $this->value[$index] = $context->loader()->loadObjectSchema($value); } if ($error = $context->validateSchemaWithoutEvaluated($value, null, false, $object)) { $this->addEvaluatedFromArrayObject($object, $context); return $this->error($schema, $context, 'allOf', 'The data should match all schemas', [ 'index' => $index, ], $error); } } $this->addEvaluatedFromArrayObject($object, $context); return null; } } json-schema/src/Keywords/MaxLengthDataKeyword.php 0000777 00000003333 15252016106 0016117 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class MaxLengthDataKeyword extends MaxLengthKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var int $length */ $length = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($length === $this || !is_int($length) || $length < 0) { return $this->error($schema, $context, 'maxLength', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->length = $length; return parent::validate($context, $schema); } } json-schema/src/Keywords/MultipleOfDataKeyword.php 0000777 00000003374 15252016106 0016315 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class MultipleOfDataKeyword extends MultipleOfKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var float|int $number */ $number = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($number === $this || !(is_float($number) || is_int($number)) || $number <= 0) { return $this->error($schema, $context, 'multipleOf', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->number = $number; return parent::validate($context, $schema); } } json-schema/src/Keywords/MinimumKeyword.php 0000777 00000002775 15252016106 0015062 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class MinimumKeyword implements Keyword { use ErrorTrait; protected float $number; /** * @param float $number */ public function __construct(float $number) { $this->number = $number; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($context->currentData() >= $this->number) { return null; } return $this->error($schema, $context, 'minimum', "Number must be greater than or equal to {min}", [ 'min' => $this->number, ]); } } json-schema/src/Keywords/RequiredKeyword.php 0000777 00000003517 15252016106 0015222 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class RequiredKeyword implements Keyword { use ErrorTrait; /** @var string[] */ protected ?array $required; /** * @param string[] $required */ public function __construct(array $required) { $this->required = $required; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $data = $context->currentData(); $max = $context->maxErrors(); $list = []; foreach ($this->required as $name) { if (!property_exists($data, $name)) { $list[] = $name; if (--$max <= 0) { break; } } } if (!$list) { return null; } return $this->error($schema, $context, 'required', 'The required properties ({missing}) are missing', [ 'missing' => $list, ]); } } json-schema/src/Keywords/ConstKeyword.php 0000777 00000002766 15252016106 0014535 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\{Helper, ValidationContext, Keyword, Schema}; class ConstKeyword implements Keyword { use ErrorTrait; /** @var mixed */ protected $const; /** * @param $const */ public function __construct($const) { $this->const = $const; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if (Helper::equals($this->const, $context->currentData())) { return null; } return $this->error($schema, $context, 'const', 'The data must match the const value', [ 'const' => $this->const ]); } } json-schema/src/Keywords/SlotsKeyword.php 0000777 00000010270 15252016106 0014540 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Keyword, Schema}; use Opis\JsonSchema\Errors\ValidationError; class SlotsKeyword implements Keyword { use ErrorTrait; /** @var bool[]|Schema[]|object[]|string[] */ protected array $slots; /** @var string[] */ protected array $stack = []; /** * @param string[]|bool[]|object[]|Schema[] $slots */ public function __construct(array $slots) { $this->slots = $slots; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $newContext = $context->newInstance($context->currentData(), $schema); foreach ($this->slots as $name => $fallback) { $slot = $this->resolveSlotSchema($name, $context); if ($slot === null) { $save = true; if (is_string($fallback)) { $save = false; $fallback = $this->resolveSlot($fallback, $context); } if ($fallback === true) { continue; } if ($fallback === false) { return $this->error($schema, $context, '$slots', "Required slot '{slot}' is missing", [ 'slot' => $name, ]); } if (is_object($fallback) && !($fallback instanceof Schema)) { $fallback = $context->loader()->loadObjectSchema($fallback); if ($save) { $this->slots[$name] = $fallback; } } $slot = $fallback; } if ($error = $slot->validate($newContext)) { return $this->error($schema, $context,'$slots', "Schema for slot '{slot}' was not matched", [ 'slot' => $name, ], $error); } } return null; } /** * @param string $name * @param ValidationContext $context * @return Schema|null */ protected function resolveSlotSchema(string $name, ValidationContext $context): ?Schema { do { $slot = $context->slot($name); } while ($slot === null && $context = $context->parent()); return $slot; } /** * @param string $name * @param ValidationContext $context * @return bool|Schema */ protected function resolveSlot(string $name, ValidationContext $context) { $slot = $this->resolveSlotSchema($name, $context); if ($slot !== null) { return $slot; } if (!isset($this->slots[$name])) { return false; } $slot = $this->slots[$name]; if (is_bool($slot)) { return $slot; } if (is_object($slot)) { if ($slot instanceof Schema) { return $slot; } $slot = $context->loader()->loadObjectSchema($slot); $this->slots[$name] = $slot; return $slot; } if (!is_string($slot)) { // Looks like the slot is missing return false; } if (in_array($slot, $this->stack)) { // Recursive return false; } $this->stack[] = $slot; $slot = $this->resolveSlot($slot, $context); array_pop($this->stack); return $slot; } } json-schema/src/Keywords/MaximumKeyword.php 0000777 00000002773 15252016106 0015062 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class MaximumKeyword implements Keyword { use ErrorTrait; protected float $number; /** * @param float $number */ public function __construct(float $number) { $this->number = $number; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($context->currentData() <= $this->number) { return null; } return $this->error($schema, $context, 'maximum', "Number must be lower than or equal to {max}", [ 'max' => $this->number, ]); } } json-schema/src/Keywords/ExclusiveMinimumDataKeyword.php 0000777 00000003376 15252016106 0017542 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class ExclusiveMinimumDataKeyword extends ExclusiveMinimumKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var float|int $number */ $number = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($number === $this || !(is_float($number) || is_int($number))) { return $this->error($schema, $context, 'exclusiveMinimum', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->number = $number; return parent::validate($context, $schema); } } json-schema/src/Keywords/DependentSchemasKeyword.php 0000777 00000004740 15252016106 0016653 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class DependentSchemasKeyword implements Keyword { use OfTrait; use ErrorTrait; protected array $value; public function __construct(object $value) { $this->value = (array)$value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $data = $context->currentData(); $object = $this->createArrayObject($context); foreach ($this->value as $name => $value) { if ($value === true || !property_exists($data, $name)) { continue; } if ($value === false) { $this->addEvaluatedFromArrayObject($object, $context); return $this->error($schema, $context, 'dependentSchemas', "'{$name}' property is not allowed", [ 'property' => $name, ]); } if (is_object($value) && !($value instanceof Schema)) { $value = $this->value[$name] = $context->loader()->loadObjectSchema($value); } if ($error = $context->validateSchemaWithoutEvaluated($value, null, false, $object)) { $this->addEvaluatedFromArrayObject($object, $context); return $this->error($schema, $context, 'dependentSchemas', "The object must match dependency schema defined on property '{$name}'", [ 'property' => $name, ], $error); } } $this->addEvaluatedFromArrayObject($object, $context); return null; } } json-schema/src/Keywords/EnumKeyword.php 0000777 00000004102 15252016106 0014335 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ Helper, ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class EnumKeyword implements Keyword { use ErrorTrait; protected ?array $enum; /** * @param array $enum */ public function __construct(array $enum) { $this->enum = $this->listByType($enum); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $type = $context->currentDataType(); $data = $context->currentData(); if (isset($this->enum[$type])) { foreach ($this->enum[$type] as $value) { if (Helper::equals($value, $data)) { return null; } } } return $this->error($schema, $context, 'enum', 'The data should match one item from enum'); } /** * @param array $values * @return array */ protected function listByType(array $values): array { $list = []; foreach ($values as $value) { $type = Helper::getJsonType($value); if (!isset($list[$type])) { $list[$type] = []; } $list[$type][] = $value; } return $list; } } json-schema/src/Keywords/RequiredDataKeyword.php 0000777 00000004641 15252016106 0016013 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class RequiredDataKeyword extends RequiredKeyword { protected JsonPointer $value; /** @var callable|null */ protected $filter; /** * @param JsonPointer $value * @param callable|null $filter */ public function __construct(JsonPointer $value, ?callable $filter = null) { $this->value = $value; $this->filter = $filter; parent::__construct([]); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $required = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($required === $this || !is_array($required) || !$this->requiredPropsAreValid($required)) { return $this->error($schema, $context, 'required', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $required = array_unique($required); if ($this->filter) { $required = array_filter($required, $this->filter); } if (!$required) { return null; } $this->required = $required; $ret = parent::validate($context, $schema); $this->required = null; return $ret; } /** * @param array $props * @return bool */ protected function requiredPropsAreValid(array $props): bool { foreach ($props as $prop) { if (!is_string($prop)) { return false; } } return true; } } json-schema/src/Keywords/ConstDataKeyword.php 0000777 00000003274 15252016106 0015322 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class ConstDataKeyword extends ConstKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(null); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $value = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($value === $this) { return $this->error($schema, $context, 'const', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->const = $value; $ret = parent::validate($context, $schema); $this->const = null; return $ret; } } json-schema/src/Keywords/ContentEncodingKeyword.php 0000777 00000004711 15252016106 0016520 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Keyword, Schema, ContentEncoding}; use Opis\JsonSchema\Resolvers\ContentEncodingResolver; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\Exceptions\UnresolvedContentEncodingException; class ContentEncodingKeyword implements Keyword { use ErrorTrait; protected string $name; protected ?ContentEncodingResolver $resolver; /** @var bool|null|callable|ContentEncoding */ protected $encoding = false; /** * @param string $name * @param null|ContentEncodingResolver $resolver */ public function __construct(string $name, ?ContentEncodingResolver $resolver = null) { $this->name = $name; $this->resolver = $resolver; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if (!$this->resolver) { return null; } if ($this->encoding === false) { $this->encoding = $this->resolver->resolve($this->name); } if ($this->encoding === null) { throw new UnresolvedContentEncodingException($this->name, $schema, $context); } $result = $this->encoding instanceof ContentEncoding ? $this->encoding->decode($context->currentData(), $this->name) : ($this->encoding)($context->currentData(), $this->name); if ($result === null) { return $this->error($schema, $context, 'contentEncoding', "The value must be encoded as '{encoding}'", [ 'encoding' => $this->name, ]); } $context->setDecodedContent($result); return null; } } json-schema/src/Keywords/PatternDataKeyword.php 0000777 00000003527 15252016106 0015652 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{Helper, ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class PatternDataKeyword extends PatternKeyword { protected JsonPointer $value; /** * @param JsonPointer $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(''); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $pattern = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($pattern === $this || !is_string($pattern) || !Helper::isValidPattern($pattern)) { return $this->error($schema, $context, 'pattern', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->pattern = $pattern; $this->regex = Helper::patternToRegex($pattern); $ret = parent::validate($context, $schema); $this->pattern = $this->regex = null; return $ret; } } json-schema/src/Keywords/MinimumDataKeyword.php 0000777 00000003327 15252016106 0015646 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; class MinimumDataKeyword extends MinimumKeyword { protected JsonPointer $value; /** * @param $value */ public function __construct(JsonPointer $value) { $this->value = $value; parent::__construct(0); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { /** @var float|int $number */ $number = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($number === $this || !(is_float($number) || is_int($number))) { return $this->error($schema, $context, 'minimum', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } $this->number = $number; return parent::validate($context, $schema); } } json-schema/src/Keywords/AdditionalItemsKeyword.php 0000777 00000005322 15252016106 0016510 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class AdditionalItemsKeyword implements Keyword { use OfTrait; use IterableDataValidationTrait; /** @var bool|object|Schema */ protected $value; protected int $index; /** * @param bool|object $value * @param int $startIndex */ public function __construct($value, int $startIndex) { $this->value = $value; $this->index = $startIndex; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { if ($this->value === true) { $context->markAllAsEvaluatedItems(); return null; } $data = $context->currentData(); $count = count($data); if ($this->index >= $count) { return null; } if ($this->value === false) { return $this->error($schema, $context, 'additionalItems', 'Array should not have additional items', [ 'index' => $this->index, ]); } if (is_object($this->value) && !($this->value instanceof Schema)) { $this->value = $context->loader()->loadObjectSchema($this->value); } $object = $this->createArrayObject($context); $error = $this->validateIterableData($schema, $this->value, $context, $this->indexes($this->index, $count), 'additionalItems', 'All additional array items must match schema', [], $object); if ($object && $object->count()) { $context->addEvaluatedItems($object->getArrayCopy()); } return $error; } /** * @param int $start * @param int $max * @return iterable|int[] */ protected function indexes(int $start, int $max): iterable { for ($i = $start; $i < $max; $i++) { yield $i; } } } json-schema/src/Keywords/PatternPropertiesKeyword.php 0000777 00000007473 15252016106 0017141 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Traversable; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\{Helper, ValidationContext, Keyword, Schema}; class PatternPropertiesKeyword implements Keyword { use IterableDataValidationTrait; /** @var bool[]|object[] */ protected array $value; /** * @param bool[]|object[] $value */ public function __construct(array $value) { $this->value = $value; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $props = $context->getObjectProperties(); if (!$props) { return null; } $checked = []; foreach ($this->value as $pattern => $value) { if ($value === true) { iterator_to_array($this->matchedProperties($pattern, $props, $checked)); continue; } if ($value === false) { $list = iterator_to_array($this->matchedProperties($pattern, $props, $checked)); if ($list) { if ($context->trackUnevaluated()) { $context->addEvaluatedProperties(array_diff(array_keys($checked), $list)); } return $this->error($schema, $context, 'patternProperties', "Object properties that match pattern '{pattern}' are not allowed", [ 'pattern' => $pattern, 'forbidden' => $list, ]); } unset($list); continue; } if (is_object($value) && !($value instanceof Schema)) { $value = $this->value[$pattern] = $context->loader()->loadObjectSchema($value); } $subErrors = $this->iterateAndValidate($value, $context, $this->matchedProperties($pattern, $props, $checked)); if (!$subErrors->isEmpty()) { if ($context->trackUnevaluated()) { $context->addEvaluatedProperties(array_keys($checked)); } return $this->error($schema, $context, 'patternProperties', "Object properties that match pattern '{pattern}' must also match pattern's schema", [ 'pattern' => $pattern, ], $subErrors); } unset($subErrors); } if ($checked) { $checked = array_keys($checked); $context->addCheckedProperties($checked); $context->addEvaluatedProperties($checked); } return null; } /** * @param string $pattern * @param array $props * @param array $checked * @return Traversable|string[] */ protected function matchedProperties(string $pattern, array $props, array &$checked): Traversable { $pattern = Helper::patternToRegex($pattern); foreach ($props as $prop) { if (preg_match($pattern, (string)$prop)) { $checked[$prop] = true; yield $prop; } } } } json-schema/src/Keywords/FormatDataKeyword.php 0000777 00000004506 15252016106 0015463 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{Helper, ValidationContext, Schema, JsonPointer}; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\Resolvers\FormatResolver; class FormatDataKeyword extends FormatKeyword { protected JsonPointer $value; protected FormatResolver $resolver; /** * @param JsonPointer $value * @param FormatResolver $resolver */ public function __construct(JsonPointer $value, FormatResolver $resolver) { $this->value = $value; $this->resolver = $resolver; parent::__construct('', []); } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $value = $this->value->data($context->rootData(), $context->currentDataPath(), $this); if ($value === $this || !is_string($value)) { return $this->error($schema, $context, 'format', 'Invalid $data', [ 'pointer' => (string)$this->value, ]); } /** @var string $value */ $type = $context->currentDataType(); $types = [ $type => $this->resolver->resolve($value, $type), ]; if (!$types[$type] && ($super = Helper::getJsonSuperType($type))) { $types[$super] = $this->resolver->resolve($value, $super); unset($super); } unset($type); $this->name = $value; $this->types = $types; $ret = parent::validate($context, $schema); $this->name = $this->types = null; return $ret; } } json-schema/src/Keywords/ContainsKeyword.php 0000777 00000010445 15252016106 0015216 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Keywords; use Opis\JsonSchema\{ ValidationContext, Keyword, Schema }; use Opis\JsonSchema\Errors\ValidationError; class ContainsKeyword implements Keyword { use ErrorTrait; /** @var bool|object */ protected $value; protected ?int $min = null; protected ?int $max = null; /** * @param bool|object $value * @param int|null $min * @param int|null $max */ public function __construct($value, ?int $min = null, ?int $max = null) { $this->value = $value; $this->min = $min; $this->max = $max; } /** * @inheritDoc */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError { $data = $context->currentData(); $count = count($data); $context->markAllAsEvaluatedItems(); if ($this->min > $count) { return $this->error($schema, $context, 'minContains', 'Array must have at least {min} items', [ 'min' => $this->min, 'count' => $count, ]); } $isMaxNull = $this->max === null; if ($this->value === true) { if ($count) { if (!$isMaxNull && $count > $this->max) { return $this->error($schema, $context, 'maxContains', 'Array must have at most {max} items', [ 'max' => $this->max, 'count' => $count, ]); } return null; } return $this->error($schema, $context, 'contains', 'Array must not be empty'); } if ($this->value === false) { return $this->error($schema, $context, 'contains', 'Any array is invalid'); } if (is_object($this->value) && !($this->value instanceof Schema)) { $this->value = $context->loader()->loadObjectSchema($this->value); } $errors = []; $valid = 0; $isMinNull = $this->min === null; if ($isMaxNull && $isMinNull) { foreach ($data as $key => $item) { $context->pushDataPath($key); $error = $this->value->validate($context); $context->popDataPath(); if ($error) { $errors[] = $error; } else { return null; } } return $this->error($schema, $context, 'contains', 'At least one array item must match schema', [], $errors); } foreach ($data as $key => $item) { $context->pushDataPath($key); $error = $this->value->validate($context); $context->popDataPath(); if ($error) { $errors[] = $error; } else { $valid++; } } if (!$isMinNull && $valid < $this->min) { return $this->error($schema, $context, 'minContains', 'At least {min} array items must match schema', [ 'min' => $this->min, 'count' => $valid, ]); } if (!$isMaxNull && $valid > $this->max) { return $this->error($schema, $context, 'maxContains', 'At most {max} array items must match schema', [ 'max' => $this->max, 'count' => $valid, ]); } if ($valid) { return null; } return $this->error($schema, $context, 'contains', 'At least one array item must match schema', [], $errors); } } json-schema/src/Resolvers/FormatResolver.php 0000777 00000010132 15252016106 0015213 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Resolvers; use Opis\JsonSchema\{Helper, Format, JsonPointer, Uri}; use Opis\JsonSchema\Formats\{ DateTimeFormats, IriFormats, MiscFormats, UriFormats }; class FormatResolver { /** @var Format[][]|callable[][] */ protected array $formats = []; /** * FormatResolver constructor. */ public function __construct() { $this->formats = [ 'string' => [ 'date' => DateTimeFormats::class . '::date', 'time' => DateTimeFormats::class . '::time', 'date-time' => DateTimeFormats::class . '::dateTime', 'duration' => DateTimeFormats::class . '::duration', 'uri' => UriFormats::class . '::uri', 'uri-reference' => UriFormats::class . '::uriReference', 'uri-template' => UriFormats::class . '::uriTemplate', 'regex' => Helper::class . '::isValidPattern', 'ipv4' => MiscFormats::class . '::ipv4', 'ipv6' => MiscFormats::class . '::ipv6', 'uuid' => MiscFormats::class . '::uuid', 'email' => MiscFormats::class . '::email', 'hostname' => Uri::class . '::isValidHost', 'json-pointer' => JsonPointer::class . '::isAbsolutePointer', 'relative-json-pointer' => JsonPointer::class . '::isRelativePointer', 'idn-hostname' => IriFormats::class . '::idnHostname', 'idn-email' => IriFormats::class . '::idnEmail', 'iri' => IriFormats::class . '::iri', 'iri-reference' => IriFormats::class . '::iriReference', ], ]; } /** * @param string $name * @param string $type * @return callable|Format|null */ public function resolve(string $name, string $type) { return $this->formats[$type][$name] ?? null; } /** * @param string $name * @return Format[]|callable[]|null */ public function resolveAll(string $name): ?array { $list = null; foreach ($this->formats as $type => $items) { if (isset($items[$name])) { $list[$type] = $items[$name]; } } return $list; } /** * @param string $type * @param string $name * @param Format $format * @return FormatResolver */ public function register(string $type, string $name, Format $format): self { $this->formats[$type][$name] = $format; return $this; } /** * @param string $type * @param string $name * @param callable $format * @return FormatResolver */ public function registerCallable(string $type, string $name, callable $format): self { $this->formats[$type][$name] = $format; return $this; } /** * @param string $type * @param string $name * @return bool */ public function unregister(string $type, string $name): bool { if (isset($this->formats[$type][$name])) { unset($this->formats[$type][$name]); return true; } return false; } public function __serialize(): array { return ['formats' => $this->formats]; } public function __unserialize(array $data): void { $this->formats = $data['formats']; } } json-schema/src/Resolvers/SchemaResolver.php 0000777 00000017303 15252016106 0015172 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Resolvers; use Opis\JsonSchema\Uri; class SchemaResolver { /** @var bool[]|object[] */ protected array $raw = []; /** @var string[] */ protected array $files = []; /** @var callable[] */ protected array $protocols = []; /** @var array */ protected array $prefixes = []; /** @var array */ protected array $dirs = []; /** * @param Uri $uri * @return bool|mixed|object|null */ public function resolve(Uri $uri) { if (!$uri->isAbsolute()) { return null; } $scheme = $uri->scheme(); if (isset($this->protocols[$scheme])) { return ($this->protocols[$scheme])($uri); } $id = (string) $uri; if (isset($this->raw[$id])) { return $this->raw[$id]; } $path = $this->resolvePath($uri); if ($path === null || !is_file($path)) { return null; } $data = file_get_contents($path); if (!is_string($data)) { return null; } $data = json_decode($data, false); return $data; } /** * @param bool|object|string $schema * @param string|null $id * @return bool */ public function registerRaw($schema, ?string $id = null): bool { if (is_string($schema)) { $schema = json_decode($schema, false); } if ($id !== null && strpos($id, '#') === false) { $id .= '#'; } if (is_bool($schema)) { if ($id === null) { return false; } $this->raw[$id] = $schema; return true; } if (!is_object($schema)) { return false; } if ($id === null) { if (!isset($schema->{'$id'}) || !is_string($schema->{'$id'})) { return false; } $id = $schema->{'$id'}; if (strpos($id, '#') === false) { $id .= '#'; } } $this->raw[$id] = $schema; return true; } /** * @param string $id * @return bool */ public function unregisterRaw(string $id): bool { if (strpos($id, '#') === false) { $id .= '#'; } if (isset($this->raw[$id])) { unset($this->raw[$id]); return true; } return false; } /** * @param string $id * @param string $file * @return SchemaResolver */ public function registerFile(string $id, string $file): self { if (strpos($id, '#') === false) { $id .= '#'; } $this->files[$id] = $file; return $this; } /** * @param string $id * @return bool */ public function unregisterFile(string $id): bool { if (strpos($id, '#') === false) { $id .= '#'; } if (!isset($this->files[$id])) { return false; } unset($this->files[$id]); return true; } /** * @param string $scheme * @param callable $handler * @return SchemaResolver */ public function registerProtocol(string $scheme, callable $handler): self { $this->protocols[$scheme] = $handler; return $this; } /** * @param string $scheme * @return bool */ public function unregisterProtocol(string $scheme): bool { if (isset($this->protocols[$scheme])) { unset($this->protocols[$scheme]); return true; } return false; } /** * @param string $scheme * @param string $host * @param string|null $dir * @return SchemaResolver */ public function registerProtocolDir(string $scheme, string $host, ?string $dir): self { if ($dir === null) { unset($this->dirs[$scheme][$host]); } else { $this->dirs[$scheme][$host] = rtrim($dir, '/'); } return $this; } /** * @param string $scheme * @return bool */ public function unregisterProtocolDirs(string $scheme): bool { if (isset($this->dirs[$scheme])) { unset($this->dirs[$scheme]); return true; } return false; } /** * @param string $prefix * @param string $dir * @return SchemaResolver */ public function registerPrefix(string $prefix, string $dir): self { $this->prefixes[$prefix] = rtrim($dir, '/'); // Sort uksort($this->prefixes, [$this, 'sortPrefixKeys']); return $this; } /** * @param string $prefix * @return SchemaResolver */ public function unregisterPrefix(string $prefix): self { if (isset($this->prefixes[$prefix])) { unset($this->prefixes[$prefix]); // Sort uksort($this->prefixes, [$this, 'sortPrefixKeys']); } return $this; } public function __serialize(): array { return [ 'raw' => $this->raw, 'protocols' => $this->protocols, 'prefixes' => $this->prefixes, 'dirs' => $this->dirs, ]; } public function __unserialize(array $data): void { $this->raw = $data['raw']; $this->protocols = $data['protocols']; $this->prefixes = $data['prefixes']; $this->dirs = $data['dirs']; } /** * @param string $a * @param string $b * @return int */ protected function sortPrefixKeys(string $a, string $b): int { $la = strlen($a); $lb = strlen($b); if ($lb > $la) { return 1; } if ($lb === $la) { return $b < $a ? 1 : ($b === $a ? 0 : -1); } return -1; } /** * @param Uri $uri * @return string|null */ protected function resolvePath(Uri $uri): ?string { $id = (string)$uri; if (isset($this->files[$id])) { return $this->files[$id]; } $scheme = $uri->scheme(); if (isset($this->dirs[$scheme])) { $host = (string)$uri->host(); if (isset($this->dirs[$scheme][$host])) { return $this->dirs[$scheme][$host] . '/' . ltrim($uri->path(), '/'); } unset($host); } $path = null; foreach ($this->prefixes as $prefix => $dir) { if ($prefix === '' || strpos($id, $prefix) === 0) { $path = substr($id, strlen($prefix)); if ($path === false || $path === '') { $path = null; continue; } $path = Uri::parseComponents($path); if ($path && isset($path['path'])) { $path = $dir . '/' . ltrim($path['path'], '/'); break; } $path = null; } } return $path; } } json-schema/src/Resolvers/ContentMediaTypeResolver.php 0000777 00000007337 15252016106 0017214 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Resolvers; use finfo; use Opis\JsonSchema\ContentMediaType; class ContentMediaTypeResolver { /** @var callable[]|ContentMediaType[] */ protected array $media; /** @var callable|null|ContentMediaType */ protected $defaultMedia = null; /** * @param callable[]|ContentMediaType[] $media * @param callable|ContentMediaType|null $defaultMedia */ public function __construct(array $media = [], $defaultMedia = null) { $media += [ 'application/json' => self::class . '::IsJsonEncoded', ]; $this->media = $media; $this->defaultMedia = $defaultMedia ?? self::class . '::IsEncodedAsType'; } /** * @param string $name * @return callable|ContentMediaType|string|null */ public function resolve(string $name) { return $this->media[$name] ?? $this->defaultMedia; } /** * @param string $name * @param ContentMediaType $media * @return ContentMediaTypeResolver */ public function register(string $name, ContentMediaType $media): self { $this->media[$name] = $media; return $this; } /** * @param string $name * @param callable $media * @return ContentMediaTypeResolver */ public function registerCallable(string $name, callable $media): self { $this->media[$name] = $media; return $this; } /** * @param string $name * @return bool */ public function unregister(string $name): bool { if (isset($this->media[$name])) { unset($this->media[$name]); return true; } return false; } /** * @param callable|ContentMediaType|null $handler * @return ContentMediaTypeResolver */ public function setDefaultHandler($handler): self { $this->defaultMedia = $handler; return $this; } public function __serialize(): array { return [ 'media' => $this->media, 'defaultMedia' => $this->defaultMedia, ]; } public function __unserialize(array $data): void { $this->media = $data['media']; $this->defaultMedia = $data['defaultMedia']; } public static function IsJsonEncoded(string $value, /** @noinspection PhpUnusedParameterInspection */ string $type): bool { json_decode($value); return json_last_error() === JSON_ERROR_NONE; } public static function IsEncodedAsType(string $value, string $type): bool { /** @var finfo|null|bool $finfo */ static $finfo = false; if ($finfo === false) { if (!class_exists(finfo::class)) { $finfo = null; return false; } $finfo = new finfo(FILEINFO_MIME_TYPE); } elseif (!$finfo) { return false; } $r = $finfo->buffer($value); return $r == $type || $r == 'application/x-empty'; } } json-schema/src/Resolvers/ContentEncodingResolver.php 0000777 00000006715 15252016106 0017060 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Resolvers; use Opis\JsonSchema\ContentEncoding; class ContentEncodingResolver { /** @var callable[]|ContentEncoding[] */ protected array $list; /** @var callable|ContentEncoding|null */ protected $defaultEncoding = null; /** * @param callable[]|ContentEncoding[] $list * @param callable|ContentEncoding|null $defaultEncoding */ public function __construct(array $list = [], $defaultEncoding = null) { $list += [ 'binary' => self::class . '::DecodeBinary', 'base64' => self::class . '::DecodeBase64', 'quoted-printable' => self::class . '::DecodeQuotedPrintable', ]; $this->list = $list; $this->defaultEncoding = $defaultEncoding; } /** * @param string $name * @return callable|ContentEncoding|string|null */ public function resolve(string $name) { return $this->list[$name] ?? $this->defaultEncoding; } /** * @param string $name * @param ContentEncoding $encoding * @return ContentEncodingResolver */ public function register(string $name, ContentEncoding $encoding): self { $this->list[$name] = $encoding; return $this; } /** * @param string $name * @param callable $encoding * @return ContentEncodingResolver */ public function registerCallable(string $name, callable $encoding): self { $this->list[$name] = $encoding; return $this; } /** * @param string $name * @return bool */ public function unregister(string $name): bool { if (isset($this->list[$name])) { unset($this->list[$name]); return true; } return false; } /** * @param callable|ContentEncoding|null $handler * @return $this */ public function setDefaultHandler($handler): self { $this->defaultEncoding = $handler; return $this; } public function __serialize(): array { return [ 'list' => $this->list, 'defaultEncoding' => $this->defaultEncoding, ]; } public function __unserialize(array $data): void { $this->list = $data['list']; $this->defaultEncoding = $data['defaultEncoding'] ?? null; } public static function DecodeBinary(string $value): ?string { return $value; } public static function DecodeBase64(string $value): ?string { $value = base64_decode($value, true); return is_string($value) ? $value : null; } public static function DecodeQuotedPrintable(string $value): ?string { return quoted_printable_decode($value); } } json-schema/src/Resolvers/FilterResolver.php 0000777 00000016130 15252016106 0015214 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Resolvers; use Opis\JsonSchema\{Helper, Filter}; use Opis\JsonSchema\Filters\{CommonFilters, DataExistsFilter, DateTimeFilters, FilterExistsFilter, FormatExistsFilter, SchemaExistsFilter, SlotExistsFilter, GlobalVarExistsFilter}; class FilterResolver { /** @var Filter[][][] */ protected array $filters = []; /** @var FilterResolver[] */ protected array $ns = []; protected string $separator; protected string $defaultNS; /** * FilterResolver constructor. * @param string $ns_separator * @param string $default_ns */ public function __construct(string $ns_separator = '::', string $default_ns = 'default') { $this->separator = $ns_separator; $this->defaultNS = $default_ns; $this->registerDefaultFilters(); } /** * You can override this to add/remove default filters */ protected function registerDefaultFilters(): void { $this->registerMultipleTypes("schema-exists", new SchemaExistsFilter()); $this->registerMultipleTypes("data-exists", new DataExistsFilter()); $this->registerMultipleTypes("global-exists", new GlobalVarExistsFilter()); $this->registerMultipleTypes("slot-exists", new SlotExistsFilter()); $this->registerMultipleTypes("filter-exists", new FilterExistsFilter()); $this->registerMultipleTypes("format-exists", new FormatExistsFilter()); $cls = DateTimeFilters::class . "::"; $this->registerCallable("string", "min-date", $cls . "MinDate"); $this->registerCallable("string", "max-date", $cls . "MaxDate"); $this->registerCallable("string", "not-date", $cls . "NotDate"); $this->registerCallable("string", "min-time", $cls . "MinTime"); $this->registerCallable("string", "max-time", $cls . "MaxTime"); $this->registerCallable("string", "min-datetime", $cls . "MinDateTime"); $this->registerCallable("string", "max-datetime", $cls . "MaxDateTime"); $cls = CommonFilters::class . "::"; $this->registerCallable("string", "regex", $cls . "Regex"); $this->registerMultipleTypes("equals", $cls . "Equals"); } /** * @param string $name * @param string $type * @return Filter|callable|null */ public function resolve(string $name, string $type) { [$ns, $name] = $this->parseName($name); if (isset($this->filters[$ns][$name])) { return $this->filters[$ns][$name][$type] ?? null; } if (!isset($this->ns[$ns])) { return null; } $this->filters[$ns][$name] = $this->ns[$ns]->resolveAll($name); return $this->filters[$ns][$name][$type] ?? null; } /** * @param string $name * @return Filter[]|callable[]|null */ public function resolveAll(string $name): ?array { [$ns, $name] = $this->parseName($name); if (isset($this->filters[$ns][$name])) { return $this->filters[$ns][$name]; } if (!isset($this->ns[$ns])) { return null; } return $this->filters[$ns][$name] = $this->ns[$ns]->resolveAll($name); } /** * @param string $type * @param string $name * @param Filter $filter * @return FilterResolver */ public function register(string $type, string $name, Filter $filter): self { [$ns, $name] = $this->parseName($name); $this->filters[$ns][$name][$type] = $filter; return $this; } /** * @param string $name * @param string|null $type * @return bool */ public function unregister(string $name, ?string $type = null): bool { [$ns, $name] = $this->parseName($name); if (!isset($this->filters[$ns][$name])) { return false; } if ($type === null) { unset($this->filters[$ns][$name]); return true; } if (isset($this->filters[$ns][$name][$type])) { unset($this->filters[$ns][$name][$type]); return true; } return false; } /** * @param string $name * @param callable|Filter $filter * @param array|null $types * @return FilterResolver */ public function registerMultipleTypes(string $name, $filter, ?array $types = null): self { [$ns, $name] = $this->parseName($name); $types = $types ?? Helper::JSON_TYPES; foreach ($types as $type) { $this->filters[$ns][$name][$type] = $filter; } return $this; } /** * @param string $type * @param string $name * @param callable $filter * @return FilterResolver */ public function registerCallable(string $type, string $name, callable $filter): self { [$ns, $name] = $this->parseName($name); $this->filters[$ns][$name][$type] = $filter; return $this; } /** * @param string $ns * @param FilterResolver $resolver * @return FilterResolver */ public function registerNS(string $ns, FilterResolver $resolver): self { $this->ns[$ns] = $resolver; return $this; } /** * @param string $ns * @return bool */ public function unregisterNS(string $ns): bool { if (isset($this->filters[$ns])) { unset($this->filters[$ns]); unset($this->ns[$ns]); return true; } if (isset($this->ns[$ns])) { unset($this->ns[$ns]); return true; } return false; } public function __serialize(): array { return [ 'separator' => $this->separator, 'defaultNS' => $this->defaultNS, 'ns' => $this->ns, 'filters' => $this->filters, ]; } public function __unserialize(array $data): void { $this->separator = $data['separator']; $this->defaultNS = $data['defaultNS']; $this->ns = $data['ns']; $this->filters = $data['filters']; } /** * @param string $name * @return array */ protected function parseName(string $name): array { $name = strtolower($name); if (strpos($name, $this->separator) === false) { return [$this->defaultNS, $name]; } return explode($this->separator, $name, 2); } } json-schema/src/Parsers/Pragmas/SlotsPragmaParser.php 0000777 00000004261 15252016106 0016705 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Pragmas; use Opis\JsonSchema\Pragma; use Opis\JsonSchema\Pragmas\SlotsPragma; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{PragmaParser, SchemaParser}; class SlotsPragmaParser extends PragmaParser { /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Pragma { if (!$parser->option('allowSlots') || !$this->pragmaExists($info)) { return null; } $value = $this->pragmaValue($info); if (!is_object($value)) { throw $this->pragmaException('Pragma {pragma} must be an object', $info); } $list = []; foreach ($value as $name => $slot) { if ($slot === null) { continue; } if (is_bool($slot)) { $list[$name] = $parser->parseSchema(new SchemaInfo( $slot, null, $info->base(), $info->root(), array_merge($info->path(), [$this->pragma, $name]), $info->draft() ?? $parser->defaultDraftVersion() )); } elseif (is_string($slot) || is_object($slot)) { $list[$name] = $slot; } else { throw $this->pragmaException('Pragma {pragma} contains invalid value for slot ' . $name, $info); } } return $list ? new SlotsPragma($list) : null; } } json-schema/src/Parsers/Pragmas/MaxErrorsPragmaParser.php 0000777 00000002664 15252016106 0017530 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Pragmas; use Opis\JsonSchema\Pragma; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Pragmas\MaxErrorsPragma; use Opis\JsonSchema\Parsers\{PragmaParser, SchemaParser}; class MaxErrorsPragmaParser extends PragmaParser { /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Pragma { if (!$this->pragmaExists($info)) { return null; } $value = $this->pragmaValue($info); if (!is_int($value)) { throw $this->pragmaException('Pragma {pragma} must be an integer', $info); } return new MaxErrorsPragma($value); } } json-schema/src/Parsers/Pragmas/CastPragmaParser.php 0000777 00000002771 15252016106 0016477 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Pragmas; use Opis\JsonSchema\Helper; use Opis\JsonSchema\Pragma; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Pragmas\CastPragma; use Opis\JsonSchema\Parsers\{PragmaParser, SchemaParser}; class CastPragmaParser extends PragmaParser { /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Pragma { if (!$this->pragmaExists($info)) { return null; } $value = $this->pragmaValue($info); if (!is_string($value) || !Helper::isValidJsonType($value)) { throw $this->pragmaException('Pragma {pragma} must contain a valid json type name', $info); } return new CastPragma($value); } } json-schema/src/Parsers/Pragmas/GlobalsPragmaParser.php 0000777 00000003131 15252016106 0017157 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Pragmas; use Opis\JsonSchema\Pragma; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Pragmas\GlobalsPragma; use Opis\JsonSchema\Parsers\{PragmaParser, SchemaParser, VariablesTrait}; class GlobalsPragmaParser extends PragmaParser { use VariablesTrait; /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Pragma { if (!$parser->option('allowGlobals') || !$this->pragmaExists($info)) { return null; } $value = $this->pragmaValue($info); if (!is_object($value)) { throw $this->pragmaException('Pragma {pragma} must be an object', $info); } $value = get_object_vars($value); return $value ? new GlobalsPragma($this->createVariables($parser, $value)) : null; } } json-schema/src/Parsers/KeywordValidators/PragmaKeywordValidatorParser.php 0000777 00000003776 15252016106 0023170 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\KeywordValidators; use Opis\JsonSchema\KeywordValidator; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\KeywordValidators\PragmaKeywordValidator; use Opis\JsonSchema\Parsers\{KeywordValidatorParser, SchemaParser}; class PragmaKeywordValidatorParser extends KeywordValidatorParser { /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?KeywordValidator { if (!$parser->option('allowPragmas') || !$this->keywordExists($info)) { return null; } $value = $this->keywordValue($info); if (!is_object($value)) { throw $this->keywordException('{keyword} must be an object', $info); } $list = []; $draft = $info->draft() ?? $parser->defaultDraftVersion(); $pragmaInfo = new SchemaInfo($value, null, $info->id() ?? $info->base(), $info->root(), array_merge($info->path(), [$this->keyword]), $draft); foreach ($parser->draft($draft)->pragmas() as $pragma) { if ($handler = $pragma->parse($pragmaInfo, $parser, $shared)) { $list[] = $handler; } } return $list ? new PragmaKeywordValidator($list) : null; } } json-schema/src/Parsers/Vocabulary.php 0000777 00000006355 15252016106 0014017 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; abstract class Vocabulary { /** @var KeywordParser[] */ protected array $keywords; /** @var KeywordValidatorParser[] */ protected array $keywordValidators; /** @var PragmaParser[] */ protected array $pragmas; /** * @param KeywordParser[] $keywords * @param KeywordValidatorParser[] $keywordValidators * @param PragmaParser[] $pragmas */ public function __construct(array $keywords = [], array $keywordValidators = [], array $pragmas = []) { $this->keywords = $keywords; $this->keywordValidators = $keywordValidators; $this->pragmas = $pragmas; } /** * @return KeywordParser[] */ public function keywords(): array { return $this->keywords; } /** * @return KeywordValidatorParser[] */ public function keywordValidators(): array { return $this->keywordValidators; } /** * @return PragmaParser[] */ public function pragmas(): array { return $this->pragmas; } /** * @param KeywordParser $keyword * @return Vocabulary */ public function appendKeyword(KeywordParser $keyword): self { $this->keywords[] = $keyword; return $this; } /** * @param KeywordParser $keyword * @return Vocabulary */ public function prependKeyword(KeywordParser $keyword): self { array_unshift($this->keywords, $keyword); return $this; } /** * @param KeywordValidatorParser $keywordValidatorParser * @return Vocabulary */ public function appendKeywordValidator(KeywordValidatorParser $keywordValidatorParser): self { $this->keywordValidators[] = $keywordValidatorParser; return $this; } /** * @param KeywordValidatorParser $keywordValidator * @return Vocabulary */ public function prependKeywordValidator(KeywordValidatorParser $keywordValidator): self { array_unshift($this->keywordValidators, $keywordValidator); return $this; } /** * @param PragmaParser $pragma * @return Vocabulary */ public function appendPragma(PragmaParser $pragma): self { $this->pragmas[] = $pragma; return $this; } /** * @param PragmaParser $pragma * @return Vocabulary */ public function prependPragma(PragmaParser $pragma): self { array_unshift($this->pragmas, $pragma); return $this; } } json-schema/src/Parsers/Drafts/Draft07.php 0000777 00000002346 15252016106 0014336 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Drafts; use Opis\JsonSchema\Parsers\Keywords\IfThenElseKeywordParser; class Draft07 extends Draft06 { /** * @inheritDoc */ public function version(): string { return '07'; } /** * @inheritDoc */ protected function getKeywordParsers(): array { $keywords = parent::getKeywordParsers(); $keywords[] = new IfThenElseKeywordParser('if', 'then', 'else'); return $keywords; } } json-schema/src/Parsers/Drafts/Draft06.php 0000777 00000010634 15252016106 0014334 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Drafts; use Opis\JsonSchema\Parsers\Draft; use Opis\JsonSchema\Parsers\KeywordParser; use Opis\JsonSchema\Parsers\Keywords\{ AdditionalItemsKeywordParser, AdditionalPropertiesKeywordParser, AllOfKeywordParser, AnyOfKeywordParser, ConstKeywordParser, ContainsKeywordParser, ContentEncodingKeywordParser, ContentMediaTypeKeywordParser, DefaultKeywordParser, DependenciesKeywordParser, EnumKeywordParser, ExclusiveMaximumKeywordParser, ExclusiveMinimumKeywordParser, FormatKeywordParser, ItemsKeywordParser, MaximumKeywordParser, MaxItemsKeywordParser, MaxLengthKeywordParser, MaxPropertiesKeywordParser, MinimumKeywordParser, MinItemsKeywordParser, MinLengthKeywordParser, MinPropertiesKeywordParser, MultipleOfKeywordParser, NotKeywordParser, OneOfKeywordParser, PatternKeywordParser, PatternPropertiesKeywordParser, PropertiesKeywordParser, PropertyNamesKeywordParser, RefKeywordParser, RequiredKeywordParser, TypeKeywordParser, UniqueItemsKeywordParser }; class Draft06 extends Draft { /** * @inheritDoc */ public function version(): string { return '06'; } public function allowKeywordsAlongsideRef(): bool { return false; } /** * @inheritDoc */ public function supportsAnchorId(): bool { return false; } /** * @inheritDoc */ protected function getRefKeywordParser(): KeywordParser { return new RefKeywordParser('$ref'); } /** * @inheritDoc */ protected function getKeywordParsers(): array { return [ // Generic new TypeKeywordParser('type'), new ConstKeywordParser('const'), new EnumKeywordParser('enum'), new FormatKeywordParser('format'), // String new MinLengthKeywordParser('minLength'), new MaxLengthKeywordParser('maxLength'), new PatternKeywordParser("pattern"), new ContentEncodingKeywordParser('contentEncoding'), new ContentMediaTypeKeywordParser('contentMediaType'), // Number new MinimumKeywordParser('minimum', 'exclusiveMinimum'), new MaximumKeywordParser('maximum', 'exclusiveMaximum'), new ExclusiveMinimumKeywordParser('exclusiveMinimum'), new ExclusiveMaximumKeywordParser('exclusiveMaximum'), new MultipleOfKeywordParser('multipleOf'), // Array new MinItemsKeywordParser('minItems'), new MaxItemsKeywordParser('maxItems'), new UniqueItemsKeywordParser('uniqueItems'), new ContainsKeywordParser('contains'), new ItemsKeywordParser('items'), new AdditionalItemsKeywordParser('additionalItems'), // Object new MinPropertiesKeywordParser('minProperties'), new MaxPropertiesKeywordParser('maxProperties'), new RequiredKeywordParser('required'), new DependenciesKeywordParser('dependencies'), new PropertyNamesKeywordParser('propertyNames'), new PropertiesKeywordParser('properties'), new PatternPropertiesKeywordParser('patternProperties'), new AdditionalPropertiesKeywordParser('additionalProperties'), // Conditionals new NotKeywordParser('not'), new AnyOfKeywordParser('anyOf'), new AllOfKeywordParser('allOf'), new OneOfKeywordParser('oneOf'), // Optional new DefaultKeywordParser('default'), ]; } } json-schema/src/Parsers/Drafts/Draft202012.php 0000777 00000012727 15252016106 0014642 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Drafts; use Opis\JsonSchema\Parsers\Draft; use Opis\JsonSchema\Parsers\KeywordParser; use Opis\JsonSchema\Parsers\Keywords\{AdditionalItemsKeywordParser, AdditionalPropertiesKeywordParser, AllOfKeywordParser, AnyOfKeywordParser, ConstKeywordParser, ContainsKeywordParser, ContentEncodingKeywordParser, ContentMediaTypeKeywordParser, ContentSchemaKeywordParser, DefaultKeywordParser, DependenciesKeywordParser, DependentRequiredKeywordParser, DependentSchemasKeywordParser, EnumKeywordParser, ExclusiveMaximumKeywordParser, ExclusiveMinimumKeywordParser, FormatKeywordParser, IfThenElseKeywordParser, ItemsKeywordParser, MaximumKeywordParser, MaxItemsKeywordParser, MaxLengthKeywordParser, MaxPropertiesKeywordParser, MinimumKeywordParser, MinItemsKeywordParser, MinLengthKeywordParser, MinPropertiesKeywordParser, MultipleOfKeywordParser, NotKeywordParser, OneOfKeywordParser, PatternKeywordParser, PatternPropertiesKeywordParser, PropertiesKeywordParser, PropertyNamesKeywordParser, RefKeywordParser, RequiredKeywordParser, TypeKeywordParser, UnevaluatedItemsKeywordParser, UnevaluatedPropertiesKeywordParser, UniqueItemsKeywordParser }; class Draft202012 extends Draft { /** * @inheritDoc */ public function version(): string { return '2020-12'; } public function allowKeywordsAlongsideRef(): bool { return true; } /** * @inheritDoc */ public function supportsAnchorId(): bool { return true; } /** * @inheritDoc */ protected function getRefKeywordParser(): KeywordParser { return new RefKeywordParser('$ref', [ ['ref' => '$dynamicRef', 'anchor' => '$dynamicAnchor', 'fragment' => true], ['ref' => '$recursiveRef', 'anchor' => '$recursiveAnchor', 'fragment' => false], ]); } /** * @inheritDoc */ protected function getKeywordParsers(): array { return [ // Generic new TypeKeywordParser('type'), new ConstKeywordParser('const'), new EnumKeywordParser('enum'), new FormatKeywordParser('format'), // String new MinLengthKeywordParser('minLength'), new MaxLengthKeywordParser('maxLength'), new PatternKeywordParser("pattern"), new ContentEncodingKeywordParser('contentEncoding'), new ContentMediaTypeKeywordParser('contentMediaType'), new ContentSchemaKeywordParser('contentSchema'), // Number new MinimumKeywordParser('minimum', 'exclusiveMinimum'), new MaximumKeywordParser('maximum', 'exclusiveMaximum'), new ExclusiveMinimumKeywordParser('exclusiveMinimum'), new ExclusiveMaximumKeywordParser('exclusiveMaximum'), new MultipleOfKeywordParser('multipleOf'), // Array new MinItemsKeywordParser('minItems'), new MaxItemsKeywordParser('maxItems'), new UniqueItemsKeywordParser('uniqueItems'), new ContainsKeywordParser('contains', 'minContains', 'maxContains'), new ItemsKeywordParser('prefixItems', ItemsKeywordParser::ONLY_ARRAY), new ItemsKeywordParser('items', ItemsKeywordParser::ONLY_SCHEMA, 'prefixItems'), // keep for draft-2019-09 compatibility new AdditionalItemsKeywordParser('additionalItems'), // Object new MinPropertiesKeywordParser('minProperties'), new MaxPropertiesKeywordParser('maxProperties'), new RequiredKeywordParser('required'), new DependenciesKeywordParser('dependencies'), // keep for draft-07 compatibility new DependentRequiredKeywordParser('dependentRequired'), new DependentSchemasKeywordParser('dependentSchemas'), new PropertyNamesKeywordParser('propertyNames'), new PropertiesKeywordParser('properties'), new PatternPropertiesKeywordParser('patternProperties'), new AdditionalPropertiesKeywordParser('additionalProperties'), // Conditionals new IfThenElseKeywordParser('if', 'then', 'else'), new AnyOfKeywordParser('anyOf'), new AllOfKeywordParser('allOf'), new OneOfKeywordParser('oneOf'), new NotKeywordParser('not'), // Unevaluated new UnevaluatedPropertiesKeywordParser('unevaluatedProperties'), new UnevaluatedItemsKeywordParser('unevaluatedItems'), // Optional new DefaultKeywordParser('default'), ]; } } json-schema/src/Parsers/Drafts/Draft201909.php 0000777 00000012307 15252016106 0014652 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Drafts; use Opis\JsonSchema\Parsers\Draft; use Opis\JsonSchema\Parsers\KeywordParser; use Opis\JsonSchema\Parsers\Keywords\{AdditionalItemsKeywordParser, AdditionalPropertiesKeywordParser, AllOfKeywordParser, AnyOfKeywordParser, ConstKeywordParser, ContainsKeywordParser, ContentEncodingKeywordParser, ContentMediaTypeKeywordParser, ContentSchemaKeywordParser, DefaultKeywordParser, DependenciesKeywordParser, DependentRequiredKeywordParser, DependentSchemasKeywordParser, EnumKeywordParser, ExclusiveMaximumKeywordParser, ExclusiveMinimumKeywordParser, FormatKeywordParser, IfThenElseKeywordParser, ItemsKeywordParser, MaximumKeywordParser, MaxItemsKeywordParser, MaxLengthKeywordParser, MaxPropertiesKeywordParser, MinimumKeywordParser, MinItemsKeywordParser, MinLengthKeywordParser, MinPropertiesKeywordParser, MultipleOfKeywordParser, NotKeywordParser, OneOfKeywordParser, PatternKeywordParser, PatternPropertiesKeywordParser, PropertiesKeywordParser, PropertyNamesKeywordParser, RefKeywordParser, RequiredKeywordParser, TypeKeywordParser, UnevaluatedItemsKeywordParser, UnevaluatedPropertiesKeywordParser, UniqueItemsKeywordParser}; class Draft201909 extends Draft { /** * @inheritDoc */ public function version(): string { return '2019-09'; } public function allowKeywordsAlongsideRef(): bool { return true; } /** * @inheritDoc */ public function supportsAnchorId(): bool { return true; } /** * @inheritDoc */ protected function getRefKeywordParser(): KeywordParser { return new RefKeywordParser('$ref', [ ['ref' => '$recursiveRef', 'anchor' => '$recursiveAnchor', 'fragment' => false], ]); } /** * @inheritDoc */ protected function getKeywordParsers(): array { return [ // Generic new TypeKeywordParser('type'), new ConstKeywordParser('const'), new EnumKeywordParser('enum'), new FormatKeywordParser('format'), // String new MinLengthKeywordParser('minLength'), new MaxLengthKeywordParser('maxLength'), new PatternKeywordParser("pattern"), new ContentEncodingKeywordParser('contentEncoding'), new ContentMediaTypeKeywordParser('contentMediaType'), new ContentSchemaKeywordParser('contentSchema'), // Number new MinimumKeywordParser('minimum', 'exclusiveMinimum'), new MaximumKeywordParser('maximum', 'exclusiveMaximum'), new ExclusiveMinimumKeywordParser('exclusiveMinimum'), new ExclusiveMaximumKeywordParser('exclusiveMaximum'), new MultipleOfKeywordParser('multipleOf'), // Array new MinItemsKeywordParser('minItems'), new MaxItemsKeywordParser('maxItems'), new UniqueItemsKeywordParser('uniqueItems'), new ContainsKeywordParser('contains', 'minContains', 'maxContains'), new ItemsKeywordParser('items'), new AdditionalItemsKeywordParser('additionalItems'), // Object new MinPropertiesKeywordParser('minProperties'), new MaxPropertiesKeywordParser('maxProperties'), new RequiredKeywordParser('required'), new DependenciesKeywordParser('dependencies'), // keep for draft-07 compatibility new DependentRequiredKeywordParser('dependentRequired'), new DependentSchemasKeywordParser('dependentSchemas'), new PropertyNamesKeywordParser('propertyNames'), new PropertiesKeywordParser('properties'), new PatternPropertiesKeywordParser('patternProperties'), new AdditionalPropertiesKeywordParser('additionalProperties'), // Conditionals new IfThenElseKeywordParser('if', 'then', 'else'), new AnyOfKeywordParser('anyOf'), new AllOfKeywordParser('allOf'), new OneOfKeywordParser('oneOf'), new NotKeywordParser('not'), // Unevaluated new UnevaluatedPropertiesKeywordParser('unevaluatedProperties'), new UnevaluatedItemsKeywordParser('unevaluatedItems'), // Optional new DefaultKeywordParser('default'), ]; } } json-schema/src/Parsers/KeywordValidatorParser.php 0000777 00000002262 15252016106 0016350 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\KeywordValidator; abstract class KeywordValidatorParser { use KeywordParserTrait; /** * @param SchemaInfo $info * @param SchemaParser $parser * @param object $shared * @return KeywordValidator|null */ abstract public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?KeywordValidator; } json-schema/src/Parsers/ResolverTrait.php 0000777 00000002256 15252016106 0014511 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; use Opis\JsonSchema\Helper; trait ResolverTrait { /** * @param array $list * @return array */ protected function resolveSubTypes(array $list): array { foreach (Helper::JSON_SUBTYPES as $sub => $super) { if (!isset($list[$sub]) && isset($list[$super])) { $list[$sub] = $list[$super]; } } return $list; } } json-schema/src/Parsers/DataKeywordTrait.php 0000777 00000003357 15252016106 0015131 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; use Opis\JsonSchema\JsonPointer; trait DataKeywordTrait { /** * @param $value * @return JsonPointer|null */ protected function getDataKeywordPointer($value): ?JsonPointer { if (!is_object($value) || !property_exists($value, '$data') || !is_string($value->{'$data'}) || count(get_object_vars($value)) !== 1) { return null; } return JsonPointer::parse($value->{'$data'}); } /** * @param SchemaParser $parser * @param string|null $keyword * @return bool */ protected function isDataKeywordAllowed(SchemaParser $parser, ?string $keyword = null): bool { if (!($enabled = $parser->option('allowDataKeyword'))) { return false; } if ($enabled === true) { return true; } if ($keyword === null) { return false; } return is_array($enabled) && in_array($keyword, $enabled); } } json-schema/src/Parsers/PragmaParser.php 0000777 00000005062 15252016106 0014266 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Exceptions\InvalidPragmaException; use Opis\JsonSchema\Pragma; abstract class PragmaParser { protected string $pragma; /** * @param string $pragma */ public function __construct(string $pragma) { $this->pragma = $pragma; } /** * @param SchemaInfo $info * @param SchemaParser $parser * @param object $shared * @return Pragma|null */ abstract public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Pragma; /** * @param object|SchemaInfo $schema * @param string|null $pragma * @return bool */ protected function pragmaExists(object $schema, ?string $pragma = null): bool { if ($schema instanceof SchemaInfo) { $schema = $schema->isObject() ? $schema->data() : null; } return is_object($schema) && property_exists($schema, $pragma ?? $this->pragma); } /** * @param object|SchemaInfo $schema * @param string|null $pragma * @return mixed */ protected function pragmaValue(object $schema, ?string $pragma = null) { if ($schema instanceof SchemaInfo) { $schema = $schema->isObject() ? $schema->data() : null; } return is_object($schema) ? $schema->{$pragma ?? $this->pragma} : null; } /** * @param string $message * @param SchemaInfo $info * @param string|null $pragma * @return InvalidPragmaException */ protected function pragmaException(string $message, SchemaInfo $info, ?string $pragma = null): InvalidPragmaException { $pragma = $pragma ?? $this->pragma; return new InvalidPragmaException(str_replace('{pragma}', $pragma, $message), $pragma, $info); } } json-schema/src/Parsers/VariablesTrait.php 0000777 00000002563 15252016106 0014621 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; use Opis\JsonSchema\Variables; use Opis\JsonSchema\Variables\{VariablesContainer}; trait VariablesTrait { /** * @param SchemaParser $parser * @param array|object $vars * @param bool $lazy * @return Variables */ protected function createVariables(SchemaParser $parser, $vars, bool $lazy = true): Variables { return new VariablesContainer( $vars, $lazy, $parser->option('varRefKey', '$ref'), $parser->option('varEachKey', '$each'), $parser->option('varDefaultKey', 'default') ); } } json-schema/src/Parsers/DefaultVocabulary.php 0000777 00000003767 15252016106 0015330 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; use Opis\JsonSchema\Parsers\Keywords\{ FiltersKeywordParser, SlotsKeywordParser }; use Opis\JsonSchema\Parsers\Pragmas\{CastPragmaParser, GlobalsPragmaParser, MaxErrorsPragmaParser, SlotsPragmaParser}; use Opis\JsonSchema\Parsers\KeywordValidators\PragmaKeywordValidatorParser; class DefaultVocabulary extends Vocabulary { /** * @param KeywordParser[] $keywords * @param KeywordValidatorParser[] $keywordValidators * @param PragmaParser[] $pragmas */ public function __construct(array $keywords = [], array $keywordValidators = [], array $pragmas = []) { $keywords = array_merge($keywords, [ new FiltersKeywordParser('$filters'), new SlotsKeywordParser('$slots'), ]); $keywordValidators = array_merge([ // $pragma has priority new PragmaKeywordValidatorParser('$pragma'), ], $keywordValidators); $pragmas = array_merge($pragmas, [ new MaxErrorsPragmaParser('maxErrors'), new SlotsPragmaParser('slots'), new GlobalsPragmaParser('globals'), new CastPragmaParser('cast'), ]); parent::__construct($keywords, $keywordValidators, $pragmas); } } json-schema/src/Parsers/KeywordParser.php 0000777 00000003472 15252016106 0014506 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keyword; abstract class KeywordParser { const TYPE_PREPEND = '_prepend'; const TYPE_BEFORE = '_before'; const TYPE_AFTER = '_after'; const TYPE_APPEND = '_append'; const TYPE_AFTER_REF = '_after_ref'; const TYPE_STRING = 'string'; const TYPE_NUMBER = 'number'; const TYPE_ARRAY = 'array'; const TYPE_OBJECT = 'object'; use KeywordParserTrait; /** * The keyword type, can be one of the TYPE_* const * @return string */ abstract public function type(): string; /** * @param SchemaInfo $info * @param SchemaParser $parser * @param object $shared * @return Keyword|null */ abstract public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword; /** * @param SchemaInfo $info * @return bool */ protected function trackEvaluated(SchemaInfo $info): bool { $draft = $info->draft(); return $draft !== '06' && $draft !== '07'; } } json-schema/src/Parsers/Keywords/PatternKeywordParser.php 0000777 00000004026 15252016106 0017647 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\{Helper, Keyword}; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\{PatternKeyword, PatternDataKeyword}; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; class PatternKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_STRING; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new PatternDataKeyword($pointer); } } if (!is_string($value)) { throw $this->keywordException("{keyword} value must be a string", $info); } if (!Helper::isValidPattern($value)) { throw $this->keywordException("{keyword} value must be a valid regex", $info); } return new PatternKeyword($value); } } json-schema/src/Parsers/Keywords/UnevaluatedItemsKeywordParser.php 0000777 00000004243 15252016106 0021512 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; use Opis\JsonSchema\Keywords\UnevaluatedItemsKeyword; class UnevaluatedItemsKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_AFTER_REF; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema) || !$parser->option('allowUnevaluated')) { return null; } // if (!$this->makesSense($schema)) { // return null; // } $value = $this->keywordValue($schema); if (!is_bool($value) && !is_object($value)) { throw $this->keywordException("{keyword} must be a json schema (object or boolean)", $info); } return new UnevaluatedItemsKeyword($value); } protected function makesSense(object $schema): bool { if (property_exists($schema, 'additionalItems')) { return false; } // if (property_exists($schema, 'contains')) { // return false; // } if (property_exists($schema, 'items') && !is_array($schema->items)) { return false; } return true; } } json-schema/src/Parsers/Keywords/OneOfKeywordParser.php 0000777 00000004702 15252016106 0017241 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\OneOfKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class OneOfKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_AFTER; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_array($value)) { throw $this->keywordException("{keyword} should be an array of json schemas", $info); } if (!$value) { throw $this->keywordException("{keyword} must have at least one element", $info); } $valid = 0; foreach ($value as $index => $item) { if ($item === false) { continue; } if ($item === true) { if (++$valid > 1) { throw $this->keywordException("{keyword} contains multiple true values", $info); } continue; } if (!is_object($item)) { throw $this->keywordException("{keyword}[{$index}] must be a json schema", $info); } elseif (!count(get_object_vars($item))) { if (++$valid > 1) { throw $this->keywordException("{keyword} contains multiple true values", $info); } } } return new OneOfKeyword($value); } } json-schema/src/Parsers/Keywords/FormatKeywordParser.php 0000777 00000004207 15252016106 0017463 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\{FormatDataKeyword, FormatKeyword}; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser, ResolverTrait}; class FormatKeywordParser extends KeywordParser { use ResolverTrait; use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_BEFORE; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); $resolver = $parser->getFormatResolver(); if (!$resolver || !$parser->option('allowFormats') || !$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new FormatDataKeyword($pointer, $resolver); } } if (!is_string($value)) { throw $this->keywordException("{keyword} must be a string", $info); } $list = $resolver->resolveAll($value); if (!$list) { return null; } return new FormatKeyword($value, $this->resolveSubTypes($list)); } } json-schema/src/Parsers/Keywords/ConstKeywordParser.php 0000777 00000004334 15252016106 0017322 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\{Keyword, Helper}; use Opis\JsonSchema\Keywords\{ConstDataKeyword, ConstKeyword}; use Opis\JsonSchema\Parsers\{ KeywordParser, DataKeywordTrait, SchemaParser }; class ConstKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_BEFORE; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new ConstDataKeyword($pointer); } } $type = Helper::getJsonType($value); if ($type === null) { throw $this->keywordException("{keyword} contains unknown json data type", $info); } if (isset($shared->types)) { if (!Helper::jsonTypeMatches($type, $shared->types)) { throw $this->keywordException("{keyword} contains a value that doesn't match the type keyword", $info); } } else { $shared->types = [$type]; } return new ConstKeyword($value); } } json-schema/src/Parsers/Keywords/MinItemsKeywordParser.php 0000777 00000003712 15252016106 0017760 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{MinItemsDataKeyword, MinItemsKeyword}; class MinItemsKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_ARRAY; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new MinItemsDataKeyword($pointer); } } if (!is_int($value) || $value < 0) { throw $this->keywordException("{keyword} most be a positive integer", $info); } if ($value === 0) { return null; } return new MinItemsKeyword($value); } } json-schema/src/Parsers/Keywords/MaximumKeywordParser.php 0000777 00000005405 15252016106 0017651 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{ ExclusiveMaximumDataKeyword, ExclusiveMaximumKeyword, MaximumDataKeyword, MaximumKeyword }; class MaximumKeywordParser extends KeywordParser { use DataKeywordTrait; protected ?string $exclusiveKeyword; /** * @param string $keyword * @param string|null $exclusiveKeyword */ public function __construct(string $keyword, ?string $exclusiveKeyword = null) { parent::__construct($keyword); $this->exclusiveKeyword = $exclusiveKeyword; } /** * @inheritDoc */ public function type(): string { return self::TYPE_NUMBER; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); $exclusive = false; if ($parser->option('allowExclusiveMinMaxAsBool') && $this->exclusiveKeyword !== null && property_exists($schema, $this->exclusiveKeyword)) { $exclusive = $schema->{$this->exclusiveKeyword} === true; } if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return $exclusive ? new ExclusiveMaximumDataKeyword($pointer) : new MaximumDataKeyword($pointer); } } if (!is_int($value) && !is_float($value) || is_nan($value) || !is_finite($value)) { throw $this->keywordException('{keyword} must contain a valid number', $info); } return $exclusive ? new ExclusiveMaximumKeyword($value) : new MaximumKeyword($value); } } json-schema/src/Parsers/Keywords/ContentEncodingKeywordParser.php 0000777 00000003551 15252016106 0021315 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\ContentEncodingKeyword; use Opis\JsonSchema\Parsers\{DraftOptionTrait, KeywordParser, SchemaParser}; class ContentEncodingKeywordParser extends KeywordParser { use DraftOptionTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_STRING; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { if (!$this->optionAllowedForDraft('decodeContent', $info, $parser)) { return null; } $schema = $info->data(); $resolver = $parser->getContentEncodingResolver(); if (!$resolver || !$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_string($value)) { throw $this->keywordException("{keyword} must be a string", $info); } return new ContentEncodingKeyword(strtolower($value), $resolver); } } json-schema/src/Parsers/Keywords/TypeKeywordParser.php 0000777 00000004466 15252016106 0017163 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\{Helper, Keyword}; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\TypeKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class TypeKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_BEFORE; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $type = $this->keywordValue($schema); if (is_string($type)) { $type = [$type]; } elseif (!is_array($type)) { throw $this->keywordException('{keyword} can only be a string or an array of string', $info); } foreach ($type as $t) { if (!Helper::isValidJsonType($t)) { throw $this->keywordException("{keyword} contains invalid json type: {$t}", $info); } } $type = array_unique($type); if (!isset($shared->types)) { $shared->types = $type; } else { $shared->types = array_unique(array_merge($shared->types, $type)); } $count = count($type); if ($count === 0) { throw $this->keywordException("{keyword} cannot be an empty array", $info); } elseif ($count === 1) { $type = reset($type); } return new TypeKeyword($type); } } json-schema/src/Parsers/Keywords/FiltersKeywordParser.php 0000777 00000011423 15252016106 0017641 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\FiltersKeyword; use Opis\JsonSchema\Resolvers\FilterResolver; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser, ResolverTrait, VariablesTrait}; class FiltersKeywordParser extends KeywordParser { use ResolverTrait; use VariablesTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_APPEND; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$parser->option('allowFilters')) { return null; } $resolver = $parser->getFilterResolver(); if (!$resolver || !$this->keywordExists($schema)) { return null; } $filters = $this->parseFilters($parser, $resolver, $this->keywordValue($schema), $info); if (!$filters) { return null; } return new FiltersKeyword($filters); } /** * @param SchemaParser $parser * @param FilterResolver $filterResolver * @param mixed $filters * @param SchemaInfo $info * @return array|null */ protected function parseFilters( SchemaParser $parser, FilterResolver $filterResolver, $filters, SchemaInfo $info ): ?array { if (is_string($filters)) { if ($filters = $this->parseFilter($parser, $filterResolver, $filters, $info)) { return [$filters]; } return null; } if (is_object($filters)) { if ($filter = $this->parseFilter($parser, $filterResolver, $filters, $info)) { return [$filter]; } return null; } if (is_array($filters)) { if (!$filters) { return null; } $list = []; foreach ($filters as $filter) { if ($filter = $this->parseFilter($parser, $filterResolver, $filter, $info)) { $list[] = $filter; } } return $list ?: null; } throw $this->keywordException('{keyword} can be a non-empty string, an object or an array of string and objects', $info); } /** * @param SchemaParser $parser * @param FilterResolver $resolver * @param $filter * @param SchemaInfo $info * @return object|null */ protected function parseFilter( SchemaParser $parser, FilterResolver $resolver, $filter, SchemaInfo $info ): ?object { $vars = null; if (is_object($filter)) { if (!property_exists($filter, '$func') || !is_string($filter->{'$func'}) || $filter->{'$func'} === '') { throw $this->keywordException('$func (for {keyword}) must be a non-empty string', $info); } $vars = get_object_vars($filter); unset($vars['$func']); if (property_exists($filter, '$vars')) { if (!is_object($filter->{'$vars'})) { throw $this->keywordException('$vars (for {keyword}) must be an object', $info); } unset($vars['$vars']); $vars = get_object_vars($filter->{'$vars'}) + $vars; } $filter = $filter->{'$func'}; } elseif (!is_string($filter) || $filter === '') { throw $this->keywordException('{keyword} can be a non-empty string, an object or an array of string and objects', $info); } $list = $resolver->resolveAll($filter); if (!$list) { throw $this->keywordException("{keyword}: {$filter} doesn't exists", $info); } $list = $this->resolveSubTypes($list); return (object)[ 'name' => $filter, 'args' => $vars ? $this->createVariables($parser, $vars) : null, 'types' => $list, ]; } } json-schema/src/Parsers/Keywords/RefKeywordParser.php 0000777 00000020052 15252016106 0016743 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\Uri\UriTemplate; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\{Keyword, Schema, JsonPointer, Uri}; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser, VariablesTrait}; use Opis\JsonSchema\Keywords\{PointerRefKeyword, RecursiveRefKeyword, TemplateRefKeyword, URIRefKeyword}; class RefKeywordParser extends KeywordParser { use VariablesTrait; protected ?array $variations = null; public function __construct(string $keyword, ?array $variations = null) { parent::__construct($keyword); $this->variations = $variations; } /** * @inheritDoc */ public function type(): string { return self::TYPE_AFTER_REF; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $ref = null; $recursive = false; $schema = $info->data(); $variation = null; if ($this->keywordExists($schema)) { $ref = $this->keywordValue($schema); if (!is_string($ref) || $ref === '') { throw $this->keywordException('{keyword} must be a non-empty string', $info); } } elseif ($this->variations) { foreach ($this->variations as $v) { if (!$this->keywordExists($schema, $v['ref'])) { continue; } $ref = $this->keywordValue($schema, $v['ref']); if ($v['fragment']) { if (!preg_match('/^#[a-z][a-z0-9\\-.:_]*/i', $ref)) { $this->keywordException("{keyword} value is malformed", $info, $v['ref']); } } elseif ($ref !== '#') { $this->keywordException("{keyword} supports only '#' as value", $info, $v['ref']); } $variation = $v; $recursive = true; break; } if (!$recursive) { return null; } } else { return null; } // Mappers $mapper = null; if ($parser->option('allowMappers') && property_exists($schema, '$map')) { if (!is_object($schema->{'$map'}) && !is_array($schema->{'$map'})) { throw $this->keywordException('$map keyword must be an object or an array', $info, '$map'); } if (!empty($schema->{'$map'})) { $mapper = $this->createVariables($parser, $schema->{'$map'}); } } // Globals $globals = null; if ($parser->option('allowGlobals') && property_exists($schema, '$globals')) { if (!is_object($schema->{'$globals'})) { throw $this->keywordException('$globals keyword must be an object', $info, '$globals'); } if (!empty($schema->{'$globals'})) { $globals = $this->createVariables($parser, $schema->{'$globals'}); } } // Pass slots $slots = null; if ($parser->option('allowSlots') && property_exists($schema, '$inject')) { $slots = $this->parseInjectedSlots($info, $parser, '$inject'); } if ($recursive) { $ref = $info->idBaseRoot()->resolveRef($ref); if ($variation['fragment']) { return new RecursiveRefKeyword($ref->resolveRef('#'), $mapper, $globals, $slots, $variation['ref'], $variation['anchor'], $ref->fragment()); } return new RecursiveRefKeyword($ref, $mapper, $globals, $slots, $variation['ref'], $variation['anchor'], true); } if ($ref === '#') { return new URIRefKeyword(Uri::merge('#', $info->idBaseRoot()), $mapper, $globals, $slots, $this->keyword); } if ($parser->option('allowTemplates') && UriTemplate::isTemplate($ref)) { $tpl = new UriTemplate($ref); if ($tpl->hasPlaceholders()) { $vars = null; if (property_exists($schema, '$vars')) { if (!is_object($schema->{'$vars'})) { throw $this->keywordException('$vars keyword must be an object', $info, '$vars'); } if (!empty($schema->{'$vars'})) { $vars = $this->createVariables($parser, $schema->{'$vars'}); } } return new TemplateRefKeyword( $tpl, $vars, $mapper, $globals, $slots, $this->keyword, $parser->option('allowRelativeJsonPointerInRef') ); } unset($tpl); } if ($ref[0] === '#') { if (($pointer = JsonPointer::parse(substr($ref, 1))) && $pointer->isAbsolute()) { return new PointerRefKeyword($pointer, $mapper, $globals, $slots, $this->keyword); } } elseif ($parser->option('allowRelativeJsonPointerInRef') && ($pointer = JsonPointer::parse($ref)) && $pointer->isRelative()) { return new PointerRefKeyword($pointer, $mapper, $globals, $slots, $this->keyword); } $ref = Uri::merge($ref, $info->idBaseRoot(), true); if ($ref === null || !$ref->isAbsolute()) { throw $this->keywordException('{keyword} must be a valid uri, uri-reference, uri-template or json-pointer', $info); } return new URIRefKeyword($ref, $mapper, $globals, $slots, $this->keyword); } /** * @param SchemaInfo $info * @param SchemaParser $parser * @param string $keyword * @return string[]|object[]|Schema[] */ protected function parseInjectedSlots(SchemaInfo $info, SchemaParser $parser, string $keyword): ?array { $schema = $info->data(); if (!is_object($schema->{$keyword})) { throw $this->keywordException('{keyword} keyword value must be an object', $info, $keyword); } return $this->getSlotSchemas($info, $parser, $schema->{$keyword}, [$keyword]); } /** * @param SchemaInfo $info * @param SchemaParser $parser * @param object $slots * @param array $path * @return null */ protected function getSlotSchemas(SchemaInfo $info, SchemaParser $parser, object $slots, array $path): ?array { $keyword = null; if ($path) { $keyword = end($path); $path = array_merge($info->path(), $path); } else { $path = $info->path(); } $list = []; foreach ($slots as $name => $value) { if ($value === null) { continue; } if (is_string($value) || is_object($value)) { $list[$name] = $value; } elseif (is_bool($value)) { $list[$name] = $parser->parseSchema(new SchemaInfo( $value, null, $info->id() ?? $info->base(), $info->root(), array_merge($path, [$name]), $info->draft() ?? $parser->defaultDraftVersion() )); } else { throw $this->keywordException('Slots must contain valid json schemas or slot names', $info, $keyword); } } return $list ?: null; } } json-schema/src/Parsers/Keywords/MaxPropertiesKeywordParser.php 0000777 00000003652 15252016106 0021040 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{MaxPropertiesDataKeyword, MaxPropertiesKeywords}; class MaxPropertiesKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_OBJECT; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new MaxPropertiesDataKeyword($pointer); } } if (!is_int($value) || $value < 0) { throw $this->keywordException("{keyword} must be a non-negative integer", $info); } return new MaxPropertiesKeywords($value); } } json-schema/src/Parsers/Keywords/ExclusiveMaximumKeywordParser.php 0000777 00000004141 15252016106 0021535 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{ ExclusiveMaximumDataKeyword, ExclusiveMaximumKeyword }; class ExclusiveMaximumKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_NUMBER; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (is_bool($value) && $parser->option('allowExclusiveMinMaxAsBool')) { return null; } if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new ExclusiveMaximumDataKeyword($pointer); } } if (!is_int($value) && !is_float($value) || is_nan($value) || !is_finite($value)) { throw $this->keywordException('{keyword} must contain a valid number', $info); } return new ExclusiveMaximumKeyword($value); } } json-schema/src/Parsers/Keywords/DefaultKeywordParser.php 0000777 00000004704 15252016106 0017621 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\{Keyword, Helper}; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\DefaultKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class DefaultKeywordParser extends KeywordParser { protected ?string $properties = null; /** * @inheritDoc */ public function __construct(string $keyword, ?string $properties = 'properties') { parent::__construct($keyword); $this->properties = $properties; } /** * @inheritDoc */ public function type(): string { return self::TYPE_APPEND; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$parser->option('allowDefaults')) { return null; } $defaults = null; if ($this->keywordExists($schema)) { $defaults = $this->keywordValue($schema); if (is_object($defaults)) { $defaults = (array)Helper::cloneValue($defaults); } else { $defaults = null; } } if ($this->properties !== null && property_exists($schema, $this->properties) && is_object($schema->{$this->properties})) { foreach ($schema->{$this->properties} as $name => $value) { if (is_object($value) && property_exists($value, $this->keyword)) { $defaults[$name] = $value->{$this->keyword}; } } } if (!$defaults) { return null; } return new DefaultKeyword($defaults); } } json-schema/src/Parsers/Keywords/AllOfKeywordParser.php 0000777 00000004441 15252016106 0017230 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\AllOfKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class AllOfKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_AFTER; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_array($value)) { throw $this->keywordException("{keyword} should be an array of json schemas", $info); } if (!$value) { throw $this->keywordException("{keyword} must have at least one element", $info); } $valid = 0; foreach ($value as $index => $item) { if ($item === false) { throw $this->keywordException("{keyword} contains false schema", $info); } if ($item === true) { $valid++; continue; } if (!is_object($item)) { throw $this->keywordException("{keyword}[{$index}] must be a json schema", $info); } elseif (!count(get_object_vars($item))) { $valid++; } } return $valid !== count($value) ? new AllOfKeyword($value) : null; } } json-schema/src/Parsers/Keywords/MaxLengthKeywordParser.php 0000777 00000003624 15252016106 0020124 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{MaxLengthDataKeyword, MaxLengthKeyword}; class MaxLengthKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_STRING; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new MaxLengthDataKeyword($pointer); } } if (!is_int($value) || $value < 0) { throw $this->keywordException("{keyword} must be a non-negative integer", $info); } return new MaxLengthKeyword($value); } } json-schema/src/Parsers/Keywords/NotKeywordParser.php 0000777 00000003315 15252016106 0016772 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\NotKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class NotKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_AFTER; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (is_bool($value)) { if (!$value) { return null; } } elseif (!is_object($value)) { throw $this->keywordException("{keyword} must contain a json schema (object or boolean)", $info); } return new NotKeyword($value); } } json-schema/src/Parsers/Keywords/ContentSchemaKeywordParser.php 0000777 00000003424 15252016106 0020766 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\ContentSchemaKeyword; use Opis\JsonSchema\Parsers\{DraftOptionTrait, KeywordParser, SchemaParser}; class ContentSchemaKeywordParser extends KeywordParser { use DraftOptionTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_STRING; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { if (!$this->optionAllowedForDraft('decodeContent', $info, $parser)) { return null; } $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_object($value)) { throw $this->keywordException("{keyword} must be a valid json schema object", $info); } return new ContentSchemaKeyword($value); } } json-schema/src/Parsers/Keywords/MinLengthKeywordParser.php 0000777 00000003724 15252016106 0020123 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{MinLengthDataKeyword, MinLengthKeyword}; class MinLengthKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_STRING; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new MinLengthDataKeyword($pointer); } } if (!is_int($value) || $value < 0) { throw $this->keywordException("{keyword} must be a non-negative integer", $info); } if ($value === 0) { return null; } return new MinLengthKeyword($value); } } json-schema/src/Parsers/Keywords/DependentSchemasKeywordParser.php 0000777 00000004221 15252016106 0021441 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\DependentSchemasKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class DependentSchemasKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_OBJECT; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_object($value)) { throw $this->keywordException("{keyword} must be an object", $info); } $valid = 0; $total = 0; foreach ($value as $name => $s) { $total++; if (is_bool($s)) { if ($s) { $valid++; } } elseif (!is_object($s)) { throw $this->keywordException("{keyword} must be an object containing json schemas", $info); } elseif (!count(get_object_vars($s))) { $valid++; } } if (!$total) { return null; } return $valid !== $total ? new DependentSchemasKeyword($value) : null; } } json-schema/src/Parsers/Keywords/AnyOfKeywordParser.php 0000777 00000004356 15252016106 0017254 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\AnyOfKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class AnyOfKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_AFTER; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_array($value)) { throw $this->keywordException("{keyword} should be an array of json schemas", $info); } if (!$value) { throw $this->keywordException("{keyword} must have at least one element", $info); } $alwaysValid = false; foreach ($value as $index => $item) { if ($item === true) { $alwaysValid = true; continue; } if ($item === false) { continue; } if (!is_object($item)) { throw $this->keywordException("{keyword}[{$index}] must be a json schema", $info); } elseif (!count(get_object_vars($item))) { $alwaysValid = true; } } return new AnyOfKeyword($value, $alwaysValid); } } json-schema/src/Parsers/Keywords/PropertyNamesKeywordParser.php 0000777 00000003354 15252016106 0021045 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\PropertyNamesKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class PropertyNamesKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_OBJECT; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (is_bool($value)) { if ($value) { return null; } } elseif (!is_object($value)) { throw $this->keywordException("{keyword} must be a valid json schema (object or boolean)", $info); } return new PropertyNamesKeyword($value); } } json-schema/src/Parsers/Keywords/AdditionalPropertiesKeywordParser.php 0000777 00000003245 15252016106 0022361 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\AdditionalPropertiesKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class AdditionalPropertiesKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_OBJECT; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_bool($value) && !is_object($value)) { throw $this->keywordException("{keyword} must be a json schema (object or boolean)", $info); } return new AdditionalPropertiesKeyword($value); } } json-schema/src/Parsers/Keywords/MultipleOfKeywordParser.php 0000777 00000004122 15252016106 0020307 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{MultipleOfDataKeyword, MultipleOfKeyword}; class MultipleOfKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_NUMBER; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new MultipleOfDataKeyword($pointer); } } if (!is_int($value) && !is_float($value) || is_nan($value) || !is_finite($value)) { throw $this->keywordException("{keyword} must be a valid number (integer or float)", $info); } if ($value <= 0) { throw $this->keywordException("{keyword} must be greater than zero", $info); } return new MultipleOfKeyword($value); } } json-schema/src/Parsers/Keywords/UnevaluatedPropertiesKeywordParser.php 0000777 00000003750 15252016106 0022567 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; use Opis\JsonSchema\Keywords\UnevaluatedPropertiesKeyword; class UnevaluatedPropertiesKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_AFTER_REF; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema) || !$parser->option('allowUnevaluated')) { return null; } // if (!$this->makesSense($schema)) { // return null; // } $value = $this->keywordValue($schema); if (!is_bool($value) && !is_object($value)) { throw $this->keywordException("{keyword} must be a json schema (object or boolean)", $info); } return new UnevaluatedPropertiesKeyword($value); } protected function makesSense(object $schema): bool { if (property_exists($schema, 'additionalProperties')) { return false; } return true; } } json-schema/src/Parsers/Keywords/RequiredKeywordParser.php 0000777 00000006023 15252016106 0020011 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{RequiredDataKeyword, RequiredKeyword}; class RequiredKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_OBJECT; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); $filter = $this->propertiesFilter($parser, $schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new RequiredDataKeyword($pointer, $filter); } } if (!is_array($value)) { throw $this->keywordException("{keyword} must be an array of strings", $info); } foreach ($value as $name) { if (!is_string($name)) { throw $this->keywordException("{keyword} must be an array of strings", $info); } } if ($filter) { $value = array_filter($value, $filter); } return $value ? new RequiredKeyword(array_unique($value)) : null; } /** * @param SchemaParser $parser * @param object $schema * @return callable|null */ protected function propertiesFilter(SchemaParser $parser, object $schema): ?callable { if (!$parser->option('allowDefaults')) { return null; } if (!property_exists($schema, 'properties') || !is_object($schema->properties)) { return null; } $props = $schema->properties; return static function (string $name) use ($props) { if (!property_exists($props, $name)) { return true; } if (is_object($props->{$name}) && property_exists($props->{$name}, 'default')) { return false; } return true; }; } } json-schema/src/Parsers/Keywords/PatternPropertiesKeywordParser.php 0000777 00000004157 15252016106 0021731 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\{Helper, Keyword}; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\PatternPropertiesKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class PatternPropertiesKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_OBJECT; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_object($value)) { throw $this->keywordException("{keyword} must be an object", $info); } $list = []; foreach ($value as $pattern => $item) { if (!Helper::isValidPattern($pattern)) { throw $this->keywordException("Each property name from {keyword} must be valid regex", $info); } if (!is_bool($item) && !is_object($item)) { throw $this->keywordException("{keyword}[{$pattern}] must be a json schema (object or boolean)", $info); } $list[$pattern] = $item; } return $list ? new PatternPropertiesKeyword($list) : null; } } json-schema/src/Parsers/Keywords/SlotsKeywordParser.php 0000777 00000003711 15252016106 0017336 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\SlotsKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class SlotsKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_APPEND; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$parser->option('allowSlots') || !$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_object($value)) { throw $this->keywordException('{keyword} keyword value must be an object', $info); } $slots = []; foreach ($value as $name => $fallback) { if (!is_string($name) || $name === '') { continue; } if (is_bool($fallback) || is_string($fallback) || is_object($fallback)) { $slots[$name] = $fallback; } } return $slots ? new SlotsKeyword($slots) : null; } } json-schema/src/Parsers/Keywords/AdditionalItemsKeywordParser.php 0000777 00000003707 15252016106 0021311 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\AdditionalItemsKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class AdditionalItemsKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_ARRAY; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { if (!$parser->option('keepAdditionalItemsKeyword') && $info->draft() === '2020-12') { return null; } $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } if (!property_exists($schema, 'items') || !is_array($schema->items)) { // Ignore additionalItems return null; } $value = $this->keywordValue($schema); if (!is_bool($value) && !is_object($value)) { throw $this->keywordException("{keyword} must be a json schema (object or boolean)", $info); } return new AdditionalItemsKeyword($value, count($schema->items)); } } json-schema/src/Parsers/Keywords/MinimumKeywordParser.php 0000777 00000005405 15252016106 0017647 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{ ExclusiveMinimumDataKeyword, ExclusiveMinimumKeyword, MinimumDataKeyword, MinimumKeyword }; class MinimumKeywordParser extends KeywordParser { use DataKeywordTrait; protected ?string $exclusiveKeyword; /** * @param string $keyword * @param string|null $exclusiveKeyword */ public function __construct(string $keyword, ?string $exclusiveKeyword = null) { parent::__construct($keyword); $this->exclusiveKeyword = $exclusiveKeyword; } /** * @inheritDoc */ public function type(): string { return self::TYPE_NUMBER; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); $exclusive = false; if ($parser->option('allowExclusiveMinMaxAsBool') && $this->exclusiveKeyword !== null && property_exists($schema, $this->exclusiveKeyword)) { $exclusive = $schema->{$this->exclusiveKeyword} === true; } if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return $exclusive ? new ExclusiveMinimumDataKeyword($pointer) : new MinimumDataKeyword($pointer); } } if (!is_int($value) && !is_float($value) || is_nan($value) || !is_finite($value)) { throw $this->keywordException('{keyword} must contain a valid number', $info); } return $exclusive ? new ExclusiveMinimumKeyword($value) : new MinimumKeyword($value); } } json-schema/src/Parsers/Keywords/DependentRequiredKeywordParser.php 0000777 00000004355 15252016106 0021646 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\DependentRequiredKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class DependentRequiredKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_OBJECT; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_object($value)) { throw $this->keywordException("{keyword} must be an object", $info); } $list = []; foreach ($value as $name => $s) { if (!is_array($s)) { throw $this->keywordException("{keyword} must be an object containing json schemas or arrays of property names", $info); } if (!$s) { // Empty array continue; } foreach ($s as $p) { if (!is_string($p)) { throw $this->keywordException("{keyword} must be an object containing arrays of property names", $info); } } $list[$name] = array_unique($s); } return $list ? new DependentRequiredKeyword($list) : null; } } json-schema/src/Parsers/Keywords/MinPropertiesKeywordParser.php 0000777 00000003750 15252016106 0021035 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{MinPropertiesDataKeyword, MinPropertiesKeyword}; class MinPropertiesKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_OBJECT; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new MinPropertiesDataKeyword($pointer); } } if (!is_int($value) || $value < 0) { throw $this->keywordException("{keyword} must be a non-negative integer", $info); } if ($value === 0) { return null; } return new MinPropertiesKeyword($value); } } json-schema/src/Parsers/Keywords/ItemsKeywordParser.php 0000777 00000010015 15252016106 0017306 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\ItemsKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class ItemsKeywordParser extends KeywordParser { const ONLY_SCHEMA = 1; const ONLY_ARRAY = 2; const BOTH = 3; protected int $mode; protected ?string $startIndexKeyword; public function __construct(string $keyword, int $mode = self::BOTH, ?string $startIndexKeyword = null) { parent::__construct($keyword); $this->mode = $mode; $this->startIndexKeyword = $startIndexKeyword; } /** * @inheritDoc */ public function type(): string { return self::TYPE_ARRAY; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); $alwaysValid = false; if (is_bool($value)) { if ($this->mode === self::ONLY_ARRAY) { throw $this->keywordException("{keyword} must contain an array of json schemas", $info); } if ($value) { $alwaysValid = true; } } elseif (is_array($value)) { if ($this->mode === self::ONLY_SCHEMA) { throw $this->keywordException("{keyword} must contain a valid json schema", $info); } $valid = 0; foreach ($value as $index => $v) { if (is_bool($v)) { if ($v) { $valid++; } } elseif (!is_object($v)) { throw $this->keywordException("{keyword}[$index] must contain a valid json schema", $info); } elseif (!count(get_object_vars($v))) { $valid++; } } if ($valid === count($value)) { $alwaysValid = true; } } elseif (!is_object($value)) { if ($this->mode === self::BOTH) { throw $this->keywordException("{keyword} must be a json schema or an array of json schemas", $info); } elseif ($this->mode === self::ONLY_ARRAY) { throw $this->keywordException("{keyword} must contain an array of json schemas", $info); } else { throw $this->keywordException("{keyword} must contain a valid json schema", $info); } } else { if ($this->mode === self::ONLY_ARRAY) { throw $this->keywordException("{keyword} must contain an array of json schemas", $info); } if (!count(get_object_vars($value))) { $alwaysValid = true; } } $startIndex = 0; if ($this->startIndexKeyword !== null && $this->keywordExists($schema, $this->startIndexKeyword)) { $start = $this->keywordValue($schema, $this->startIndexKeyword); if (is_array($start)) { $startIndex = count($start); } } return new ItemsKeyword($value, $alwaysValid, $this->keyword, $startIndex); } } json-schema/src/Parsers/Keywords/ExclusiveMinimumKeywordParser.php 0000777 00000004141 15252016106 0021533 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{ ExclusiveMinimumDataKeyword, ExclusiveMinimumKeyword }; class ExclusiveMinimumKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_NUMBER; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (is_bool($value) && $parser->option('allowExclusiveMinMaxAsBool')) { return null; } if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new ExclusiveMinimumDataKeyword($pointer); } } if (!is_int($value) && !is_float($value) || is_nan($value) || !is_finite($value)) { throw $this->keywordException('{keyword} must contain a valid number', $info); } return new ExclusiveMinimumKeyword($value); } } json-schema/src/Parsers/Keywords/UniqueItemsKeywordParser.php 0000777 00000003615 15252016106 0020505 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{UniqueItemsDataKeyword, UniqueItemsKeyword}; class UniqueItemsKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_ARRAY; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new UniqueItemsDataKeyword($pointer); } } if (!is_bool($value)) { throw $this->keywordException("{keyword} must be a boolean", $info); } return $value ? new UniqueItemsKeyword() : null; } } json-schema/src/Parsers/Keywords/MaxItemsKeywordParser.php 0000777 00000003612 15252016106 0017761 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Parsers\{KeywordParser, DataKeywordTrait, SchemaParser}; use Opis\JsonSchema\Keywords\{MaxItemsDataKeyword, MaxItemsKeyword}; class MaxItemsKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_ARRAY; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new MaxItemsDataKeyword($pointer); } } if (!is_int($value) || $value < 0) { throw $this->keywordException("{keyword} most be a positive integer", $info); } return new MaxItemsKeyword($value); } } json-schema/src/Parsers/Keywords/ContainsKeywordParser.php 0000777 00000005635 15252016106 0020017 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\ContainsKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class ContainsKeywordParser extends KeywordParser { protected ?string $minContains = null; protected ?string $maxContains = null; public function __construct(string $keyword, ?string $minContains = null, ?string $maxContains = null) { parent::__construct($keyword); $this->minContains = $minContains; $this->maxContains = $maxContains; } /** * @inheritDoc */ public function type(): string { return self::TYPE_ARRAY; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_bool($value) && !is_object($value)) { throw $this->keywordException("{keyword} must be a json schema (object or boolean)", $info); } $min = $max = null; if ($this->minContains && $this->keywordExists($schema, $this->minContains)) { $min = $this->keywordValue($schema, $this->minContains); if (!is_int($min) || $min < 0) { throw $this->keywordException("{keyword} must be a non-negative integer", $info, $this->minContains); } } if ($this->maxContains && $this->keywordExists($schema, $this->maxContains)) { $max = $this->keywordValue($schema, $this->maxContains); if (!is_int($max) || $max < 0) { throw $this->keywordException("{keyword} must be a non-negative integer", $info, $this->maxContains); } if ($min !== null && $max < $min) { throw $this->keywordException("{keyword} must be greater than {$this->minContains}", $info, $this->maxContains); } } elseif ($min === 0) { return null; } return new ContainsKeyword($value, $min, $max); } } json-schema/src/Parsers/Keywords/PropertiesKeywordParser.php 0000777 00000003611 15252016106 0020365 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\PropertiesKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class PropertiesKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_OBJECT; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_object($value)) { throw $this->keywordException("{keyword} must be an object", $info); } $list = []; foreach ($value as $name => $s) { if (!is_bool($s) && !is_object($s)) { throw $this->keywordException("{keyword}[{$name}] must be a json schema (object or boolean)", $info); } $list[$name] = $s; } return $list ? new PropertiesKeyword($list) : null; } } json-schema/src/Parsers/Keywords/EnumKeywordParser.php 0000777 00000006246 15252016106 0017144 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\{Keyword, Helper}; use Opis\JsonSchema\Keywords\{EnumDataKeyword, EnumKeyword}; use Opis\JsonSchema\Parsers\{ KeywordParser, DataKeywordTrait, SchemaParser }; class EnumKeywordParser extends KeywordParser { use DataKeywordTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_BEFORE; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if ($this->isDataKeywordAllowed($parser, $this->keyword)) { if ($pointer = $this->getDataKeywordPointer($value)) { return new EnumDataKeyword($pointer); } } if (!is_array($value) || !$value) { throw $this->keywordException("{keyword} must be a non-empty array", $info); } $hasConst = property_exists($schema, 'const'); $constMatched = false; $allowedTypes = isset($shared->types) ? $shared->types : null; $foundTypes = []; $list = []; foreach ($value as $item) { $type = Helper::getJsonType($item); if ($type === null) { throw $this->keywordException("{keyword} contains invalid json data type", $info); } if ($allowedTypes && !Helper::jsonTypeMatches($type, $allowedTypes)) { continue; } if ($hasConst && Helper::equals($item, $schema->const)) { $constMatched = true; break; } if (!in_array($type, $foundTypes)) { $foundTypes[] = $type; } $list[] = $item; } if ($hasConst) { if ($constMatched) { return null; } throw $this->keywordException("{keyword} does not contain the value of const keyword", $info); } if ($foundTypes) { if ($allowedTypes === null) { $shared->types = $foundTypes; } else { $shared->types = array_unique(array_merge($shared->types, $foundTypes)); } } return new EnumKeyword($list); } } json-schema/src/Parsers/Keywords/ContentMediaTypeKeywordParser.php 0000777 00000003532 15252016106 0021447 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\ContentMediaTypeKeyword; use Opis\JsonSchema\Parsers\{DraftOptionTrait, KeywordParser, SchemaParser}; class ContentMediaTypeKeywordParser extends KeywordParser { use DraftOptionTrait; /** * @inheritDoc */ public function type(): string { return self::TYPE_STRING; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { if (!$this->optionAllowedForDraft('decodeContent', $info, $parser)) { return null; } $schema = $info->data(); $resolver = $parser->getMediaTypeResolver(); if (!$resolver || !$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_string($value)) { throw $this->keywordException("{keyword} must be a string", $info); } return new ContentMediaTypeKeyword($value, $resolver); } } json-schema/src/Parsers/Keywords/IfThenElseKeywordParser.php 0000777 00000006025 15252016106 0020221 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\IfThenElseKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class IfThenElseKeywordParser extends KeywordParser { protected string $then; protected string $else; /** * @param string $if * @param string $then * @param string $else */ public function __construct(string $if, string $then, string $else) { parent::__construct($if); $this->then = $then; $this->else = $else; } /** * @inheritDoc */ public function type(): string { return self::TYPE_AFTER; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $if = $this->keywordValue($schema); if (!$this->isJsonSchema($if)) { throw $this->keywordException("{keyword} keyword must be a json schema", $info); } $then = true; if (property_exists($schema, $this->then)) { $then = $schema->{$this->then}; } if (!$this->isJsonSchema($then)) { throw $this->keywordException("{keyword} keyword must be a json schema", $info, $this->then); } $else = true; if (property_exists($schema, $this->else)) { $else = $schema->{$this->else}; } if (!$this->isJsonSchema($else)) { throw $this->keywordException("{keyword} keyword must be a json schema", $info, $this->else); } if ($if === true) { if ($then === true) { return null; } $else = true; } elseif ($if === false) { if ($else === true) { return null; } $then = true; } elseif ($then === true && $else === true) { return null; } return new IfThenElseKeyword($if, $then, $else); } /** * @param $value * @return bool */ protected function isJsonSchema($value): bool { return is_bool($value) || is_object($value); } } json-schema/src/Parsers/Keywords/DependenciesKeywordParser.php 0000777 00000005134 15252016106 0020621 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers\Keywords; use Opis\JsonSchema\Keyword; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Keywords\DependenciesKeyword; use Opis\JsonSchema\Parsers\{KeywordParser, SchemaParser}; class DependenciesKeywordParser extends KeywordParser { /** * @inheritDoc */ public function type(): string { return self::TYPE_OBJECT; } /** * @inheritDoc */ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword { if (!$parser->option('keepDependenciesKeyword') && !in_array($info->draft(), ['06', '07'])) { return null; } $schema = $info->data(); if (!$this->keywordExists($schema)) { return null; } $value = $this->keywordValue($schema); if (!is_object($value)) { throw $this->keywordException("{keyword} must be an object", $info); } $list = get_object_vars($value); foreach ($list as $name => $s) { if (is_array($s)) { if (!$s) { unset($list[$name]); continue; } foreach ($s as $p) { if (!is_string($p)) { throw $this->keywordException("{keyword} must be an object containing json schemas or arrays of property names", $info); } } $list[$name] = array_unique($s); } elseif (is_bool($s)) { if ($s) { unset($list[$name]); } } elseif (!is_object($s)) { throw $this->keywordException("{keyword} must be an object containing json schemas or arrays of property names", $info); } } return $list ? new DependenciesKeyword($list) : null; } } json-schema/src/Parsers/DraftOptionTrait.php 0000777 00000002412 15252016106 0015133 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; use Opis\JsonSchema\Info\SchemaInfo; trait DraftOptionTrait { protected function optionAllowedForDraft(string $option, SchemaInfo $info, SchemaParser $parser): bool { $value = $parser->option($option); if (!$value) { return false; } if ($value === true) { return true; } if (is_array($value)) { return in_array($info->draft(), $value); } return $value === $info->draft(); } } json-schema/src/Parsers/Draft.php 0000777 00000004467 15252016106 0012752 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; abstract class Draft extends Vocabulary { /** * @param Vocabulary|null $extraVocabulary */ public function __construct(?Vocabulary $extraVocabulary = null) { $keywords = $this->getKeywordParsers(); $keywordValidators = $this->getKeywordValidatorParsers(); $pragmas = $this->getPragmaParsers(); if ($extraVocabulary) { $keywords = array_merge($keywords, $extraVocabulary->keywords()); $keywordValidators = array_merge($keywordValidators, $extraVocabulary->keywordValidators()); $pragmas = array_merge($pragmas, $extraVocabulary->pragmas()); } array_unshift($keywords, $this->getRefKeywordParser()); parent::__construct($keywords, $keywordValidators, $pragmas); } /** * @return string */ abstract public function version(): string; /** * @return bool */ abstract public function allowKeywordsAlongsideRef(): bool; /** * @return bool */ abstract public function supportsAnchorId(): bool; /** * @return KeywordParser */ abstract protected function getRefKeywordParser(): KeywordParser; /** * @return KeywordParser[] */ abstract protected function getKeywordParsers(): array; /** * @return KeywordValidatorParser[] */ protected function getKeywordValidatorParsers(): array { return []; } /** * @return PragmaParser[] */ protected function getPragmaParsers(): array { return []; } } json-schema/src/Parsers/SchemaParser.php 0000777 00000042076 15252016106 0014265 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; use Opis\JsonSchema\{ Keyword, KeywordValidator, Schema, Uri }; use Opis\JsonSchema\Schemas\{ BooleanSchema, EmptySchema, ExceptionSchema, ObjectSchema }; use Opis\JsonSchema\Resolvers\{ FilterResolver, FormatResolver, ContentMediaTypeResolver, ContentEncodingResolver }; use Opis\JsonSchema\Parsers\Drafts\{Draft06, Draft07, Draft201909, Draft202012}; use Opis\JsonSchema\Exceptions\{ParseException, SchemaException}; use Opis\JsonSchema\Info\SchemaInfo; class SchemaParser { protected const DRAFT_REGEX = '~^https?://json-schema\.org/draft(?:/|-)(\d[0-9-]*\d)/schema#?$~i'; protected const ANCHOR_REGEX = '/^[a-z][a-z0-9\\-.:_]*/i'; protected const DEFAULT_DRAFT = '2020-12'; /** @var array */ protected const DEFAULT_OPTIONS = [ 'allowFilters' => true, 'allowFormats' => true, 'allowMappers' => true, 'allowTemplates' => true, 'allowGlobals' => true, 'allowDefaults' => true, 'allowSlots' => true, 'allowKeywordValidators' => true, 'allowPragmas' => true, 'allowDataKeyword' => true, 'allowKeywordsAlongsideRef' => false, 'allowUnevaluated' => true, 'allowRelativeJsonPointerInRef' => true, 'allowExclusiveMinMaxAsBool' => true, 'keepDependenciesKeyword' => true, 'keepAdditionalItemsKeyword' => true, 'decodeContent' => ['06', '07'], 'defaultDraft' => self::DEFAULT_DRAFT, 'varRefKey' => '$ref', 'varEachKey' => '$each', 'varDefaultKey' => 'default', ]; /** @var array */ protected array $options; /** @var Draft[] */ protected array $drafts; /** @var array */ protected array $resolvers; /** * @param array $resolvers * @param array $options * @param Vocabulary|null $extraVocabulary */ public function __construct( array $resolvers = [], array $options = [], ?Vocabulary $extraVocabulary = null ) { if ($options) { $this->options = $options + self::DEFAULT_OPTIONS; } else { $this->options = self::DEFAULT_OPTIONS; } $this->resolvers = $this->getResolvers($resolvers); $this->drafts = $this->getDrafts($extraVocabulary ?? new DefaultVocabulary()); } /** * @param Vocabulary|null $extraVocabulary * @return array */ protected function getDrafts(?Vocabulary $extraVocabulary): array { return [ '06' => new Draft06($extraVocabulary), '07' => new Draft07($extraVocabulary), '2019-09' => new Draft201909($extraVocabulary), '2020-12' => new Draft202012($extraVocabulary), ]; } /** * @param array $resolvers * @return array */ protected function getResolvers(array $resolvers): array { if (!array_key_exists('format', $resolvers)) { $resolvers['format'] = new FormatResolver(); } if (!array_key_exists('contentEncoding', $resolvers)) { $resolvers['contentEncoding'] = new ContentEncodingResolver(); } if (!array_key_exists('contentMediaType', $resolvers)) { $resolvers['contentMediaType'] = new ContentMediaTypeResolver(); } if (!array_key_exists('$filters', $resolvers)) { $resolvers['$filters'] = new FilterResolver(); } return $resolvers; } /** * @param string $name * @param null $default * @return mixed|null */ public function option(string $name, $default = null) { return $this->options[$name] ?? $default; } /** * @param string $name * @param $value * @return $this */ public function setOption(string $name, $value): self { $this->options[$name] = $value; return $this; } /** * @return array */ public function getOptions(): array { return $this->options; } /** * @param string $name * @param $resolver * @return $this */ public function setResolver(string $name, $resolver): self { $this->resolvers[$name] = $resolver; return $this; } /** * @return null|FilterResolver */ public function getFilterResolver(): ?FilterResolver { return $this->getResolver('$filters'); } /** * @param null|FilterResolver $resolver * @return $this */ public function setFilterResolver(?FilterResolver $resolver): self { return $this->setResolver('$filters', $resolver); } /** * @return null|FormatResolver */ public function getFormatResolver(): ?FormatResolver { return $this->getResolver('format'); } /** * @param FormatResolver|null $resolver * @return $this */ public function setFormatResolver(?FormatResolver $resolver): self { return $this->setResolver('format', $resolver); } /** * @return null|ContentEncodingResolver */ public function getContentEncodingResolver(): ?ContentEncodingResolver { return $this->getResolver('contentEncoding'); } /** * @param ContentEncodingResolver|null $resolver * @return $this */ public function setContentEncodingResolver(?ContentEncodingResolver $resolver): self { return $this->setResolver('contentEncoding', $resolver); } /** * @return null|ContentMediaTypeResolver */ public function getMediaTypeResolver(): ?ContentMediaTypeResolver { return $this->getResolver('contentMediaType'); } /** * @param ContentMediaTypeResolver|null $resolver * @return $this */ public function setMediaTypeResolver(?ContentMediaTypeResolver $resolver): self { return $this->setResolver('contentMediaType', $resolver); } /** * @return string */ public function defaultDraftVersion(): string { return $this->option('defaultDraft', self::DEFAULT_DRAFT); } /** * @param string $draft * @return $this */ public function setDefaultDraftVersion(string $draft): self { return $this->setOption('defaultDraft', $draft); } /** * @param string $schema * @return string|null */ public function parseDraftVersion(string $schema): ?string { if (!preg_match(self::DRAFT_REGEX, $schema, $m)) { return null; } return $m[1] ?? null; } /** * @param object $schema * @return string|null */ public function parseId(object $schema): ?string { if (property_exists($schema, '$id') && is_string($schema->{'$id'})) { return $schema->{'$id'}; } return null; } /** * @param object $schema * @param string $draft * @return string|null */ public function parseAnchor(object $schema, string $draft): ?string { if (!property_exists($schema, '$anchor') || !isset($this->drafts[$draft]) || !$this->drafts[$draft]->supportsAnchorId()) { return null; } $anchor = $schema->{'$anchor'}; if (!is_string($anchor) || !preg_match(self::ANCHOR_REGEX, $anchor)) { return null; } return $anchor; } /** * @param object $schema * @return string|null */ public function parseSchemaDraft(object $schema): ?string { if (!property_exists($schema, '$schema') || !is_string($schema->{'$schema'})) { return null; } return $this->parseDraftVersion($schema->{'$schema'}); } /** * @param object $schema * @param Uri $id * @param callable $handle_id * @param callable $handle_object * @param string|null $draft * @return Schema|null */ public function parseRootSchema( object $schema, Uri $id, callable $handle_id, callable $handle_object, ?string $draft = null ): ?Schema { $existent = false; if (property_exists($schema, '$id')) { $existent = true; $id = Uri::parse($schema->{'$id'}, true); } if ($id instanceof Uri) { if ($id->fragment() === null) { $id = Uri::merge($id, null, true); } } else { throw new ParseException('Root schema id must be an URI', new SchemaInfo($schema, $id)); } if (!$id->isAbsolute()) { throw new ParseException('Root schema id must be an absolute URI', new SchemaInfo($schema, $id)); } if ($id->fragment() !== '') { throw new ParseException('Root schema id must have an empty fragment or none', new SchemaInfo($schema, $id)); } // Check if id exists if ($resolved = $handle_id($id)) { return $resolved; } if (property_exists($schema, '$schema')) { if (!is_string($schema->{'$schema'})) { throw new ParseException('Schema draft must be a string', new SchemaInfo($schema, $id)); } $draft = $this->parseDraftVersion($schema->{'$schema'}); } if ($draft === null) { $draft = $this->defaultDraftVersion(); } if (!$existent) { $schema->{'$id'} = (string)$id; } $resolved = $handle_object($schema, $id, $draft); if (!$existent) { unset($schema->{'$id'}); } return $resolved; } /** * @param SchemaInfo $info * @return Schema */ public function parseSchema(SchemaInfo $info): Schema { if ($info->isBoolean()) { return new BooleanSchema($info); } try { return $this->parseSchemaObject($info); } catch (SchemaException $exception) { return new ExceptionSchema($info, $exception); } } /** * @param string $version * @return Draft|null */ public function draft(string $version): ?Draft { return $this->drafts[$version] ?? null; } /** * @param Draft $draft * @return $this */ public function addDraft(Draft $draft): self { $this->drafts[$draft->version()] = $draft; return $this; } /** * @return string[] */ public function supportedDrafts(): array { return array_keys($this->drafts); } /** * @param array $options * @return $this */ protected function setOptions(array $options): self { $this->options = $options + $this->options; return $this; } /** * @param string $name * @return mixed|null */ protected function getResolver(string $name) { $resolver = $this->resolvers[$name] ?? null; if (!is_object($resolver)) { return null; } return $resolver; } /** * @param SchemaInfo $info * @return Schema */ protected function parseSchemaObject(SchemaInfo $info): Schema { $draftObject = $this->draft($info->draft()); if ($draftObject === null) { throw new ParseException("Unsupported draft-{$info->draft()}", $info); } /** @var object $schema */ $schema = $info->data(); // Check id if (property_exists($schema, '$id')) { $id = $info->id(); if ($id === null || !$id->isAbsolute()) { throw new ParseException('Schema id must be a valid URI', $info); } } if ($hasRef = property_exists($schema, '$ref')) { if ($this->option('allowKeywordsAlongsideRef') || $draftObject->allowKeywordsAlongsideRef()) { $hasRef = false; } } $shared = (object) []; if ($this->option('allowKeywordValidators')) { $keywordValidator = $this->parseKeywordValidators($info, $draftObject->keywordValidators(), $shared); } else { $keywordValidator = null; } return $this->parseSchemaKeywords($info, $keywordValidator, $draftObject->keywords(), $shared, $hasRef); } /** * @param SchemaInfo $info * @param KeywordValidatorParser[] $keywordValidators * @param object $shared * @return KeywordValidator|null */ protected function parseKeywordValidators(SchemaInfo $info, array $keywordValidators, object $shared): ?KeywordValidator { $last = null; while ($keywordValidators) { /** @var KeywordValidatorParser $keywordValidator */ $keywordValidator = array_pop($keywordValidators); if ($keywordValidator && ($keyword = $keywordValidator->parse($info, $this, $shared))) { $keyword->setNext($last); $last = $keyword; unset($keyword); } unset($keywordValidator); } return $last; } /** * @param SchemaInfo $info * @param KeywordValidator|null $keywordValidator * @param KeywordParser[] $parsers * @param object $shared * @param bool $hasRef * @return Schema */ protected function parseSchemaKeywords(SchemaInfo $info, ?KeywordValidator $keywordValidator, array $parsers, object $shared, bool $hasRef = false): Schema { /** @var Keyword[] $prepend */ $prepend = []; /** @var Keyword[] $append */ $append = []; /** @var Keyword[] $before */ $before = []; /** @var Keyword[] $after */ $after = []; /** @var Keyword[][] $types */ $types = []; /** @var Keyword[] $ref */ $ref = []; if ($hasRef) { foreach ($parsers as $parser) { $kType = $parser->type(); if ($kType === KeywordParser::TYPE_APPEND) { $container = &$append; } elseif ($kType === KeywordParser::TYPE_AFTER_REF) { $container = &$ref; } elseif ($kType === KeywordParser::TYPE_PREPEND) { $container = &$prepend; } else { continue; } if ($keyword = $parser->parse($info, $this, $shared)) { $container[] = $keyword; } unset($container, $keyword, $kType); } } else { foreach ($parsers as $parser) { $keyword = $parser->parse($info, $this, $shared); if ($keyword === null) { continue; } $kType = $parser->type(); switch ($kType) { case KeywordParser::TYPE_PREPEND: $prepend[] = $keyword; break; case KeywordParser::TYPE_APPEND: $append[] = $keyword; break; case KeywordParser::TYPE_BEFORE: $before[] = $keyword; break; case KeywordParser::TYPE_AFTER: $after[] = $keyword; break; case KeywordParser::TYPE_AFTER_REF: $ref[] = $keyword; break; default: if (!isset($types[$kType])) { $types[$kType] = []; } $types[$kType][] = $keyword; break; } } } unset($shared); if ($prepend) { $before = array_merge($prepend, $before); } unset($prepend); if ($ref) { $after = array_merge($after, $ref); } unset($ref); if ($append) { $after = array_merge($after, $append); } unset($append); if (empty($before)) { $before = null; } if (empty($after)) { $after = null; } if (empty($types)) { $types = null; } if (empty($types) && empty($before) && empty($after)) { return new EmptySchema($info, $keywordValidator); } return new ObjectSchema($info, $keywordValidator, $types, $before, $after); } } json-schema/src/Parsers/KeywordParserTrait.php 0000777 00000004353 15252016106 0015511 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Parsers; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Exceptions\InvalidKeywordException; trait KeywordParserTrait { /** @var string */ protected string $keyword; /** * @param string $keyword */ public function __construct(string $keyword) { $this->keyword = $keyword; } /** * @param object|SchemaInfo $schema * @param string|null $keyword * @return bool */ protected function keywordExists(object $schema, ?string $keyword = null): bool { if ($schema instanceof SchemaInfo) { $schema = $schema->data(); } return property_exists($schema, $keyword ?? $this->keyword); } /** * @param object|SchemaInfo $schema * @param string|null $keyword * @return mixed */ protected function keywordValue(object $schema, ?string $keyword = null) { if ($schema instanceof SchemaInfo) { $schema = $schema->data(); } return $schema->{$keyword ?? $this->keyword}; } /** * @param string $message * @param SchemaInfo $info * @param string|null $keyword * @return InvalidKeywordException */ protected function keywordException(string $message, SchemaInfo $info, ?string $keyword = null): InvalidKeywordException { $keyword = $keyword ?? $this->keyword; return new InvalidKeywordException(str_replace('{keyword}', $keyword, $message), $keyword, $info); } } json-schema/src/Variables/VariablesContainer.php 0000777 00000010373 15252016106 0015747 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Variables; use Opis\JsonSchema\JsonPointer; use Opis\JsonSchema\Variables; final class VariablesContainer implements Variables { /** * @var array|object|Variables|null */ private $vars; private bool $parsed = false; private bool $hasRefs = false; /** @var string[] */ private array $keys; /** * @param array|object $data * @param bool $lazy * @param string $ref_key * @param string $each_key * @param string $default_key */ public function __construct($data, bool $lazy = true, string $ref_key = '$ref', string $each_key = '$each', string $default_key = 'default') { $this->keys = [ 'ref' => $ref_key, 'each' => $each_key, 'default' => $default_key, ]; if ($lazy) { $this->vars = $data; } else { $this->parsed = true; $this->vars = $this->parse($data); } } /** * @inheritdoc */ public function resolve($data, array $path = []) { if (!$this->parsed) { $this->vars = $this->parse($this->vars); $this->parsed = true; } if (!$this->hasRefs) { // Nothing to resolve return $this->vars; } return $this->deepClone($this->vars, $data, $path); } /** * @param $vars * @param $data * @param string[]|int[] $path * @return array|object|mixed */ private function deepClone($vars, $data, array $path) { $toObject = false; if (is_object($vars)) { if ($vars instanceof Variables) { return $vars->resolve($data, $path); } $vars = get_object_vars($vars); $toObject = true; } elseif (!is_array($vars)) { return $vars; } foreach ($vars as &$var) { if ($var !== null && !is_scalar($var)) { $var = $this->deepClone($var, $data, $path); } unset($var); } return $toObject ? (object)$vars : $vars; } /** * @param mixed $data * @return mixed */ private function parse($data) { if (is_array($data)) { return array_map([$this, 'parse'], $data); } if (!is_object($data)) { return $data; } if ($vars = $this->parseRef($data)) { $this->hasRefs = true; return $vars; } return (object)array_map([$this, 'parse'], get_object_vars($data)); } /** * @param object $data * @return null|Variables */ private function parseRef(object $data): ?Variables { if (!property_exists($data, $this->keys['ref'])) { return null; } $ref = $data->{$this->keys['ref']}; if (!is_string($ref)) { return null; } $pointer = JsonPointer::parse($ref); if ($pointer === null) { return null; } $each = null; if (property_exists($data, $this->keys['each']) && is_object($data->{$this->keys['each']})) { $each = new self($data->{$this->keys['each']}, !$this->parsed, $this->keys['ref'], $this->keys['each'], $this->keys['default']); } if (property_exists($data, $this->keys['default'])) { return new RefVariablesContainer($pointer, $each, $data->{$this->keys['default']}); } return new RefVariablesContainer($pointer, $each); } } json-schema/src/Variables/RefVariablesContainer.php 0000777 00000005052 15252016106 0016402 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Variables; use Opis\JsonSchema\JsonPointer; use Opis\JsonSchema\Variables; final class RefVariablesContainer implements Variables { private JsonPointer $pointer; private ?Variables $each; private bool $hasDefault; /** @var mixed */ private $defaultValue; /** * @param JsonPointer $pointer * @param Variables|null $each * @param mixed $default */ public function __construct(JsonPointer $pointer, ?Variables $each = null, $default = null) { $this->pointer = $pointer; $this->each = $each; $this->hasDefault = func_num_args() === 3; $this->defaultValue = $default; } /** * @return JsonPointer */ public function pointer(): JsonPointer { return $this->pointer; } /** * @return null|Variables */ public function each(): ?Variables { return $this->each; } /** * @return bool */ public function hasDefaultValue(): bool { return $this->hasDefault; } /** * @return mixed|null */ public function defaultValue() { return $this->defaultValue; } /** * @inheritDoc */ public function resolve($data, array $path = []) { $resolved = $this->pointer->data($data, $path, $this); if ($resolved === $this) { return $this->defaultValue; } if ($this->each && (is_array($resolved) || is_object($resolved))) { $path = $this->pointer->absolutePath($path); foreach ($resolved as $key => &$value) { $path[] = $key; $value = $this->each->resolve($data, $path); array_pop($path); unset($value); } } return $resolved; } } json-schema/src/Pragmas/SlotsPragma.php 0000777 00000002625 15252016106 0014113 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Pragmas; use Opis\JsonSchema\{ValidationContext, Pragma}; class SlotsPragma implements Pragma { protected array $slots; /** * @param array $slots */ public function __construct(array $slots) { $this->slots = $slots; } /** * @inheritDoc */ public function enter(ValidationContext $context) { $data = $context->slots(); $context->setSlots($data ? $this->slots + $data : $this->slots); return $data; } /** * @inheritDoc */ public function leave(ValidationContext $context, $data): void { $context->setSlots($data); } } json-schema/src/Pragmas/MaxErrorsPragma.php 0000777 00000002724 15252016106 0014731 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Pragmas; use Opis\JsonSchema\{ValidationContext, Pragma}; class MaxErrorsPragma implements Pragma { protected int $maxErrors; /** * @param int $maxErrors */ public function __construct(int $maxErrors) { $this->maxErrors = $maxErrors; } /** * @inheritDoc */ public function enter(ValidationContext $context) { $data = $context->maxErrors(); $context->setMaxErrors($this->maxErrors); return $data; } /** * @inheritDoc */ public function leave(ValidationContext $context, $data): void { if ($data === null) { return; } $context->setMaxErrors($data); } } json-schema/src/Pragmas/GlobalsPragma.php 0000777 00000003237 15252016106 0014372 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Pragmas; use Opis\JsonSchema\{ValidationContext, Pragma}; use Opis\JsonSchema\Variables; class GlobalsPragma implements Pragma { protected Variables $globals; /** * @param Variables $globals */ public function __construct(Variables $globals) { $this->globals = $globals; } /** * @inheritDoc */ public function enter(ValidationContext $context) { $resolved = (array) $this->globals->resolve($context->rootData(), $context->currentDataPath()); if (!$resolved) { return null; } $data = $context->globals(); $context->setGlobals($resolved, false); return $data; } /** * @inheritDoc */ public function leave(ValidationContext $context, $data): void { if ($data === null) { return; } $context->setGlobals($data, true); } } json-schema/src/Pragmas/CastPragma.php 0000777 00000010534 15252016106 0013677 0 ustar 00 <?php /* =========================================================================== * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Pragmas; use Opis\JsonSchema\{Helper, ValidationContext, Pragma}; class CastPragma implements Pragma { protected string $cast; /** @var callable */ protected $func; /** * @param string $cast */ public function __construct(string $cast) { $this->cast = $cast; $this->func = $this->getCastFunction($cast); } /** * @inheritDoc */ public function enter(ValidationContext $context) { $currentType = $context->currentDataType(); if ($currentType !== null && Helper::jsonTypeMatches($currentType, $this->cast)) { // Cast not needed return $this; } unset($currentType); $currentData = $context->currentData(); $context->setCurrentData(($this->func)($currentData)); return $currentData; } /** * @inheritDoc */ public function leave(ValidationContext $context, $data): void { if ($data !== $this) { $context->setCurrentData($data); } } /** * @param string $type * @return callable */ protected function getCastFunction(string $type): callable { $f = 'toNull'; switch ($type) { case 'integer': $f = 'toInteger'; break; case 'number': $f = 'toNumber'; break; case 'string': $f = 'toString'; break; case 'array': $f = 'toArray'; break; case 'object': $f = 'toObject'; break; case 'boolean': $f = 'toBoolean'; break; } return [$this, $f]; } /** * @param $value * @return int|null */ public function toInteger($value): ?int { if ($value === null) { return 0; } return is_scalar($value) ? intval($value) : null; } /** * @param $value * @return float|null */ public function toNumber($value): ?float { if ($value === null) { return 0.0; } return is_scalar($value) ? floatval($value) : null; } /** * @param $value * @return string|null */ public function toString($value): ?string { if ($value === null) { return ''; } if (is_scalar($value)) { return (string) $value; } return null; } /** * @param $value * @return array|null */ public function toArray($value): ?array { if ($value === null) { return []; } if (is_scalar($value)) { return [$value]; } if (is_array($value)) { return array_values($value); } if (is_object($value)) { return array_values(get_object_vars($value)); } return null; } /** * @param $value * @return object|null */ public function toObject($value): ?object { if (is_object($value) || is_array($value)) { return (object) $value; } return null; } /** * @param $value * @return bool */ public function toBoolean($value): bool { if ($value === null) { return false; } if (is_string($value)) { return !($value === ''); } if (is_object($value)) { return count(get_object_vars($value)) > 0; } return boolval($value); } } json-schema/src/ValidationContext.php 0000777 00000043656 15252016106 0013735 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; use ArrayObject; use Opis\String\UnicodeString; use Opis\JsonSchema\Errors\ValidationError; class ValidationContext { /** @var mixed */ protected $rootData; /** @var string[]|int[] */ protected array $currentDataPath = []; protected ?array $fullPath = null; protected array $globals; /** @var mixed */ protected $currentData = null; protected ?self $parent = null; protected SchemaLoader $loader; protected ?Schema $sender = null; /** @var object[]|null[]|null */ protected ?array $shared = null; /** @var null|string[]|Schema[]|object[] */ protected ?array $slots = null; protected int $sharedIndex = -1; protected int $pathIndex = 0; protected int $maxErrors = 1; protected bool $stopAtFirstError = true; /** * @param $data * @param SchemaLoader $loader * @param null|self $parent * @param Schema|null $parent * @param array $globals * @param null|string[]|Schema[] $slots * @param int $max_errors */ public function __construct( $data, SchemaLoader $loader, ?self $parent = null, ?Schema $sender = null, array $globals = [], ?array $slots = null, int $max_errors = 1, bool $stop_at_first_error = true ) { $this->sender = $sender; $this->rootData = $data; $this->loader = $loader; $this->parent = $parent; $this->globals = $globals; $this->slots = null; $this->maxErrors = $max_errors; $this->stopAtFirstError = $stop_at_first_error; $this->currentData = [ [$data, false], ]; if ($slots) { $this->setSlots($slots); } } /** * @param $data * @param Schema|null $sender * @param array|null $globals * @param array|null $slots * @param int|null $max_errors * @return self */ public function newInstance( $data, ?Schema $sender, ?array $globals = null, ?array $slots = null, ?int $max_errors = null, ?bool $stop_at_first_error = null ): self { return new self( $data, $this->loader, $this, $sender, $globals ?? $this->globals, $slots ?? $this->slots, $max_errors ?? $this->maxErrors, $stop_at_first_error ?? $this->stopAtFirstError ); } public function create( Schema $sender, ?Variables $mapper = null, ?Variables $globals = null, ?array $slots = null, ?int $maxErrors = null, ?bool $stop_at_first_error = null ): self { if ($globals) { $globals = $globals->resolve($this->rootData(), $this->currentDataPath()); if (!is_array($globals)) { $globals = (array)$globals; } $globals += $this->globals; } else { $globals = $this->globals; } if ($mapper) { $data = $mapper->resolve($this->rootData(), $this->currentDataPath()); } else { $data = $this->currentData(); } return new self($data, $this->loader, $this, $sender, $globals, $slots ?? $this->slots, $maxErrors ?? $this->maxErrors, $stop_at_first_error ?? $this->stopAtFirstError); } public function sender(): ?Schema { return $this->sender; } /** * @return self|null */ public function parent(): ?self { return $this->parent; } /** * @return SchemaLoader */ public function loader(): SchemaLoader { return $this->loader; } /** * @return mixed */ public function rootData() { return $this->rootData; } /** * @return mixed */ public function currentData() { return $this->currentData[$this->pathIndex][0]; } /** * @param $value */ public function setCurrentData($value): void { $this->currentData[$this->pathIndex][0] = $value; $this->currentData[$this->pathIndex][1] = false; } /** * @return string|null */ public function currentDataType(): ?string { $type = $this->currentData[$this->pathIndex][1]; if ($type === false) { $type = Helper::getJsonType($this->currentData[$this->pathIndex][0]); $this->currentData[$this->pathIndex][1] = $type; } return $type; } public function fullDataPath(): array { if ($this->fullPath === null) { if ($this->parent === null) { return $this->currentDataPath; } $this->fullPath = array_merge($this->parent->fullDataPath(), $this->currentDataPath); } return $this->fullPath; } /** * @return int[]|string[] */ public function currentDataPath(): array { return $this->currentDataPath; } /** * @param string|int $key * @return $this */ public function pushDataPath($key): self { $this->currentDataPath[] = $key; if ($this->fullPath !== null) { $this->fullPath[] = $key; } $data = $this->currentData[$this->pathIndex][0]; if (is_array($data)) { $data = $data[$key] ?? null; } elseif (is_object($data)) { $data = $data->{$key} ?? null; } else { $data = null; } $this->currentData[] = [$data, false]; $this->pathIndex++; return $this; } /** * @return $this */ public function popDataPath(): self { if ($this->pathIndex < 1) { return $this; } if ($this->fullPath !== null) { array_pop($this->fullPath); } array_pop($this->currentDataPath); array_pop($this->currentData); $this->pathIndex--; return $this; } /** * @return array */ public function globals(): array { return $this->globals; } /** * @param array $globals * @param bool $overwrite * @return $this */ public function setGlobals(array $globals, bool $overwrite = false): self { if ($overwrite) { $this->globals = $globals; } elseif ($globals) { $this->globals = $globals + $this->globals; } return $this; } /** * @return object[]|Schema[]|string[]|null */ public function slots(): ?array { return $this->slots; } /** * @param array|null $slots * @return $this */ public function setSlots(?array $slots): self { if ($slots) { $list = []; foreach ($slots as $name => $value) { if (is_bool($value)) { $value = $this->loader->loadBooleanSchema($value); } elseif (is_object($value)) { if ($value instanceof Schema) { $list[$name] = $value; continue; } $value = $this->loader->loadObjectSchema($value); } elseif (is_string($value)) { if (isset($this->slots[$value])) { $value = $this->slots[$value]; } elseif ($this->parent) { $value = $this->parent->slot($value); } } if ($value instanceof Schema) { $list[$name] = $value; } } $this->slots = $list; } else { $this->slots = null; } return $this; } /** * @param string $name * @return Schema|null */ public function slot(string $name): ?Schema { return $this->slots[$name] ?? null; } /** * @return int */ public function maxErrors(): int { return $this->maxErrors; } /** * @param int $max * @return $this */ public function setMaxErrors(int $max): self { $this->maxErrors = $max; return $this; } public function stopAtFirstError(): bool { return $this->stopAtFirstError; } public function setStopAtFirstError(bool $stop): self { $this->stopAtFirstError = $stop; return $this; } /* --------------------- */ /** * @param Schema $schema * @return $this */ public function pushSharedObject(Schema $schema): self { $unevaluated = !in_array($schema->info()->draft(), ['06', '07']); if ($unevaluated && ($parser = $this->loader->parser()) && !$parser->option('allowUnevaluated', true)) { $unevaluated = false; } $this->shared[] = [ 'schema' => $schema, 'unevaluated' => $unevaluated, 'object' => null, ]; $this->sharedIndex++; return $this; } /** * @return $this */ public function popSharedObject(): self { if ($this->sharedIndex < 0) { return $this; } $data = array_pop($this->shared); $this->sharedIndex--; if ($data['unevaluated'] && $data['object']) { if ($this->sharedIndex >= 0) { $this->mergeUnevaluated($data['object']); } elseif ($this->parent && $this->parent->sharedIndex >= 0) { $this->parent->mergeUnevaluated($data['object']); } } return $this; } /** * @return object|null */ public function sharedObject(): ?object { if ($this->sharedIndex < 0) { return null; } return $this->shared[$this->sharedIndex]['object'] ??= (object)[]; } public function schema(): ?Schema { return $this->shared[$this->sharedIndex]['schema'] ?? null; } public function trackUnevaluated(): bool { return $this->shared[$this->sharedIndex]['unevaluated'] ?? false; } protected function mergeUnevaluated(object $obj): void { switch ($this->currentDataType()) { case 'object': if (isset($obj->evaluatedProperties)) { $this->addEvaluatedProperties($obj->evaluatedProperties); } break; case 'array': if (isset($obj->evaluatedItems)) { $this->addEvaluatedItems($obj->evaluatedItems); } break; } } /* ----------------*/ public function getStringLength(): ?int { if ($this->currentDataType() !== 'string') { return null; } $shared = $this->sharedObject(); if (!isset($shared->stringLength)) { $shared->stringLength = UnicodeString::getStringLength($this->currentData()); } return $shared->stringLength; } public function setDecodedContent(string $content): bool { if ($this->currentDataType() !== 'string') { return false; } $this->sharedObject()->decodedContent = $content; return true; } public function getDecodedContent(): ?string { if ($this->currentDataType() !== 'string') { return null; } return $this->sharedObject()->decodedContent ?? $this->currentData(); } public function getObjectProperties(): ?array { if ($this->currentDataType() !== 'object') { return null; } return $this->sharedObject()->objectProperties ??= array_keys(get_object_vars($this->currentData())); } public function addCheckedProperties(?array $properties): bool { if (!$properties) { return false; } $shared = $this->sharedObject(); if (!isset($shared->checkedProperties)) { $shared->checkedProperties = $properties; } else { $shared->checkedProperties = array_values(array_unique(array_merge($shared->checkedProperties, $properties))); } return true; } public function getCheckedProperties(): ?array { return $this->sharedObject()->checkedProperties ?? null; } public function getUncheckedProperties(): ?array { $properties = $this->getObjectProperties(); if (!$properties) { return $properties; } $checked = $this->sharedObject()->checkedProperties ?? null; if (!$checked) { return $properties; } return array_values(array_diff($properties, $checked)); } public function markAllAsEvaluatedProperties(): bool { return $this->addEvaluatedProperties($this->getObjectProperties()); } public function addEvaluatedProperties(?array $properties): bool { if (!$properties || !($this->currentDataType() === 'object') || !$this->trackUnevaluated()) { return false; } $shared = $this->sharedObject(); if (!isset($shared->evaluatedProperties)) { $shared->evaluatedProperties = $properties; } else { $shared->evaluatedProperties = array_values(array_unique(array_merge($shared->evaluatedProperties, $properties))); } return true; } public function getEvaluatedProperties(): ?array { return $this->sharedObject()->evaluatedProperties ?? null; } public function getUnevaluatedProperties(): ?array { $properties = $this->getObjectProperties(); if (!$properties) { return $properties; } $evaluated = $this->sharedObject()->evaluatedProperties ?? null; if (!$evaluated) { return $properties; } return array_values(array_diff($properties, $evaluated)); } public function markAllAsEvaluatedItems(): bool { return $this->addEvaluatedItems(range(0, count($this->currentData()))); } public function markCountAsEvaluatedItems(int $count): bool { if (!$count) { return false; } return $this->addEvaluatedItems(range(0, $count)); } public function addEvaluatedItems(?array $items): bool { if (!$items || !($this->currentDataType() === 'array') || !$this->trackUnevaluated()) { return false; } $shared = $this->sharedObject(); if (!isset($shared->evaluatedItems)) { $shared->evaluatedItems = $items; } else { $shared->evaluatedItems = array_values(array_unique(array_merge($shared->evaluatedItems, $items), SORT_NUMERIC)); } return true; } public function getEvaluatedItems(): ?array { return $this->sharedObject()->evaluatedItems ?? null; } public function getUnevaluatedItems(): ?array { if ($this->currentDataType() !== 'array') { return null; } $items = array_keys($this->currentData()); if (!$items) { return $items; } $evaluated = $this->sharedObject()->evaluatedItems ?? null; if (!$evaluated) { return $items; } return array_values(array_diff($items, $evaluated)); } public function validateSchemaWithoutEvaluated( Schema $schema, ?int $maxErrors = null, bool $reset_on_error_only = false, ?ArrayObject $array = null ): ?ValidationError { $currentMaxErrors = $this->maxErrors; $this->maxErrors = $maxErrors ?? $currentMaxErrors; if ($this->trackUnevaluated()) { $shared = $this->sharedObject(); $props = $shared->evaluatedProperties ?? null; $items = $shared->evaluatedItems ?? null; $error = $schema->validate($this); if ($array) { $value = null; if ($shared->evaluatedProperties ?? null) { if ($props) { if ($diff = array_diff($shared->evaluatedProperties, $props)) { $value['properties'] = $diff; } } else { $value['properties'] = $shared->evaluatedProperties; } } if ($shared->evaluatedItems ?? null) { if ($items) { if ($diff = array_diff($shared->evaluatedItems, $items)) { $value['items'] = $diff; } } else { $value['items'] = $shared->evaluatedItems; } } if ($value) { $array[] = $value; } } if ($reset_on_error_only) { if ($error) { $shared->evaluatedProperties = $props; $shared->evaluatedItems = $items; } } else { $shared->evaluatedProperties = $props; $shared->evaluatedItems = $items; } } else { $error = $schema->validate($this); } $this->maxErrors = $currentMaxErrors; return $error; } } json-schema/src/Info/SchemaInfo.php 0000777 00000005163 15252016106 0013174 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Info; use Opis\JsonSchema\Uri; class SchemaInfo { /** @var bool|object */ protected $data; protected ?Uri $id; protected ?Uri $root; protected ?Uri $base; /** @var string[]|int[] */ protected array $path; protected ?string $draft; /** * @param object|bool $data * @param Uri|null $id * @param Uri|null $base * @param Uri|null $root * @param string[]|int[] $path * @param string|null $draft */ public function __construct($data, ?Uri $id, ?Uri $base = null, ?Uri $root = null, array $path = [], ?string $draft = null) { if ($root === $id || ((string)$root === (string)$id)) { $root = null; } if ($root === null) { $base = null; } $this->data = $data; $this->id = $id; $this->root = $root; $this->base = $base; $this->path = $path; $this->draft = $draft; } public function id(): ?Uri { return $this->id; } public function root(): ?Uri { return $this->root; } public function base(): ?Uri { return $this->base; } public function draft(): ?string { return $this->draft; } public function data() { return $this->data; } public function path(): array { return $this->path; } /** * Returns first non-null property: id, base or root * @return Uri|null */ public function idBaseRoot(): ?Uri { return $this->id ?? $this->base ?? $this->root; } public function isBoolean(): bool { return is_bool($this->data); } public function isObject(): bool { return is_object($this->data); } public function isDocumentRoot(): bool { return $this->id && !$this->root && !$this->base; } } json-schema/src/Info/DataInfo.php 0000777 00000005345 15252016106 0012647 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Info; use Opis\JsonSchema\ValidationContext; class DataInfo { /** @var mixed */ protected $value; protected ?string $type; /** @var mixed */ protected $root; /** @var string[]|int[] */ protected array $path; protected ?DataInfo $parent = null; /** @var string[]|int[]|null */ protected ?array $fullPath = null; /** * DataInfo constructor. * @param $value * @param string|null $type * @param $root * @param string[]|int[] $path * @param DataInfo|null $parent */ public function __construct($value, ?string $type, $root, array $path = [], ?DataInfo $parent = null) { $this->value = $value; $this->type = $type; $this->root = $root; $this->path = $path; $this->parent = $parent; } public function value() { return $this->value; } public function type(): ?string { return $this->type; } public function root() { return $this->root; } /** * @return int[]|string[] */ public function path(): array { return $this->path; } public function parent(): ?DataInfo { return $this->parent; } /** * @return int[]|string[] */ public function fullPath(): array { if ($this->parent === null) { return $this->path; } if ($this->fullPath === null) { $this->fullPath = array_merge($this->parent->fullPath(), $this->path); } return $this->fullPath; } /** * @param ValidationContext $context * @return static */ public static function fromContext(ValidationContext $context): self { if ($parent = $context->parent()) { $parent = self::fromContext($parent); } return new self($context->currentData(), $context->currentDataType(), $context->rootData(), $context->currentDataPath(), $parent); } } json-schema/src/Schemas/ObjectSchema.php 0000777 00000010774 15252016106 0014203 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Schemas; use Opis\JsonSchema\{Helper, Keyword, ValidationContext, KeywordValidator}; use Opis\JsonSchema\Info\{DataInfo, SchemaInfo}; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\KeywordValidators\CallbackKeywordValidator; class ObjectSchema extends AbstractSchema { protected ?KeywordValidator $keywordValidator = null; /** @var Keyword[]|null */ protected ?array $before = null; /** @var Keyword[]|null */ protected ?array $after = null; /** @var Keyword[][]|null */ protected ?array $types = null; /** * @param SchemaInfo $info * @param KeywordValidator|null $keywordValidator * @param Keyword[][]|null $types * @param Keyword[]|null $before * @param Keyword[]|null $after */ public function __construct(SchemaInfo $info, ?KeywordValidator $keywordValidator, ?array $types, ?array $before, ?array $after) { parent::__construct($info); $this->types = $types; $this->before = $before; $this->after = $after; $this->keywordValidator = $keywordValidator; if ($keywordValidator) { while ($next = $keywordValidator->next()) { $keywordValidator = $next; } $keywordValidator->setNext(new CallbackKeywordValidator([$this, 'doValidate'])); } } /** * @inheritDoc */ public function validate(ValidationContext $context): ?ValidationError { $context->pushSharedObject($this); $error = $this->keywordValidator ? $this->keywordValidator->validate($context) : $this->doValidate($context); $context->popSharedObject(); return $error; } /** * @param ValidationContext $context * @return null|ValidationError *@internal */ public function doValidate(ValidationContext $context): ?ValidationError { if ($this->before && ($error = $this->applyKeywords($this->before, $context))) { return $error; } if ($this->types && ($type = $context->currentDataType())) { if (isset($this->types[$type]) && ($error = $this->applyKeywords($this->types[$type], $context))) { return $error; } if (($type = Helper::getJsonSuperType($type)) && isset($this->types[$type])) { if ($error = $this->applyKeywords($this->types[$type], $context)) { return $error; } } unset($type); } if ($this->after && ($error = $this->applyKeywords($this->after, $context))) { return $error; } return null; } /** * @param Keyword[] $keywords * @param ValidationContext $context * @return ValidationError|null */ protected function applyKeywords(array $keywords, ValidationContext $context): ?ValidationError { if ($context->stopAtFirstError()) { foreach ($keywords as $keyword) { if ($error = $keyword->validate($context, $this)) { return $error; } } return null; } /** @var null|ValidationError[] $error_list */ $error_list = null; foreach ($keywords as $keyword) { if ($error = $keyword->validate($context, $this)) { $error_list ??= []; $error_list[] = $error; } } if (!$error_list) { return null; } if (count($error_list) === 1) { return $error_list[0]; } return new ValidationError( '', $this, DataInfo::fromContext($context), 'Data must match schema', [], $error_list ); } } json-schema/src/Schemas/BooleanSchema.php 0000777 00000002704 15252016106 0014346 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Schemas; use Opis\JsonSchema\ValidationContext; use Opis\JsonSchema\Info\{DataInfo, SchemaInfo}; use Opis\JsonSchema\Errors\ValidationError; final class BooleanSchema extends AbstractSchema { private bool $data; /** * @param SchemaInfo $info */ public function __construct(SchemaInfo $info) { parent::__construct($info); $this->data = $info->data(); } /** * @inheritDoc */ public function validate(ValidationContext $context): ?ValidationError { if ($this->data) { return null; } return new ValidationError('', $this, DataInfo::fromContext($context), 'Data not allowed'); } } json-schema/src/Schemas/ExceptionSchema.php 0000777 00000002735 15252016106 0014731 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Schemas; use Opis\JsonSchema\ValidationContext; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\Exceptions\SchemaException; final class ExceptionSchema extends AbstractSchema { private SchemaException $exception; /** * @param SchemaInfo $info * @param SchemaException $exception */ public function __construct(SchemaInfo $info, SchemaException $exception) { parent::__construct($info); $this->exception = $exception; } /** * @inheritDoc * @throws SchemaException */ public function validate(ValidationContext $context): ?ValidationError { throw $this->exception; } } json-schema/src/Schemas/EmptySchema.php 0000777 00000003165 15252016106 0014067 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Schemas; use Opis\JsonSchema\ValidationContext; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\KeywordValidator; final class EmptySchema extends AbstractSchema { protected ?KeywordValidator $keywordValidator; /** * @inheritDoc */ public function __construct(SchemaInfo $info, ?KeywordValidator $keywordValidator = null) { parent::__construct($info); $this->keywordValidator = $keywordValidator; } /** * @inheritDoc */ public function validate(ValidationContext $context): ?ValidationError { if (!$this->keywordValidator) { return null; } $context->pushSharedObject($this); $error = $this->keywordValidator->validate($context); $context->popSharedObject(); return $error; } } json-schema/src/Schemas/LazySchema.php 0000777 00000003266 15252016106 0013712 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Schemas; use Opis\JsonSchema\{ValidationContext, Info\SchemaInfo, Schema}; use Opis\JsonSchema\Parsers\SchemaParser; use Opis\JsonSchema\Errors\ValidationError; final class LazySchema extends AbstractSchema { private SchemaParser $parser; private ?Schema $schema = null; /** * @param SchemaInfo $info * @param SchemaParser $parser */ public function __construct(SchemaInfo $info, SchemaParser $parser) { parent::__construct($info); $this->parser = $parser; } /** * @inheritDoc */ public function validate(ValidationContext $context): ?ValidationError { return $this->schema()->validate($context); } /** * @return Schema */ public function schema(): Schema { if ($this->schema === null) { $this->schema = $this->parser->parseSchema($this->info); } return $this->schema; } } json-schema/src/Schemas/AbstractSchema.php 0000777 00000002261 15252016106 0014530 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\Schemas; use Opis\JsonSchema\Schema; use Opis\JsonSchema\Info\SchemaInfo; abstract class AbstractSchema implements Schema { protected SchemaInfo $info; /** * @param SchemaInfo $info */ public function __construct(SchemaInfo $info) { $this->info = $info; } /** * @inheritDoc */ public function info(): SchemaInfo { return $this->info; } } json-schema/src/KeywordValidators/AbstractKeywordValidator.php 0000777 00000002351 15252016106 0020714 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\KeywordValidators; use Opis\JsonSchema\KeywordValidator; abstract class AbstractKeywordValidator implements KeywordValidator { protected ?KeywordValidator $next = null; /** * @inheritDoc */ public function next(): ?KeywordValidator { return $this->next; } /** * @inheritDoc */ public function setNext(?KeywordValidator $next): KeywordValidator { $this->next = $next; return $this; } } json-schema/src/KeywordValidators/CallbackKeywordValidator.php 0000777 00000003101 15252016106 0020637 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\KeywordValidators; use Opis\JsonSchema\{ValidationContext, KeywordValidator}; use Opis\JsonSchema\Errors\ValidationError; final class CallbackKeywordValidator implements KeywordValidator { /** @var callable */ private $callback; /** * @param callable $callback */ public function __construct(callable $callback) { $this->callback = $callback; } /** * @inheritDoc */ public function validate(ValidationContext $context): ?ValidationError { return ($this->callback)($context); } /** * @inheritDoc */ public function next(): ?KeywordValidator { return null; } /** * @inheritDoc */ public function setNext(?KeywordValidator $next): KeywordValidator { return $this; } } json-schema/src/KeywordValidators/PragmaKeywordValidator.php 0000777 00000003432 15252016106 0020361 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema\KeywordValidators; use Opis\JsonSchema\{ValidationContext, Pragma}; use Opis\JsonSchema\Errors\ValidationError; final class PragmaKeywordValidator extends AbstractKeywordValidator { /** @var Pragma[] */ protected array $pragmas = []; /** * @param Pragma[] $pragmas */ public function __construct(array $pragmas) { $this->pragmas = $pragmas; } /** * @inheritDoc */ public function validate(ValidationContext $context): ?ValidationError { if (!$this->next) { return null; } if (!$this->pragmas) { return $this->next->validate($context); } $data = []; foreach ($this->pragmas as $key => $handler) { $data[$key] = $handler->enter($context); } $error = $this->next->validate($context); foreach (array_reverse($this->pragmas, true) as $key => $handler) { $handler->leave($context, $data[$key] ?? null); } return $error; } } json-schema/src/SchemaLoader.php 0000777 00000030737 15252016106 0012621 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; use SplObjectStorage; use Opis\JsonSchema\Info\SchemaInfo; use Opis\JsonSchema\Schemas\LazySchema; use Opis\JsonSchema\Parsers\SchemaParser; use Opis\JsonSchema\Resolvers\SchemaResolver; use Opis\JsonSchema\Exceptions\DuplicateSchemaIdException; class SchemaLoader { /** @var Schema[]|object[] */ protected array $uriCache = []; protected SplObjectStorage $dataCache; protected SchemaParser $parser; protected ?SchemaResolver $resolver; protected bool $decodeJsonString = false; protected ?Uri $base = null; /** * @param null|SchemaParser $parser * @param null|SchemaResolver $resolver * @param bool $decodeJsonString */ public function __construct(?SchemaParser $parser = null, ?SchemaResolver $resolver = null, bool $decodeJsonString = true) { $this->dataCache = new SplObjectStorage(); $this->parser = $parser ?? new SchemaParser(); $this->resolver = $resolver; $this->decodeJsonString = $decodeJsonString; } public function baseUri(): ?Uri { return $this->base; } public function setBaseUri(?Uri $uri): self { $this->base = $uri; return $this; } public function parser(): SchemaParser { return $this->parser; } public function setParser(SchemaParser $parser): self { $this->parser = $parser; return $this; } public function resolver(): ?SchemaResolver { return $this->resolver; } public function setResolver(?SchemaResolver $resolver): self { $this->resolver = $resolver; return $this; } /** * @param object $data * @param null $id * @param string|null $draft * @return Schema */ public function loadObjectSchema(object $data, $id = null, ?string $draft = null): Schema { // Check if already loaded if ($schema = $this->checkExistingObject($data)) { return $schema; } if (!$id) { $id = $this->createSchemaId($data); } $handle_id = fn (Uri $id): ?Schema => $this->checkExistingUri($id); $handle_object = function (object $data, Uri $id, string $draft): ?Schema { $this->handleObject($data, $id, null, null, [], $draft, (string)$id); return $this->checkExistingObject($data); }; return $this->parser->parseRootSchema($data, Uri::parse($id, true), $handle_id, $handle_object, $draft); } /** * @param bool $data * @param null $id * @param string|null $draft * @return Schema */ public function loadBooleanSchema(bool $data, $id = null, ?string $draft = null): Schema { if (!$id) { $id = $this->createSchemaId($data); } return $this->parser->parseSchema(new SchemaInfo($data, Uri::parse($id, true), null, null, [], $draft)); } /** * @param Uri $uri * @return Schema|null */ public function loadSchemaById(Uri $uri): ?Schema { if (!$uri->isAbsolute()) { if ($this->base === null || !$this->base->isAbsolute()) { return null; } $uri = $this->base->resolveRef($uri); } $fragment = $uri->fragment(); if ($fragment === null) { $uri = Uri::merge($uri, null, true); $fragment = ''; } $schema = $this->checkExistingUri($uri); if ($schema !== null) { return $schema; } if ($fragment === '') { return $this->resolve($uri); } $root = Uri::merge('#', $uri); // Check if already resolved if (($schema = $this->checkExistingUri($root)) === null) { // Try to resolve if (($schema = $this->resolve($root)) === null) { // Schema not found return null; } } // Resolve json pointer if ($fragment !== '' && $schema && $schema->info()->isObject() && ($pointer = JsonPointer::parse($fragment)) && $pointer->isAbsolute()) { $object = $pointer->data($schema->info()->data()); if (is_bool($object)) { $schema = $this->loadBooleanSchema($object, $uri, $schema->info()->draft()); } elseif (is_object($object)) { $schema = $this->loadObjectSchema($object, $uri, $schema->info()->draft()); } else { $schema = null; } if ($schema) { $key = $this->cacheKey((string) $uri); $this->uriCache[$key] = $schema; return $schema; } } // Check fragment return $this->checkExistingUri($uri); } /** * Clears internal cache */ public function clearCache(): void { $this->dataCache->removeAll($this->dataCache); $this->uriCache = []; } /** * @param Uri $uri * @return null|Schema */ protected function resolve(Uri $uri): ?Schema { if ($this->resolver === null) { return null; } $data = $this->resolver->resolve($uri); if ($this->decodeJsonString && is_string($data)) { $data = json_decode($data, false); } if (is_bool($data)) { $this->handleBoolean($data, $uri, null, null, [], $this->parser->defaultDraftVersion(), (string)$uri); return $this->checkExistingUri($uri); } if (is_object($data)) { if ($data instanceof Schema) { return $data; } $this->handleObject($data, $uri, null, null, [], $this->parser->defaultDraftVersion(), (string)$uri); return $this->checkExistingObject($data); } return null; } /** * @param object $data * @return null|Schema */ protected function checkExistingObject(object $data): ?Schema { if (!$this->dataCache->offsetExists($data)) { return null; } $schema = $this->dataCache[$data]; if ($schema instanceof LazySchema) { $schema = $schema->schema(); $this->dataCache[$data] = $schema; } elseif (!($schema instanceof Schema)) { $schema = null; } return $schema; } /** * @param Uri $uri * @return null|Schema */ protected function checkExistingUri(Uri $uri): ?Schema { if ($uri->fragment() === null || !$uri->isAbsolute()) { return null; } $key = $this->cacheKey((string)$uri); if (!isset($this->uriCache[$key])) { return null; } $schema = $this->uriCache[$key]; if (!($schema instanceof Schema)) { return $this->uriCache[$key] = $this->checkExistingObject($schema); } if ($schema instanceof LazySchema) { $schema = $schema->schema(); $this->uriCache[$key] = $schema; } return $schema; } /** * @param bool $data * @param Uri|null $id * @param Uri|null $base * @param Uri|null $root * @param array $path * @param string $draft * @param string $pointer */ protected function handleBoolean( bool $data, ?Uri $id, ?Uri $base, ?Uri $root, array $path, string $draft, string $pointer ) { $key = $this->cacheKey($pointer); if (isset($this->uriCache[$key])) { return; } $this->uriCache[$key] = $this->parser->parseSchema(new SchemaInfo($data, $id, $base, $root, $path, $draft)); } /** * @param array $data * @param Uri $base * @param Uri $root * @param array $path * @param string $draft * @param string $pointer */ protected function handleArray(array $data, Uri $base, Uri $root, array $path, string $draft, string $pointer) { foreach ($data as $key => $value) { if (!is_int($key)) { continue; } if (is_bool($value)) { $this->handleBoolean($value, null, $base, $root, array_merge($path, [$key]), $draft, $pointer . '/' . $key); } elseif (is_array($value)) { $this->handleArray($value, $base, $root, array_merge($path, [$key]), $draft, $pointer . '/' . $key); } elseif (is_object($value)) { $this->handleObject($value, null, $base, $root, array_merge($path, [$key]), $draft, $pointer . '/' . $key); } } } /** * @param object $data * @param Uri|null $id * @param Uri|null $base * @param Uri|null $root * @param array $path * @param string $draft * @param string $pointer */ protected function handleObject( object $data, ?Uri $id, ?Uri $base, ?Uri $root, array $path, string $draft, string $pointer ) { $schema_id = $this->parser->parseId($data); $schema_anchor = $this->parser->parseAnchor($data, $draft); $draft = $this->parser->parseSchemaDraft($data) ?? $draft; if ($schema_id !== null) { $id = Uri::merge($schema_id, $base, true); } elseif ($schema_anchor !== null) { $id = Uri::merge('#' . $schema_anchor, $base, true); } $lazy = new LazySchema(new SchemaInfo($data, $id, $base, $root, $path, $draft), $this->parser); if ($id && $id->isAbsolute()) { $key = $this->cacheKey((string)$id); if (isset($this->uriCache[$key])) { throw new DuplicateSchemaIdException($id, $data); } $this->uriCache[$key] = $lazy; } // When $id and $anchor are both present add a reference to the same lazy object if ($schema_id !== null && $schema_anchor !== null) { $anchor_id = Uri::merge('#' . $schema_anchor, $id, true); $key = $this->cacheKey((string)$anchor_id); if (isset($this->uriCache[$key])) { throw new DuplicateSchemaIdException($anchor_id, $data); } $this->uriCache[$key] = $lazy; } $this->dataCache[$data] = $lazy; $this->uriCache[$this->cacheKey($pointer)] = $lazy; if ($root === null) { $root = $id; } if ($base === null) { $base = $id ?? $root; } elseif ($id !== null) { $base = $id; } foreach ($data as $key => $value) { if (!is_string($key)) { continue; } if (is_bool($value)) { $this->handleBoolean($value, null, $base, $root, array_merge($path, [$key]), $draft, $pointer . '/' . JsonPointer::encodePath($key)); } elseif (is_array($value)) { $this->handleArray($value, $base, $root, array_merge($path, [$key]), $draft, $pointer . '/' . JsonPointer::encodePath($key)); } elseif (is_object($value)) { $this->handleObject($value, null, $base, $root, array_merge($path, [$key]), $draft, $pointer . '/' . JsonPointer::encodePath($key)); } } } /** * @param string $path * @return string */ protected function cacheKey(string $path): string { return isset($path[32]) ? md5($path) : $path; } /** * @param bool|object $data * @return string */ protected function createSchemaId($data): string { if (is_bool($data)) { $data = $data ? 'true' : 'false'; } else { $data = spl_object_hash($data); } return "schema:///{$data}.json"; } } json-schema/src/Keyword.php 0000777 00000002055 15252016106 0011706 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; use Opis\JsonSchema\Errors\ValidationError; interface Keyword { /** * @param ValidationContext $context * @param Schema $schema * @return null|ValidationError */ public function validate(ValidationContext $context, Schema $schema): ?ValidationError; } json-schema/src/ContentMediaType.php 0000777 00000001737 15252016106 0013504 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; interface ContentMediaType { /** * @param string $content * @param string $media_type * @return bool */ public function validate(string $content, string $media_type): bool; } json-schema/src/Pragma.php 0000777 00000002211 15252016106 0011463 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; interface Pragma { /** * @param ValidationContext $context * @return mixed Data to pass to leave() method */ public function enter(ValidationContext $context); /** * @param ValidationContext $context * @param mixed $data Data returned by the enter() method */ public function leave(ValidationContext $context, $data): void; } json-schema/src/Validator.php 0000777 00000017777 15252016106 0012230 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; use InvalidArgumentException, RuntimeException; use Opis\JsonSchema\Parsers\SchemaParser; use Opis\JsonSchema\Errors\ValidationError; use Opis\JsonSchema\Resolvers\{SchemaResolver}; class Validator { protected SchemaLoader $loader; protected int $maxErrors = 1; protected bool $stopAtFirstError = true; /** * @param SchemaLoader|null $loader * @param int $max_errors * @param bool $stop_at_first_error */ public function __construct( ?SchemaLoader $loader = null, int $max_errors = 1, bool $stop_at_first_error = true ) { $this->loader = $loader ?? new SchemaLoader(new SchemaParser(), new SchemaResolver(), true); $this->maxErrors = $max_errors; $this->stopAtFirstError = $stop_at_first_error; } /** * @param $data * @param bool|string|Uri|Schema|object $schema * @param array|null $globals * @param array|null $slots * @return ValidationResult */ public function validate($data, $schema, ?array $globals = null, ?array $slots = null): ValidationResult { if (is_string($schema)) { if ($uri = Uri::parse($schema, true)) { $schema = $uri; } else { $schema = json_decode($schema, false); } } $error = null; if (is_bool($schema)) { $error = $this->dataValidation($data, $schema, $globals, $slots); } elseif (is_object($schema)) { if ($schema instanceof Uri) { $error = $this->uriValidation($data, $schema, $globals, $slots); } elseif ($schema instanceof Schema) { $error = $this->schemaValidation($data, $schema, $globals, $slots); } else { $error = $this->dataValidation($data, $schema, $globals, $slots); } } else { throw new InvalidArgumentException("Invalid schema"); } return new ValidationResult($error); } /** * @param $data * @param Uri|string $uri * @param array|null $globals * @param array|null $slots * @return null|ValidationError */ public function uriValidation($data, $uri, ?array $globals = null, ?array $slots = null): ?ValidationError { if (is_string($uri)) { $uri = Uri::parse($uri, true); } if (!($uri instanceof Uri)) { throw new InvalidArgumentException("Invalid uri"); } if ($uri->fragment() === null) { $uri = Uri::merge($uri, null, true); } $schema = $this->loader->loadSchemaById($uri); if ($schema === null) { throw new RuntimeException("Schema not found: $uri"); } return $this->schemaValidation($data, $schema, $globals, $slots); } /** * @param $data * @param string|object|bool $schema * @param array|null $globals * @param array|null $slots * @param string|null $id * @param string|null $draft * @return ValidationError|null */ public function dataValidation( $data, $schema, ?array $globals = null, ?array $slots = null, ?string $id = null, ?string $draft = null ): ?ValidationError { if (is_string($schema)) { $schema = json_decode($schema, false); } if ($schema === true) { return null; } if ($schema === false) { $schema = $this->loader->loadBooleanSchema(false, $id, $draft); } else { if (!is_object($schema)) { throw new InvalidArgumentException("Invalid schema"); } $schema = $this->loader->loadObjectSchema($schema, $id, $draft); } return $this->schemaValidation($data, $schema, $globals, $slots); } /** * @param $data * @param Schema $schema * @param array|null $globals * @param array|null $slots * @return null|ValidationError */ public function schemaValidation( $data, Schema $schema, ?array $globals = null, ?array $slots = null ): ?ValidationError { return $schema->validate($this->createContext($data, $globals, $slots)); } /** * @param $data * @param array|null $globals * @param array|null $slots * @return ValidationContext */ public function createContext($data, ?array $globals = null, ?array $slots = null): ValidationContext { if ($slots) { $slots = $this->parseSlots($slots); } return new ValidationContext( $data, $this->loader, null, null, $globals ?? [], $slots, $this->maxErrors, $this->stopAtFirstError, ); } /** * @return SchemaParser */ public function parser(): SchemaParser { return $this->loader->parser(); } /** * @param SchemaParser $parser * @return Validator */ public function setParser(SchemaParser $parser): self { $this->loader->setParser($parser); return $this; } /** * @return SchemaResolver|null */ public function resolver(): ?SchemaResolver { return $this->loader->resolver(); } /** * @param SchemaResolver|null $resolver * @return Validator */ public function setResolver(?SchemaResolver $resolver): self { $this->loader->setResolver($resolver); return $this; } /** * @return SchemaLoader */ public function loader(): SchemaLoader { return $this->loader; } /** * @param SchemaLoader $loader * @return Validator */ public function setLoader(SchemaLoader $loader): self { $this->loader = $loader; return $this; } /** * @return int */ public function getMaxErrors(): int { return $this->maxErrors; } /** * @param int $max_errors * @return Validator */ public function setMaxErrors(int $max_errors): self { $this->maxErrors = $max_errors; return $this; } /** * @return bool */ public function getStopAtFirstError(): bool { return $this->stopAtFirstError; } /** * @param bool $stop * @return $this */ public function setStopAtFirstError(bool $stop): self { $this->stopAtFirstError = $stop; return $this; } /** * @param array $slots * @return array */ protected function parseSlots(array $slots): array { foreach ($slots as $name => &$value) { if (!is_string($name)) { unset($slots[$name]); continue; } if (is_string($value)) { $value = Uri::parse($value, true); } if ($value instanceof Uri) { $value = $this->loader->loadSchemaById($value); } elseif (is_bool($value)) { $value = $this->loader->loadBooleanSchema($value); } if (!is_object($value)) { unset($slots[$name]); } unset($value); } return $slots; } } json-schema/src/Filter.php 0000777 00000002017 15252016106 0011505 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; interface Filter { /** * @param ValidationContext $context * @param Schema $schema * @param array $args * @return bool */ public function validate(ValidationContext $context, Schema $schema, array $args = []): bool; } json-schema/src/JsonPointer.php 0000777 00000023552 15252016106 0012541 0 ustar 00 <?php /* ============================================================================ * Copyright 2020 Zindex Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================ */ namespace Opis\JsonSchema; final class JsonPointer { /** @var string */ protected const PATTERN = '~^(?:(?<level>0|[1-9][0-9]*)(?<shift>(?:\+|-)(?:0|[1-9][0-9]*))?)?(?<pointer>(?:/[^/#]*)*)(?<fragment>#)?$~'; /** @var string */ protected const UNESCAPED = '/~([^01]|$)/'; protected int $level = -1; protected int $shift = 0; protected bool $fragment = false; /** @var string[]|int[] */ protected array $path; protected ?string $str = null; final protected function __construct(array $path, int $level = -1, int $shift = 0, bool $fragment = false) { $this->path = $path; $this->level = $level < 0 ? -1 : $level; $this->shift = $shift; $this->fragment = $level >= 0 && $fragment; } public function isRelative(): bool { return $this->level >= 0; } public function isAbsolute(): bool { return $this->level < 0; } public function level(): int { return $this->level; } public function shift(): int { return $this->shift; } /** * @return string[] */ public function path(): array { return $this->path; } /** * @return bool */ public function hasFragment(): bool { return $this->fragment; } /** * @return string */ public function __toString(): string { if ($this->str === null) { if ($this->level >= 0) { $this->str = (string)$this->level; if ($this->shift !== 0) { if ($this->shift > 0) { $this->str .= '+'; } $this->str .= $this->shift; } if ($this->path) { $this->str .= '/'; $this->str .= implode('/', self::encodePath($this->path)); } if ($this->fragment) { $this->str .= '#'; } } else { $this->str = '/'; $this->str .= implode('/', self::encodePath($this->path)); } } return $this->str; } /** * @param $data * @param array|null $path * @param mixed $default * @return mixed */ public function data($data, ?array $path = null, $default = null) { if ($this->level < 0) { return self::getData($data, $this->path, false, $default); } if ($path !== null) { $path = $this->absolutePath($path); } if ($path === null) { return $default; } return self::getData($data, $path, $this->fragment, $default); } /** * @param array $path * @return array|null */ public function absolutePath(array $path = []): ?array { if ($this->level < 0) { // Absolute pointer return $this->path; } if ($this->level === 0) { if ($this->shift && !$this->handleShift($path)) { return null; } return $this->path ? array_merge($path, $this->path) : $path; } $count = count($path); if ($count === $this->level) { if ($this->shift) { return null; } return $this->path; } if ($count > $this->level) { $count -= $this->level; /** @var array $path */ $path = array_slice($path, 0, $count); if ($this->shift && !$this->handleShift($path, $count)) { return null; } return $this->path ? array_merge($path, $this->path) : $path; } return null; } protected function handleShift(array &$path, ?int $count = null): bool { if (!$path) { return false; } $count ??= count($path); $last = $path[$count - 1]; if (is_string($last) && preg_match('/^[1-9]\d*$/', $last)) { $last = (int) $last; } if (!is_int($last)) { return false; } $path[$count - 1] = $last + $this->shift; return true; } public static function parse(string $pointer, bool $decode = true): ?self { if ($pointer === '' || !preg_match(self::PATTERN, $pointer, $m)) { // Not a pointer return null; } $pointer = $m['pointer'] ?? null; // Check if the pointer is escaped if ($decode && $pointer && preg_match(self::UNESCAPED, $pointer)) { // Invalid pointer return null; } $level = isset($m['level']) && $m['level'] !== '' ? (int)$m['level'] : -1; $shift = 0; if ($level >= 0 && isset($m['shift']) && $m['shift'] !== '') { $shift = (int) $m['shift']; } $fragment = isset($m['fragment']) && $m['fragment'] === '#'; unset($m); if ($fragment && $level < 0) { return null; } if ($pointer === '') { $pointer = null; } elseif ($pointer !== null) { // Remove leading slash $pointer = substr($pointer, 1); if ($pointer !== '') { $pointer = self::decodePath(explode('/', $pointer)); } else { $pointer = null; } } return new self($pointer ?? [], $level, $shift, $fragment); } /** * @param $data * @param array|null $path * @param bool $fragment * @param mixed $default * @return mixed */ public static function getData($data, ?array $path = null, bool $fragment = false, $default = null) { if ($path === null) { return $default; } if (!$path) { return $fragment ? $default : $data; } if ($fragment) { return end($path); } foreach ($path as $key) { if (is_array($data)) { if (!array_key_exists($key, $data)) { return $default; } $data = $data[$key]; } elseif (is_object($data)) { if (!property_exists($data, $key)) { return $default; } $data = $data->{$key}; } else { return $default; } } return $data; } /** * @param string|string[] $path * @return string|string[] */ public static function encodePath($path) { $path = str_replace('~', '~0', $path); $path = str_replace('/', '~1', $path); if (is_array($path)) { return array_map('rawurlencode', $path); } return rawurlencode($path); } /** * @param string|string[] $path * @return string|string[] */ public static function decodePath($path) { if (is_array($path)) { $path = array_map('rawurldecode', $path); } else { $path = rawurldecode($path); } $path = str_replace('~1', '/', $path); $path = str_replace('~0', '~', $path); return $path; } /** * @param array $path * @return string */ public static function pathToString(array $path): string { if (!$path) { return '/'; } return '/' . implode('/', self::encodePath($path)); } /** * @param array $path * @return string */ public static function pathToFragment(array $path): string { if (!$path) { return '#'; } return '#/' . implode('/', self::encodePath($path)); } /** * @param string $pointer * @return bool */ public static function isAbsolutePointer(string $pointer): bool { if ($pointer === '/') { return true; } if (!preg_match(self::PATTERN, $pointer, $m)) { return false; } if (isset($m['fragment']) || isset($m['level']) && $m['level'] !== '') { return false; } if (!isset($m['pointer']) || $m['pointer'] === '') { return true; } return !preg_match(self::UNESCAPED, $m['pointer']); } /** * @param string $pointer * @return bool */ public static function isRelativePointer(string $pointer): bool { if ($pointer === '') { return false; } if (!preg_match(self::PATTERN, $pointer, $m)) { return false; } if (!isset($m['level']) || $m['level'] === '' || (int)$m['level'] < 0) { return false; } if (!isset($m['pointer']) || $m['pointer'] === '') { return true; } return !preg_match(self::UNESCAPED, $m['pointer']); } public static function createAbsolute(array $path): self { return new self($path, -1, 0, false); } public static function createRelative(int $level, array $path = [], int $shift = 0, bool $fragment = false): self { return new self($path, $level, $shift, $fragment); } } json-schema/LICENSE 0000777 00000023674 15252016106 0010001 0 ustar 00 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS json-schema/composer.json 0000777 00000002171 15252016106 0011503 0 ustar 00 { "name": "opis/json-schema", "description": "Json Schema Validator for PHP", "keywords": [ "json", "schema", "json-schema", "validation", "validator" ], "homepage": "https://opis.io/json-schema", "license": "Apache-2.0", "authors": [ { "name": "Sorin Sarca", "email": "sarca_sorin@hotmail.com" }, { "name": "Marius Sarca", "email": "marius.sarca@gmail.com" } ], "require": { "php": "^7.4 || ^8.0", "ext-json": "*", "opis/string": "^2.1", "opis/uri": "^1.0" }, "require-dev": { "ext-bcmath": "*", "ext-intl": "*", "phpunit/phpunit": "^9.0" }, "autoload": { "psr-4": { "Opis\\JsonSchema\\": "src/" } }, "autoload-dev": { "psr-4": { "Opis\\JsonSchema\\Test\\": "tests/" } }, "scripts": { "tests": "./vendor/bin/phpunit --verbose --color" }, "extra": { "branch-alias": { "dev-master": "2.x-dev" } } }
| ver. 1.6 |
Github
|
.
| PHP 8.1.34 | Генерация страницы: 0.01 |
proxy
|
phpinfo
|
Настройка