Import latest app updates from streamline

An updated set of source files and initialization was provided by streamline to
address issues observed during initial testing. These files have been updated in
order to generate a new set of app images.
This commit is contained in:
2024-04-11 11:16:51 -07:00
parent cb4c6b4c1a
commit 4a89356390
10435 changed files with 107737 additions and 1220538 deletions
@@ -1,51 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema;
final class Context
{
public bool $skipDefaults = false;
/** @var string[] */
public array $path = [];
public bool $isKey = false;
/** @var Message[] */
public array $errors = [];
/** @var Message[] */
public array $warnings = [];
/** @var array[] */
public array $dynamics = [];
public function addError(string $message, string $code, array $variables = []): Message
{
$variables['isKey'] = $this->isKey;
return $this->errors[] = new Message($message, $code, $this->path, $variables);
}
public function addWarning(string $message, string $code, array $variables = []): Message
{
return $this->warnings[] = new Message($message, $code, $this->path, $variables);
}
/** @return \Closure(): bool */
public function createChecker(): \Closure
{
$count = count($this->errors);
return fn(): bool => $count === count($this->errors);
}
}
@@ -1,15 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema;
interface DynamicParameter
{
}
@@ -1,147 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema\Elements;
use Nette;
use Nette\Schema\Context;
use Nette\Schema\Helpers;
use Nette\Schema\Schema;
final class AnyOf implements Schema
{
use Base;
private array $set;
public function __construct(mixed ...$set)
{
if (!$set) {
throw new Nette\InvalidStateException('The enumeration must not be empty.');
}
$this->set = $set;
}
public function firstIsDefault(): self
{
$this->default = $this->set[0];
return $this;
}
public function nullable(): self
{
$this->set[] = null;
return $this;
}
public function dynamic(): self
{
$this->set[] = new Type(Nette\Schema\DynamicParameter::class);
return $this;
}
/********************* processing ****************d*g**/
public function normalize(mixed $value, Context $context): mixed
{
return $this->doNormalize($value, $context);
}
public function merge(mixed $value, mixed $base): mixed
{
if (is_array($value) && isset($value[Helpers::PreventMerging])) {
unset($value[Helpers::PreventMerging]);
return $value;
}
return Helpers::merge($value, $base);
}
public function complete(mixed $value, Context $context): mixed
{
$isOk = $context->createChecker();
$value = $this->findAlternative($value, $context);
$isOk() && $value = $this->doTransform($value, $context);
return $isOk() ? $value : null;
}
private function findAlternative(mixed $value, Context $context): mixed
{
$expecteds = $innerErrors = [];
foreach ($this->set as $item) {
if ($item instanceof Schema) {
$dolly = new Context;
$dolly->path = $context->path;
$res = $item->complete($item->normalize($value, $dolly), $dolly);
if (!$dolly->errors) {
$context->warnings = array_merge($context->warnings, $dolly->warnings);
return $res;
}
foreach ($dolly->errors as $error) {
if ($error->path !== $context->path || empty($error->variables['expected'])) {
$innerErrors[] = $error;
} else {
$expecteds[] = $error->variables['expected'];
}
}
} else {
if ($item === $value) {
return $value;
}
$expecteds[] = Nette\Schema\Helpers::formatValue($item);
}
}
if ($innerErrors) {
$context->errors = array_merge($context->errors, $innerErrors);
} else {
$context->addError(
'The %label% %path% expects to be %expected%, %value% given.',
Nette\Schema\Message::TypeMismatch,
[
'value' => $value,
'expected' => implode('|', array_unique($expecteds)),
],
);
}
return null;
}
public function completeDefault(Context $context): mixed
{
if ($this->required) {
$context->addError(
'The mandatory item %path% is missing.',
Nette\Schema\Message::MissingItem,
);
return null;
}
if ($this->default instanceof Schema) {
return $this->default->completeDefault($context);
}
return $this->default;
}
}
@@ -1,162 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema\Elements;
use Nette;
use Nette\Schema\Context;
use Nette\Schema\Helpers;
/**
* @internal
*/
trait Base
{
private bool $required = false;
private mixed $default = null;
/** @var ?callable */
private $before;
/** @var callable[] */
private array $transforms = [];
private ?string $deprecated = null;
public function default(mixed $value): self
{
$this->default = $value;
return $this;
}
public function required(bool $state = true): self
{
$this->required = $state;
return $this;
}
public function before(callable $handler): self
{
$this->before = $handler;
return $this;
}
public function castTo(string $type): self
{
return $this->transform(Helpers::getCastStrategy($type));
}
public function transform(callable $handler): self
{
$this->transforms[] = $handler;
return $this;
}
public function assert(callable $handler, ?string $description = null): self
{
$expected = $description ?: (is_string($handler) ? "$handler()" : '#' . count($this->transforms));
return $this->transform(function ($value, Context $context) use ($handler, $description, $expected) {
if ($handler($value)) {
return $value;
}
$context->addError(
'Failed assertion ' . ($description ? "'%assertion%'" : '%assertion%') . ' for %label% %path% with value %value%.',
Nette\Schema\Message::FailedAssertion,
['value' => $value, 'assertion' => $expected],
);
});
}
/** Marks as deprecated */
public function deprecated(string $message = 'The item %path% is deprecated.'): self
{
$this->deprecated = $message;
return $this;
}
public function completeDefault(Context $context): mixed
{
if ($this->required) {
$context->addError(
'The mandatory item %path% is missing.',
Nette\Schema\Message::MissingItem,
);
return null;
}
return $this->default;
}
public function doNormalize(mixed $value, Context $context): mixed
{
if ($this->before) {
$value = ($this->before)($value);
}
return $value;
}
private function doDeprecation(Context $context): void
{
if ($this->deprecated !== null) {
$context->addWarning(
$this->deprecated,
Nette\Schema\Message::Deprecated,
);
}
}
private function doTransform(mixed $value, Context $context): mixed
{
$isOk = $context->createChecker();
foreach ($this->transforms as $handler) {
$value = $handler($value, $context);
if (!$isOk()) {
return null;
}
}
return $value;
}
/** @deprecated use Nette\Schema\Validators::validateType() */
private function doValidate(mixed $value, string $expected, Context $context): bool
{
$isOk = $context->createChecker();
Helpers::validateType($value, $expected, $context);
return $isOk();
}
/** @deprecated use Nette\Schema\Validators::validateRange() */
private static function doValidateRange(mixed $value, array $range, Context $context, string $types = ''): bool
{
$isOk = $context->createChecker();
Helpers::validateRange($value, $range, $context, $types);
return $isOk();
}
/** @deprecated use doTransform() */
private function doFinalize(mixed $value, Context $context): mixed
{
return $this->doTransform($value, $context);
}
}
@@ -1,200 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema\Elements;
use Nette;
use Nette\Schema\Context;
use Nette\Schema\Helpers;
use Nette\Schema\Schema;
final class Structure implements Schema
{
use Base;
/** @var Schema[] */
private array $items;
/** for array|list */
private ?Schema $otherItems = null;
/** @var array{?int, ?int} */
private array $range = [null, null];
private bool $skipDefaults = false;
/**
* @param Schema[] $items
*/
public function __construct(array $items)
{
(function (Schema ...$items) {})(...array_values($items));
$this->items = $items;
$this->castTo('object');
$this->required = true;
}
public function default(mixed $value): self
{
throw new Nette\InvalidStateException('Structure cannot have default value.');
}
public function min(?int $min): self
{
$this->range[0] = $min;
return $this;
}
public function max(?int $max): self
{
$this->range[1] = $max;
return $this;
}
public function otherItems(string|Schema $type = 'mixed'): self
{
$this->otherItems = $type instanceof Schema ? $type : new Type($type);
return $this;
}
public function skipDefaults(bool $state = true): self
{
$this->skipDefaults = $state;
return $this;
}
/********************* processing ****************d*g**/
public function normalize(mixed $value, Context $context): mixed
{
if ($prevent = (is_array($value) && isset($value[Helpers::PreventMerging]))) {
unset($value[Helpers::PreventMerging]);
}
$value = $this->doNormalize($value, $context);
if (is_object($value)) {
$value = (array) $value;
}
if (is_array($value)) {
foreach ($value as $key => $val) {
$itemSchema = $this->items[$key] ?? $this->otherItems;
if ($itemSchema) {
$context->path[] = $key;
$value[$key] = $itemSchema->normalize($val, $context);
array_pop($context->path);
}
}
if ($prevent) {
$value[Helpers::PreventMerging] = true;
}
}
return $value;
}
public function merge(mixed $value, mixed $base): mixed
{
if (is_array($value) && isset($value[Helpers::PreventMerging])) {
unset($value[Helpers::PreventMerging]);
$base = null;
}
if (is_array($value) && is_array($base)) {
$index = 0;
foreach ($value as $key => $val) {
if ($key === $index) {
$base[] = $val;
$index++;
} elseif (array_key_exists($key, $base)) {
$itemSchema = $this->items[$key] ?? $this->otherItems;
$base[$key] = $itemSchema
? $itemSchema->merge($val, $base[$key])
: Helpers::merge($val, $base[$key]);
} else {
$base[$key] = $val;
}
}
return $base;
}
return Helpers::merge($value, $base);
}
public function complete(mixed $value, Context $context): mixed
{
if ($value === null) {
$value = []; // is unable to distinguish null from array in NEON
}
$this->doDeprecation($context);
$isOk = $context->createChecker();
Helpers::validateType($value, 'array', $context);
$isOk() && Helpers::validateRange($value, $this->range, $context);
$isOk() && $this->validateItems($value, $context);
$isOk() && $value = $this->doTransform($value, $context);
return $isOk() ? $value : null;
}
private function validateItems(array &$value, Context $context): void
{
$items = $this->items;
if ($extraKeys = array_keys(array_diff_key($value, $items))) {
if ($this->otherItems) {
$items += array_fill_keys($extraKeys, $this->otherItems);
} else {
$keys = array_map('strval', array_keys($items));
foreach ($extraKeys as $key) {
$hint = Nette\Utils\Helpers::getSuggestion($keys, (string) $key);
$context->addError(
'Unexpected item %path%' . ($hint ? ", did you mean '%hint%'?" : '.'),
Nette\Schema\Message::UnexpectedItem,
['hint' => $hint],
)->path[] = $key;
}
}
}
foreach ($items as $itemKey => $itemVal) {
$context->path[] = $itemKey;
if (array_key_exists($itemKey, $value)) {
$value[$itemKey] = $itemVal->complete($value[$itemKey], $context);
} else {
$default = $itemVal->completeDefault($context); // checks required item
if (!$context->skipDefaults && !$this->skipDefaults) {
$value[$itemKey] = $default;
}
}
array_pop($context->path);
}
}
public function completeDefault(Context $context): mixed
{
return $this->required
? $this->complete([], $context)
: null;
}
}
@@ -1,208 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema\Elements;
use Nette\Schema\Context;
use Nette\Schema\DynamicParameter;
use Nette\Schema\Helpers;
use Nette\Schema\Schema;
final class Type implements Schema
{
use Base;
private string $type;
private ?Schema $itemsValue = null;
private ?Schema $itemsKey = null;
/** @var array{?float, ?float} */
private array $range = [null, null];
private ?string $pattern = null;
private bool $merge = true;
public function __construct(string $type)
{
$defaults = ['list' => [], 'array' => []];
$this->type = $type;
$this->default = strpos($type, '[]') ? [] : $defaults[$type] ?? null;
}
public function nullable(): self
{
$this->type = 'null|' . $this->type;
return $this;
}
public function mergeDefaults(bool $state = true): self
{
$this->merge = $state;
return $this;
}
public function dynamic(): self
{
$this->type = DynamicParameter::class . '|' . $this->type;
return $this;
}
public function min(?float $min): self
{
$this->range[0] = $min;
return $this;
}
public function max(?float $max): self
{
$this->range[1] = $max;
return $this;
}
/**
* @internal use arrayOf() or listOf()
*/
public function items(string|Schema $valueType = 'mixed', string|Schema $keyType = null): self
{
$this->itemsValue = $valueType instanceof Schema
? $valueType
: new self($valueType);
$this->itemsKey = $keyType instanceof Schema || $keyType === null
? $keyType
: new self($keyType);
return $this;
}
public function pattern(?string $pattern): self
{
$this->pattern = $pattern;
return $this;
}
/********************* processing ****************d*g**/
public function normalize(mixed $value, Context $context): mixed
{
if ($prevent = (is_array($value) && isset($value[Helpers::PreventMerging]))) {
unset($value[Helpers::PreventMerging]);
}
$value = $this->doNormalize($value, $context);
if (is_array($value) && $this->itemsValue) {
$res = [];
foreach ($value as $key => $val) {
$context->path[] = $key;
$context->isKey = true;
$key = $this->itemsKey
? $this->itemsKey->normalize($key, $context)
: $key;
$context->isKey = false;
$res[$key] = $this->itemsValue->normalize($val, $context);
array_pop($context->path);
}
$value = $res;
}
if ($prevent && is_array($value)) {
$value[Helpers::PreventMerging] = true;
}
return $value;
}
public function merge(mixed $value, mixed $base): mixed
{
if (is_array($value) && isset($value[Helpers::PreventMerging])) {
unset($value[Helpers::PreventMerging]);
return $value;
}
if (is_array($value) && is_array($base) && $this->itemsValue) {
$index = 0;
foreach ($value as $key => $val) {
if ($key === $index) {
$base[] = $val;
$index++;
} else {
$base[$key] = array_key_exists($key, $base)
? $this->itemsValue->merge($val, $base[$key])
: $val;
}
}
return $base;
}
return Helpers::merge($value, $base);
}
public function complete(mixed $value, Context $context): mixed
{
$merge = $this->merge;
if (is_array($value) && isset($value[Helpers::PreventMerging])) {
unset($value[Helpers::PreventMerging]);
$merge = false;
}
if ($value === null && is_array($this->default)) {
$value = []; // is unable to distinguish null from array in NEON
}
$this->doDeprecation($context);
$isOk = $context->createChecker();
Helpers::validateType($value, $this->type, $context);
$isOk() && Helpers::validateRange($value, $this->range, $context, $this->type);
$isOk() && $value !== null && $this->pattern !== null && Helpers::validatePattern($value, $this->pattern, $context);
$isOk() && is_array($value) && $this->validateItems($value, $context);
$isOk() && $merge && $value = Helpers::merge($value, $this->default);
$isOk() && $value = $this->doTransform($value, $context);
if (!$isOk()) {
return null;
}
if ($value instanceof DynamicParameter) {
$expected = $this->type . ($this->range === [null, null] ? '' : ':' . implode('..', $this->range));
$context->dynamics[] = [$value, str_replace(DynamicParameter::class . '|', '', $expected), $context->path];
}
return $value;
}
private function validateItems(array &$value, Context $context): void
{
if (!$this->itemsValue) {
return;
}
$res = [];
foreach ($value as $key => $val) {
$context->path[] = $key;
$context->isKey = true;
$key = $this->itemsKey ? $this->itemsKey->complete($key, $context) : $key;
$context->isKey = false;
$res[$key] = $this->itemsValue->complete($val, $context);
array_pop($context->path);
}
$value = $res;
}
}
@@ -1,108 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema;
use Nette;
use Nette\Schema\Elements\AnyOf;
use Nette\Schema\Elements\Structure;
use Nette\Schema\Elements\Type;
/**
* Schema generator.
*
* @method static Type scalar($default = null)
* @method static Type string($default = null)
* @method static Type int($default = null)
* @method static Type float($default = null)
* @method static Type bool($default = null)
* @method static Type null()
* @method static Type array($default = [])
* @method static Type list($default = [])
* @method static Type mixed($default = null)
* @method static Type email($default = null)
* @method static Type unicode($default = null)
*/
final class Expect
{
public static function __callStatic(string $name, array $args): Type
{
$type = new Type($name);
if ($args) {
$type->default($args[0]);
}
return $type;
}
public static function type(string $type): Type
{
return new Type($type);
}
public static function anyOf(mixed ...$set): AnyOf
{
return new AnyOf(...$set);
}
/**
* @param Schema[] $items
*/
public static function structure(array $items): Structure
{
return new Structure($items);
}
public static function from(object $object, array $items = []): Structure
{
$ro = new \ReflectionObject($object);
$props = $ro->hasMethod('__construct')
? $ro->getMethod('__construct')->getParameters()
: $ro->getProperties();
foreach ($props as $prop) {
$item = &$items[$prop->getName()];
if (!$item) {
$type = Helpers::getPropertyType($prop) ?? 'mixed';
$item = new Type($type);
if ($prop instanceof \ReflectionProperty ? $prop->isInitialized($object) : $prop->isOptional()) {
$def = ($prop instanceof \ReflectionProperty ? $prop->getValue($object) : $prop->getDefaultValue());
if (is_object($def)) {
$item = static::from($def);
} elseif ($def === null && !Nette\Utils\Validators::is(null, $type)) {
$item->required();
} else {
$item->default($def);
}
} else {
$item->required();
}
}
}
return (new Structure($items))->castTo($ro->getName());
}
public static function arrayOf(string|Schema $valueType, string|Schema $keyType = null): Type
{
return (new Type('array'))->items($valueType, $keyType);
}
public static function listOf(string|Schema $type): Type
{
return (new Type('list'))->items($type);
}
}
@@ -1,183 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema;
use Nette;
use Nette\Utils\Reflection;
/**
* @internal
*/
final class Helpers
{
use Nette\StaticClass;
public const PreventMerging = '_prevent_merging';
/**
* Merges dataset. Left has higher priority than right one.
*/
public static function merge(mixed $value, mixed $base): mixed
{
if (is_array($value) && isset($value[self::PreventMerging])) {
unset($value[self::PreventMerging]);
return $value;
}
if (is_array($value) && is_array($base)) {
$index = 0;
foreach ($value as $key => $val) {
if ($key === $index) {
$base[] = $val;
$index++;
} else {
$base[$key] = static::merge($val, $base[$key] ?? null);
}
}
return $base;
} elseif ($value === null && is_array($base)) {
return $base;
} else {
return $value;
}
}
public static function getPropertyType(\ReflectionProperty|\ReflectionParameter $prop): ?string
{
if ($type = Nette\Utils\Type::fromReflection($prop)) {
return (string) $type;
} elseif (
($prop instanceof \ReflectionProperty)
&& ($type = preg_replace('#\s.*#', '', (string) self::parseAnnotation($prop, 'var')))
) {
$class = Reflection::getPropertyDeclaringClass($prop);
return preg_replace_callback('#[\w\\\\]+#', fn($m) => Reflection::expandClassName($m[0], $class), $type);
}
return null;
}
/**
* Returns an annotation value.
* @param \ReflectionProperty $ref
*/
public static function parseAnnotation(\Reflector $ref, string $name): ?string
{
if (!Reflection::areCommentsAvailable()) {
throw new Nette\InvalidStateException('You have to enable phpDoc comments in opcode cache.');
}
$re = '#[\s*]@' . preg_quote($name, '#') . '(?=\s|$)(?:[ \t]+([^@\s]\S*))?#';
if ($ref->getDocComment() && preg_match($re, trim($ref->getDocComment(), '/*'), $m)) {
return $m[1] ?? '';
}
return null;
}
public static function formatValue(mixed $value): string
{
if ($value instanceof DynamicParameter) {
return 'dynamic';
} elseif (is_object($value)) {
return 'object ' . $value::class;
} elseif (is_string($value)) {
return "'" . Nette\Utils\Strings::truncate($value, 15, '...') . "'";
} elseif (is_scalar($value)) {
return var_export($value, return: true);
} else {
return get_debug_type($value);
}
}
public static function validateType(mixed $value, string $expected, Context $context): void
{
if (!Nette\Utils\Validators::is($value, $expected)) {
$expected = str_replace(DynamicParameter::class . '|', '', $expected);
$expected = str_replace(['|', ':'], [' or ', ' in range '], $expected);
$context->addError(
'The %label% %path% expects to be %expected%, %value% given.',
Message::TypeMismatch,
['value' => $value, 'expected' => $expected],
);
}
}
public static function validateRange(mixed $value, array $range, Context $context, string $types = ''): void
{
if (is_array($value) || is_string($value)) {
[$length, $label] = is_array($value)
? [count($value), 'items']
: (in_array('unicode', explode('|', $types), true)
? [Nette\Utils\Strings::length($value), 'characters']
: [strlen($value), 'bytes']);
if (!self::isInRange($length, $range)) {
$context->addError(
"The length of %label% %path% expects to be in range %expected%, %length% $label given.",
Message::LengthOutOfRange,
['value' => $value, 'length' => $length, 'expected' => implode('..', $range)],
);
}
} elseif ((is_int($value) || is_float($value)) && !self::isInRange($value, $range)) {
$context->addError(
'The %label% %path% expects to be in range %expected%, %value% given.',
Message::ValueOutOfRange,
['value' => $value, 'expected' => implode('..', $range)],
);
}
}
public static function isInRange(mixed $value, array $range): bool
{
return ($range[0] === null || $value >= $range[0])
&& ($range[1] === null || $value <= $range[1]);
}
public static function validatePattern(string $value, string $pattern, Context $context): void
{
if (!preg_match("\x01^(?:$pattern)$\x01Du", $value)) {
$context->addError(
"The %label% %path% expects to match pattern '%pattern%', %value% given.",
Message::PatternMismatch,
['value' => $value, 'pattern' => $pattern],
);
}
}
public static function getCastStrategy(string $type): \Closure
{
if (Nette\Utils\Reflection::isBuiltinType($type)) {
return static function ($value) use ($type) {
settype($value, $type);
return $value;
};
} elseif (method_exists($type, '__construct')) {
return static fn($value) => is_array($value) || $value instanceof \stdClass
? new $type(...(array) $value)
: new $type($value);
} else {
return static fn($value) => Nette\Utils\Arrays::toObject((array) $value, new $type);
}
}
}
@@ -1,98 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema;
use Nette;
final class Message
{
/** variables: {value: mixed, expected: string} */
public const TypeMismatch = 'schema.typeMismatch';
/** variables: {value: mixed, expected: string} */
public const ValueOutOfRange = 'schema.valueOutOfRange';
/** variables: {value: mixed, length: int, expected: string} */
public const LengthOutOfRange = 'schema.lengthOutOfRange';
/** variables: {value: string, pattern: string} */
public const PatternMismatch = 'schema.patternMismatch';
/** variables: {value: mixed, assertion: string} */
public const FailedAssertion = 'schema.failedAssertion';
/** no variables */
public const MissingItem = 'schema.missingItem';
/** variables: {hint: string} */
public const UnexpectedItem = 'schema.unexpectedItem';
/** no variables */
public const Deprecated = 'schema.deprecated';
/** @deprecated use Message::TypeMismatch */
public const TYPE_MISMATCH = self::TypeMismatch;
/** @deprecated use Message::ValueOutOfRange */
public const VALUE_OUT_OF_RANGE = self::ValueOutOfRange;
/** @deprecated use Message::LengthOutOfRange */
public const LENGTH_OUT_OF_RANGE = self::LengthOutOfRange;
/** @deprecated use Message::PatternMismatch */
public const PATTERN_MISMATCH = self::PatternMismatch;
/** @deprecated use Message::FailedAssertion */
public const FAILED_ASSERTION = self::FailedAssertion;
/** @deprecated use Message::MissingItem */
public const MISSING_ITEM = self::MissingItem;
/** @deprecated use Message::UnexpectedItem */
public const UNEXPECTED_ITEM = self::UnexpectedItem;
/** @deprecated use Message::Deprecated */
public const DEPRECATED = self::Deprecated;
public string $message;
public string $code;
/** @var string[] */
public array $path;
/** @var string[] */
public array $variables;
public function __construct(string $message, string $code, array $path, array $variables = [])
{
$this->message = $message;
$this->code = $code;
$this->path = $path;
$this->variables = $variables;
}
public function toString(): string
{
$vars = $this->variables;
$vars['label'] = empty($vars['isKey']) ? 'item' : 'key of item';
$vars['path'] = $this->path
? "'" . implode("\u{a0}\u{a0}", $this->path) . "'"
: null;
$vars['value'] = Helpers::formatValue($vars['value'] ?? null);
return preg_replace_callback('~( ?)%(\w+)%~', function ($m) use ($vars) {
[, $space, $key] = $m;
return $vars[$key] === null ? '' : $space . $vars[$key];
}, $this->message) ?? throw new Nette\InvalidStateException(preg_last_error_msg());
}
}
@@ -1,96 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema;
use Nette;
/**
* Schema validator.
*/
final class Processor
{
public array $onNewContext = [];
private Context $context;
private bool $skipDefaults = false;
public function skipDefaults(bool $value = true): void
{
$this->skipDefaults = $value;
}
/**
* Normalizes and validates data. Result is a clean completed data.
* @throws ValidationException
*/
public function process(Schema $schema, mixed $data): mixed
{
$this->createContext();
$data = $schema->normalize($data, $this->context);
$this->throwsErrors();
$data = $schema->complete($data, $this->context);
$this->throwsErrors();
return $data;
}
/**
* Normalizes and validates and merges multiple data. Result is a clean completed data.
* @throws ValidationException
*/
public function processMultiple(Schema $schema, array $dataset): mixed
{
$this->createContext();
$flatten = null;
$first = true;
foreach ($dataset as $data) {
$data = $schema->normalize($data, $this->context);
$this->throwsErrors();
$flatten = $first ? $data : $schema->merge($data, $flatten);
$first = false;
}
$data = $schema->complete($flatten, $this->context);
$this->throwsErrors();
return $data;
}
/**
* @return string[]
*/
public function getWarnings(): array
{
$res = [];
foreach ($this->context->warnings as $message) {
$res[] = $message->toString();
}
return $res;
}
private function throwsErrors(): void
{
if ($this->context->errors) {
throw new ValidationException(null, $this->context->errors);
}
}
private function createContext(): void
{
$this->context = new Context;
$this->context->skipDefaults = $this->skipDefaults;
Nette\Utils\Arrays::invoke($this->onNewContext, $this->context);
}
}
@@ -1,37 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema;
interface Schema
{
/**
* Normalization.
* @return mixed
*/
function normalize(mixed $value, Context $context);
/**
* Merging.
* @return mixed
*/
function merge(mixed $value, mixed $base);
/**
* Validation and finalization.
* @return mixed
*/
function complete(mixed $value, Context $context);
/**
* @return mixed
*/
function completeDefault(Context $context);
}
@@ -1,55 +0,0 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Schema;
use Nette;
/**
* Validation error.
*/
class ValidationException extends Nette\InvalidStateException
{
/** @var Message[] */
private array $messages;
/**
* @param Message[] $messages
*/
public function __construct(?string $message, array $messages = [])
{
parent::__construct($message ?: $messages[0]->toString());
$this->messages = $messages;
}
/**
* @return string[]
*/
public function getMessages(): array
{
$res = [];
foreach ($this->messages as $message) {
$res[] = $message->toString();
}
return $res;
}
/**
* @return Message[]
*/
public function getMessageObjects(): array
{
return $this->messages;
}
}