<pman>Intégration de la branche dev74

This commit is contained in:
Jephté Clain 2025-03-23 08:20:26 +04:00
commit 9d7dbd76d1
46 changed files with 1596 additions and 847 deletions

View File

@ -8,6 +8,18 @@ use nulib\cl;
* de {@link IAccess} * de {@link IAccess}
*/ */
abstract class AbstractAccess implements IAccess { abstract class AbstractAccess implements IAccess {
const ALLOW_EMPTY = true;
function __construct(?array $params=null) {
$this->allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY;
}
protected bool $allowEmpty;
function isAllowEmpty(): bool {
return $this->allowEmpty;
}
function inc(): int { function inc(): int {
$value = (int)$this->get(); $value = (int)$this->get();
$this->set(++$value); $this->set(++$value);
@ -33,4 +45,13 @@ abstract class AbstractAccess implements IAccess {
cl::set($array, $key, $value); cl::set($array, $key, $value);
$this->set($array); $this->set($array);
} }
function ensureAssoc(array $keys, ?array $params=null): void {
}
function ensureKeys(array $defaults, ?array $params=null): void {
}
function ensureOrder(array $keys, ?array $params=null): void {
}
} }

View File

@ -0,0 +1,182 @@
<?php
namespace nur\sery\wip\php\access;
use nulib\cl;
use nulib\StateException;
use ReflectionClass;
use ReflectionException;
class ChainAccess extends AbstractAccess {
const ACCESS_AUTO = 0, ACCESS_KEY = 1, ACCESS_PROPERTY = 2;
private static function unexpected_access_type(): StateException {
return StateException::unexpected_state("access_type");
}
function __construct(IAccess $access, $key, ?array $params=null) {
parent::__construct();
$this->access = $access;
$this->key = $key;
$this->accessType = $params["access_type"] ?? self::ACCESS_AUTO;
}
protected IAccess $access;
/** @var null|int|string|array */
protected $key;
protected int $accessType;
protected function _accessType($access, $key): int {
$accessType = $this->accessType;
if ($accessType === self::ACCESS_AUTO) {
if (is_object($access) && is_string($key)) {
$accessType = self::ACCESS_PROPERTY;
} else {
$accessType = self::ACCESS_KEY;
}
}
return $accessType;
}
protected function _has(): bool {
$src = $this->access->get();
$key = $this->key;
$accessType = $this->_accessType($src, $key);
if ($accessType === self::ACCESS_KEY) {
return cl::phas($src, $key);
} elseif ($accessType === self::ACCESS_PROPERTY) {
$class = new ReflectionClass($src);
return $class->hasProperty($key) || property_exists($src, $key);
} else {
throw self::unexpected_access_type();
}
}
protected function _get($default=null) {
$src = $this->access->get();
$key = $this->key;
$accessType = $this->_accessType($src, $key);
if ($accessType === self::ACCESS_KEY) {
return cl::pget($src, $key);
} elseif ($accessType === self::ACCESS_PROPERTY) {
$class = new ReflectionClass($src);
try {
$property = $class->getProperty($key);
$property->setAccessible(true);
} catch (ReflectionException $e) {
$property = null;
}
if ($property !== null) {
return $property->getValue($src);
} elseif (property_exists($src, $key)) {
return $src->$key;
} else {
return $default;
}
} else {
throw self::unexpected_access_type();
}
}
protected function _pset(object $dest, $name, $value): void {
$class = new ReflectionClass($dest);
try {
$property = $class->getProperty($name);
$property->setAccessible(true);
} catch (ReflectionException $e) {
$property = null;
}
if ($property !== null) {
$property->setValue($dest, $value);
} else {
$dest->$name = $value;
}
}
function isAllowEmpty(): bool {
return $this->access->isAllowEmpty();
}
function exists(): bool {
if (!$this->access->exists()) return false;
if ($this->key === null) return true;
return $this->_has();
}
function available(): bool {
if (!$this->access->available()) return false;
if ($this->key === null) return true;
if (!$this->_has()) return false;
return $this->isAllowEmpty() || $this->_get() !== "";
}
function get($default=null) {
if ($this->key === null) {
return $this->access->get($default);
}
return $this->_get($default);
}
function set($value): void {
if ($this->key === null) {
$this->access->set($value);
return;
}
$dest = $this->access->get();
$key = $this->key;
$accessType = $this->_accessType($dest, $key);
if ($accessType === self::ACCESS_KEY) {
cl::pset($dest, $key, $value);
$this->access->set($dest);
} elseif ($accessType === self::ACCESS_PROPERTY) {
$this->_pset($dest, $key, $value);
} else {
throw self::unexpected_access_type();
}
}
function del(): void {
if ($this->key === null) {
$this->access->del();
return;
}
$dest = $this->access->get();
$key = $this->key;
$accessType = $this->_accessType($dest, $key);
if ($accessType === self::ACCESS_KEY) {
cl::pdel($dest, $key);
$this->access->set($dest);
} elseif ($accessType === self::ACCESS_PROPERTY) {
$this->_pset($dest, $key, null);
} else {
throw self::unexpected_access_type();
}
}
function addKey($key, ?array $params=null): IAccess {
if ($key === null) return $this;
$accessType = $params["access_type"] ?? $this->accessType;
if ($accessType === self::ACCESS_KEY && $accessType === $this->accessType) {
if ($this->key !== null) $key = cl::merge($this->key, $key);
return new ChainAccess($this->access, $key);
} else {
return new ChainAccess($this, $key);
}
}
function ensureAssoc(array $keys, ?array $params=null): void {
#XXX fonction de $accessType?
#$this->access->ensureAssoc($keys, $params);
}
function ensureKeys(array $defaults, ?array $params=null): void {
#XXX fonction de $accessType?
#$this->access->ensureKeys($defaults, $params);
}
function ensureOrder(array $keys, ?array $params=null): void {
#XXX fonction de $accessType?
#$this->access->ensureOrder($keys, $params);
}
}

View File

@ -7,67 +7,99 @@ use nulib\cl;
* Class FormAccess: accès à une valeur de $_POST puis $_GET, dans cet ordre * Class FormAccess: accès à une valeur de $_POST puis $_GET, dans cet ordre
*/ */
class FormAccess extends AbstractAccess { class FormAccess extends AbstractAccess {
const ALLOW_EMPTY = false;
function __construct($key, ?array $params=null) { function __construct($key, ?array $params=null) {
parent::__construct($params);
$this->key = $key; $this->key = $key;
$this->allowEmpty = $params["allow_empty"] ?? false;
} }
/** @var int|string */ /** @var null|int|string|array */
protected $key; protected $key;
protected bool $allowEmpty; protected function _exists(array $first, ?array $second=null): bool {
function exists(): bool {
$key = $this->key; $key = $this->key;
if ($key === null) return false; if ($key === null) return true;
return array_key_exists($key, $_POST) || array_key_exists($key, $_GET); if (cl::phas($first, $key)) return true;
return $second !== null && cl::phas($second, $key);
} }
public function available(): bool { function exists(): bool {
return $this->_exists($_POST, $_GET);
}
protected function _available(array $first, ?array $second=null): bool {
$key = $this->key; $key = $this->key;
if ($key === null) return false; if ($key === null) return true;
if (array_key_exists($key, $_POST)) { if (cl::phas($first, $key)) {
return $this->allowEmpty || $_POST[$key] !== ""; return $this->allowEmpty || cl::pget($first, $key) !== "";
} elseif (array_key_exists($key, $_GET)) { } elseif ($second !== null && cl::phas($second, $key)) {
return $this->allowEmpty || $_GET[$key] !== ""; return $this->allowEmpty || cl::pget($second, $key) !== "";
} else { } else {
return false; return false;
} }
} }
function get($default=null) { public function available(): bool {
return $this->_available($_POST, $_GET);
}
protected function _get($default, array $first, ?array $second=null) {
$key = $this->key; $key = $this->key;
if ($key === null) return $default; if ($key === null) return cl::merge($first, $second);
if (array_key_exists($key, $_POST)) { if (cl::phas($first, $key)) {
$value = $_POST[$key]; $value = cl::pget($first, $key);
if ($value === "" && !$this->allowEmpty) return $default; if ($value !== "" || $this->allowEmpty) return $value;
return $value; } elseif ($second !== null && cl::phas($second, $key)) {
} elseif (array_key_exists($key, $_GET)) { $value = cl::pget($second, $key);
$value = $_GET[$key]; if ($value !== "" || $this->allowEmpty) return $value;
if ($value === "" && !$this->allowEmpty) return $default; }
return $value; return $default;
}
function get($default=null) {
return $this->_get($default, $_POST, $_GET);
}
function _set($value, array &$first, ?array &$second=null): void {
$key = $this->key;
if ($key === null) {
# interdire la modification de la destination
return;
}
if ($second !== null && !cl::phas($first, $key) && cl::phas($second, $key)) {
cl::pset($second, $key, $value);
} else { } else {
return $default; cl::pset($first, $key, $value);
} }
} }
function set($value): void { function set($value): void {
$this->_set($value, $_POST, $_GET);
}
function _del(array &$first, ?array &$second=null): void {
$key = $this->key; $key = $this->key;
if ($key === null) return; if ($key === null) {
if (!array_key_exists($key, $_POST) && array_key_exists($key, $_GET)) { # interdire la modification de la destination
cl::set($_GET, $key, $value); return;
}
if ($second !== null && !cl::phas($first, $key) && cl::phas($second, $key)) {
cl::pdel($second, $key);
} else { } else {
cl::set($_POST, $key, $value); cl::pdel($first, $key);
} }
} }
function del(): void { function del(): void {
$key = $this->key; $this->_del($_POST, $_GET);
if ($key === null) return; }
if (!array_key_exists($key, $_POST) && array_key_exists($key, $_GET)) {
cl::del($_GET, $key); function addKey($key): self {
} else { if ($key === null) return $this;
cl::del($_POST, $key); if ($this->key !== null) $key = cl::merge($this->key, $key);
} return new static($key, [
"allow_empty" => $this->allowEmpty
]);
} }
} }

View File

@ -8,42 +8,22 @@ use nulib\cl;
*/ */
class GetAccess extends FormAccess { class GetAccess extends FormAccess {
function exists(): bool { function exists(): bool {
$key = $this->key; return $this->_exists($_GET);
if ($key === null) return false;
return array_key_exists($key, $_GET);
} }
public function available(): bool { public function available(): bool {
$key = $this->key; return $this->_available($_GET);
if ($key === null) return false;
if (array_key_exists($key, $_GET)) {
return $this->allowEmpty || $_GET[$key] !== "";
} else {
return false;
}
} }
function get($default=null) { function get($default=null) {
$key = $this->key; return $this->_get($default, $_GET);
if ($key === null) return $default;
if (array_key_exists($key, $_GET)) {
$value = $_GET[$key];
if ($value === "" && !$this->allowEmpty) return $default;
return $value;
} else {
return $default;
}
} }
function set($value): void { function set($value): void {
$key = $this->key; $this->_set($value, $_GET);
if ($key === null) return;
cl::set($_GET, $key, $value);
} }
function del(): void { function del(): void {
$key = $this->key; $this->_del($_GET);
if ($key === null) return;
cl::del($_GET, $key);
} }
} }

View File

@ -1,6 +1,8 @@
<?php <?php
namespace nur\sery\wip\php\access; namespace nur\sery\wip\php\access;
use ReflectionClass;
/** /**
* Interface IAccess: abstraction d'un accès complet à une valeur * Interface IAccess: abstraction d'un accès complet à une valeur
*/ */
@ -25,4 +27,26 @@ interface IAccess extends IGetter, ISetter, IDeleter {
* tableau si $key===null * tableau si $key===null
*/ */
function append($value, $key=null): void; function append($value, $key=null): void;
/** retourner une instance permettant d'accéder à $value[$key] */
function addKey($key): IAccess;
/**
* s'assurer que la destination est un tableau associatif en remplaçant les
* clés numériques par les clés correspondantes du tableau $keys
*/
function ensureAssoc(array $keys, ?array $params=null): void;
/**
* s'assurer que toutes les clés mentionnées dans le tableau $defaults
* existent. si elles n'existent pas, leur donner la valeur du tableau
* $defaults
*/
function ensureKeys(array $defaults, ?array $params=null): void;
/**
* s'assure que les clés de la destination sont dans l'ordre mentionné dans le
* tableau $keys. toutes les clés supplémentaires sont placées à la fin
*/
function ensureOrder(array $keys, ?array $params=null): void;
} }

View File

@ -11,6 +11,12 @@ interface IGetter {
*/ */
function exists(): bool; function exists(): bool;
/**
* @return bool true si cet objet autorise les chaines vides. si c'est le cas,
* {@link exists()} et {@link available()} sont fonctionnellement identiques
*/
function isAllowEmpty(): bool;
/** @return bool true si la valeur existe et est utilisable, false sinon */ /** @return bool true si la valeur existe et est utilisable, false sinon */
function available(): bool; function available(): bool;

View File

@ -3,67 +3,186 @@ namespace nur\sery\wip\php\access;
use ArrayAccess; use ArrayAccess;
use nulib\cl; use nulib\cl;
use nulib\ref\schema\ref_schema;
/** /**
* Class KeyAccess: accès à une valeur d'une clé dans un tableau * Class KeyAccess: accès
* - soit à une valeur d'un chemin de clé dans un tableau (si $key !== null)
* - soit à une valeur scalaire (si $key === null)
*/ */
class KeyAccess extends AbstractAccess { class KeyAccess extends AbstractAccess {
function __construct(&$array, $key, ?array $params=null) { const ALLOW_NULL = null;
$this->array =& $array; const ALLOW_FALSE = null;
const PROTECT_DEST = false;
function __construct(&$dest, $key=null, ?array $params=null) {
parent::__construct($params);
$this->protectDest = $params["protect_dest"] ?? static::PROTECT_DEST;
$this->dest =& $dest;
$this->key = $key; $this->key = $key;
$this->allowNull = $params["allow_null"] ?? true; $this->allowNull = $params["allow_null"] ?? static::ALLOW_NULL;
$this->allowFalse = $params["allow_false"] ?? false; $this->allowFalse = $params["allow_false"] ?? static::ALLOW_FALSE;
$this->allowEmpty = $params["allow_empty"] ?? true;
} }
/** @var array|ArrayAccess */ protected bool $protectDest;
protected $array;
function reset(&$array): self { /** @var mixed|array|ArrayAccess */
$this->array =& $array; protected $dest;
/** @var null|int|string|array */
protected $key;
function reset(&$dest, $key=null): self {
$this->dest =& $dest;
$this->key = $key;
return $this; return $this;
} }
/** @var int|string */ function resetKey($key=null): self {
protected $key; $this->key = $key;
return $this;
}
protected bool $allowNull; protected ?bool $allowNull;
protected bool $allowFalse; protected function isAllowNull(): bool {
$allowNull = $this->allowNull;
if ($allowNull !== null) return $allowNull;
return $this->key !== null;
}
protected bool $allowEmpty; protected ?bool $allowFalse;
protected function isAllowFalse(): bool {
$allowFalse = $this->allowFalse;
if ($allowFalse !== null) return $allowFalse;
return $this->key === null;
}
function exists(): bool { function exists(): bool {
$key = $this->key; $key = $this->key;
if ($key === null) return false; if ($key === null) {
return cl::has($this->array, $key); return $this->isAllowNull() || $this->dest !== null;
} else {
return cl::phas($this->dest, $key);
}
} }
function available(): bool { function available(): bool {
if (!$this->exists()) return false; if (!$this->exists()) return false;
$value = cl::get($this->array, $this->key); $key = $this->key;
if ($value === null) return $this->allowNull; if ($key === null) $value = $this->dest;
if ($value === false) return $this->allowFalse; else $value = cl::pget($this->dest, $key);
if ($value === "") return $this->allowEmpty; if ($value === "") return $this->allowEmpty;
if ($value === null) return $this->isAllowNull();
if ($value === false) return $this->isAllowFalse();
return true; return true;
} }
function get($default=null) { function get($default=null) {
if ($this->key === null) return $default; $key = $this->key;
$value = cl::get($this->array, $this->key, $default); if ($key === null) $value = $this->dest;
if ($value === null && !$this->allowNull) return $default; else $value = cl::pget($this->dest, $key, $default);
if ($value === false && !$this->allowFalse) return $default;
if ($value === "" && !$this->allowEmpty) return $default; if ($value === "" && !$this->allowEmpty) return $default;
if ($value === null && !$this->isAllowNull()) return $default;
if ($value === false && !$this->isAllowFalse()) return $default;
return $value; return $value;
} }
function set($value): void { function set($value): void {
if ($this->key === null) return; $key = $this->key;
cl::set($this->array, $this->key, $value); if ($key === null) {
if (!$this->protectDest) $this->dest = $value;
} else {
cl::pset($this->dest, $key, $value);
}
} }
function del(): void { function del(): void {
if ($this->key === null) return; $key = $this->key;
cl::del($this->array, $this->key); if ($key === null) {
if (!$this->protectDest) $this->dest = null;
} else {
cl::pdel($this->dest, $key);
}
}
function addKey($key): self {
if ($key === null) return $this;
if ($this->key !== null) $key = cl::merge($this->key, $key);
return new KeyAccess($this->dest, $key, [
"allow_empty" => $this->allowEmpty,
"allow_null" => $this->allowNull,
"allow_false" => $this->allowFalse,
"protect_dest" => $this->protectDest,
]);
}
function ensureAssoc(array $keys, ?array $params=null): void {
$dest =& $this->dest;
$prefix = $params["key_prefix"] ?? null;
$suffix = $params["key_suffix"] ?? null;
$index = 0;
foreach ($keys as $key) {
if ($prefix !== null || $suffix !== null) {
$destKey = "$prefix$key$suffix";
} else {
# préserver les clés numériques
$destKey = $key;
}
if ($dest !== null && array_key_exists($destKey, $dest)) continue;
while (in_array($index, $keys, true)) {
$index++;
}
if ($dest !== null && array_key_exists($index, $dest)) {
$dest[$destKey] = $dest[$index];
unset($dest[$index]);
$index++;
}
}
}
function ensureKeys(array $defaults, ?array $params=null): void {
$dest =& $this->dest;
$keys = array_keys($defaults);
$prefix = $params["key_prefix"] ?? null;
$suffix = $params["key_suffix"] ?? null;
foreach ($keys as $key) {
$destKey = "$prefix$key$suffix";
if ($dest === null || !array_key_exists($destKey, $dest)) {
$dest[$destKey] = $defaults[$key];
}
}
}
function ensureOrder(array $keys, ?array $params=null): void {
$dest =& $this->dest;
if ($dest === null) return;
$prefix = $params["key_prefix"] ?? null;
$suffix = $params["key_suffix"] ?? null;
if ($prefix !== null || $suffix !== null) {
foreach ($keys as &$key) {
$key = "$prefix$key$suffix";
}; unset($key);
}
$destKeys = array_keys($dest);
$keyCount = count($keys);
if (array_slice($destKeys, 0, $keyCount) === $keys) {
# si le tableau a déjà les bonnes clés dans le bon ordre, rien à faire
return;
}
$ordered = [];
foreach ($keys as $key) {
if (array_key_exists($key, $dest)) {
$ordered[$key] = $dest[$key];
unset($dest[$key]);
}
}
$preserveKeys = $params["preserve_keys"] ?? false;
if ($preserveKeys) $dest = cl::merge2($ordered, $dest);
else $dest = array_merge($ordered, $dest);
} }
} }

View File

@ -8,42 +8,22 @@ use nulib\cl;
*/ */
class PostAccess extends FormAccess { class PostAccess extends FormAccess {
function exists(): bool { function exists(): bool {
$key = $this->key; return $this->_exists($_POST);
if ($key === null) return false;
return array_key_exists($key, $_POST);
} }
public function available(): bool { public function available(): bool {
$key = $this->key; return $this->_available($_POST);
if ($key === null) return false;
if (array_key_exists($key, $_POST)) {
return $this->allowEmpty || $_POST[$key] !== "";
} else {
return false;
}
} }
function get($default=null) { function get($default=null) {
$key = $this->key; return $this->_get($default, $_POST);
if ($key === null) return $default;
if (array_key_exists($key, $_POST)) {
$value = $_POST[$key];
if ($value === "" && !$this->allowEmpty) return $default;
return $value;
} else {
return $default;
}
} }
function set($value): void { function set($value): void {
$key = $this->key; $this->_set($value, $_POST);
if ($key === null) return;
cl::set($_POST, $key, $value);
} }
function del(): void { function del(): void {
$key = $this->key; $this->_del($_POST);
if ($key === null) return;
cl::del($_POST, $key);
} }
} }

View File

@ -0,0 +1,175 @@
<?php
namespace nur\sery\wip\php\access;
use nulib\StateException;
use nulib\str;
use ReflectionClass;
use ReflectionException;
use ReflectionNamedType;
use ReflectionProperty;
use stdClass;
class PropertyAccess extends AbstractAccess {
const PROTECT_DEST = true;
const MAP_NAMES = true;
const ALLOW_NULL = true;
const ALLOW_FALSE = false;
function __construct(?object $dest, ?string $name=null, ?array $params=null) {
parent::__construct($params);
$this->protectDest = $params["protect_dest"] ?? static::PROTECT_DEST;
$this->mapNames = $params["map_names"] ?? static::MAP_NAMES;
$this->_setName($name);
$this->_setDest($dest);
$this->allowNull = $params["allow_null"] ?? static::ALLOW_NULL;
$this->allowFalse = $params["allow_false"] ?? static::ALLOW_FALSE;
}
protected bool $protectDest;
protected ?object $dest;
protected bool $mapNames;
protected ?string $name;
protected ?ReflectionProperty $property;
private function _getName(string $key): string {
return $this->mapNames? str::us2camel($key): $key;
}
private function _setName(?string $name): void {
if ($name !== null) $name = $this->_getName($name);
$this->name = $name;
}
private function _getProperty(?string $name, ?ReflectionClass $class, ?object $object=null): ?ReflectionProperty {
$property = null;
if ($class === null && $object !== null) {
$class = new ReflectionClass($object);
}
if ($class !== null && $name !== null) {
try {
$property = $class->getProperty($name);
$property->setAccessible(true);
} catch (ReflectionException $e) {
}
}
return $property;
}
private function _setDest(?object $dest): void {
$this->dest = $dest;
$this->property = $this->_getProperty($this->name, null, $dest);
}
function reset(?object $dest, ?string $name=null): self {
$this->_setName($name);
$this->_setDest($dest);
return $this;
}
function resetKey($name=null): self {
$this->_setName($name);
return $this;
}
protected bool $allowNull;
protected bool $allowFalse;
function exists(): bool {
$name = $this->name;
if ($this->dest === null) return false;
return $name === null
|| $this->property !== null
|| property_exists($this->dest, $name);
}
protected function _get($default=null) {
$name = $this->name;
$property = $this->property;
if ($this->dest === null) {
return $default;
} elseif ($name === null) {
return $this->dest;
} elseif ($property !== null) {
return $property->getValue($this->dest);
} elseif (property_exists($this->dest, $name)) {
return $this->dest->$name;
} else {
return $default;
}
}
function available(): bool {
if (!$this->exists()) return false;
$value = $this->_get();
if ($value === "") return $this->allowEmpty;
if ($value === null) return $this->allowNull;
if ($value === false) return $this->allowFalse;
return true;
}
function get($default=null) {
if (!$this->exists()) return $default;
$value = $this->_get();
if ($value === "" && !$this->allowEmpty) return $default;
if ($value === null && !$this->allowNull) return $default;
if ($value === false && !$this->allowFalse) return $default;
return $value;
}
protected function _set($value): void {
$name = $this->name;
$property = $this->property;
if ($this->dest === null) {
throw StateException::unexpected_state("dest is null");
} elseif ($name === null) {
if (!$this->protectDest) $this->_setDest($value);
} elseif ($property !== null) {
$property->setValue($this->dest, $value);
} else {
$this->dest->$name = $value;
}
}
function set($value): void {
$this->_set($value);
}
function del(): void {
$this->_set(null);
}
function addKey($key): IAccess {
if ($key === null) return $this;
return new ChainAccess($this, $key);
}
function ensureKeys(array $defaults, ?array $params=null): void {
$dest = $this->dest;
if ($dest === null) {
# comme ne connait pas la classe de l'objet destination, on n'essaie pas
# de le créer
return;
}
$class = new ReflectionClass($dest);
$keys = array_keys($defaults);
$prefix = $params["key_prefix"] ?? null;
$suffix = $params["key_suffix"] ?? null;
foreach ($keys as $key) {
$name = $this->_getName("$prefix$key$suffix");
$property = $this->_getProperty($name, $class);
if ($property !== null) {
$type = $property->getType();
if ($type !== null && !$property->isInitialized($dest) && $type->allowsNull()) {
# initialiser avec null au lieu de $defaults[$key] pour respecter le
# type de la propriété
$property->setValue($dest, null);
}
} elseif (!property_exists($dest, $name)) {
$dest->$name = $defaults[$key];
}
}
}
}

View File

@ -16,6 +16,7 @@ namespace nur\sery\wip\php\access;
*/ */
class ShadowAccess extends AbstractAccess { class ShadowAccess extends AbstractAccess {
function __construct(IAccess $reader, IAccess $writer) { function __construct(IAccess $reader, IAccess $writer) {
parent::__construct();
$this->reader = $reader; $this->reader = $reader;
$this->writer = $writer; $this->writer = $writer;
$this->getter = $reader; $this->getter = $reader;
@ -27,6 +28,10 @@ class ShadowAccess extends AbstractAccess {
protected IGetter $getter; protected IGetter $getter;
public function isAllowEmpty(): bool {
return $this->getter->isAllowEmpty();
}
function exists(): bool { function exists(): bool {
return $this->getter->exists(); return $this->getter->exists();
} }
@ -48,4 +53,20 @@ class ShadowAccess extends AbstractAccess {
$this->writer->del(); $this->writer->del();
$this->getter = $this->reader; $this->getter = $this->reader;
} }
function addKey($key): IAccess {
return new ChainAccess($this, $key);
}
function ensureAssoc(array $keys, ?array $params=null): void {
$this->writer->ensureAssoc($keys, $params);
}
function ensureKeys(array $defaults, ?array $params=null): void {
$this->writer->ensureKeys($defaults, $params);
}
function ensureOrder(array $keys, ?array $params=null): void {
$this->writer->ensureOrder($keys, $params);
}
} }

View File

@ -1,56 +0,0 @@
<?php
namespace nur\sery\wip\php\access;
/**
* Class ValueAccess: accès à une valeur unitaire
*/
class ValueAccess extends AbstractAccess {
function __construct(&$value, ?array $params=null) {
$this->value =& $value;
$this->allowNull = $params["allow_null"] ?? false;
$this->allowFalse = $params["allow_false"] ?? true;
$this->allowEmpty = $params["allow_empty"] ?? true;
}
/** @var mixed */
protected $value;
function reset(&$value): self {
$this->value =& $value;
return $this;
}
protected bool $allowNull;
protected bool $allowFalse;
protected bool $allowEmpty;
function exists(): bool {
return $this->allowNull || $this->value !== null;
}
function available(): bool {
if (!$this->exists()) return false;
$value = $this->value;
if ($value === false) return $this->allowFalse;
if ($value === "") return $this->allowEmpty;
return true;
}
function get($default=null) {
$value = $this->value;
if ($value === null && !$this->allowNull) return $default;
if ($value === false && !$this->allowFalse) return $default;
if ($value === "" && !$this->allowEmpty) return $default;
return $value;
}
function set($value): void {
$this->value = $value;
}
function del(): void {
$this->value = null;
}
}

View File

@ -2,9 +2,7 @@
namespace nur\sery\wip\schema; namespace nur\sery\wip\schema;
use IteratorAggregate; use IteratorAggregate;
use nur\sery\wip\schema\_assoc\AssocResult; use Throwable;
use nur\sery\wip\schema\_list\ListResult;
use nur\sery\wip\schema\_scalar\ScalarResult;
/** /**
* Class Result: résultat de l'analyse ou de la normalisation d'une valeur * Class Result: résultat de l'analyse ou de la normalisation d'une valeur
@ -17,6 +15,8 @@ use nur\sery\wip\schema\_scalar\ScalarResult;
* @property bool $normalized si la valeur est valide, est-elle normalisée? * @property bool $normalized si la valeur est valide, est-elle normalisée?
* @property string|null $messageKey clé de message si la valeur n'est pas valide * @property string|null $messageKey clé de message si la valeur n'est pas valide
* @property string|null $message message si la valeur n'est pas valide * @property string|null $message message si la valeur n'est pas valide
* @property Throwable|null $exception l'exception qui a fait échouer la
* validation le cas échéant
* @property string|null $origValue valeur originale avant extraction et analyse * @property string|null $origValue valeur originale avant extraction et analyse
* @property mixed|null $normalizedValue la valeur normalisée si elle est * @property mixed|null $normalizedValue la valeur normalisée si elle est
* disponible, null sinon. ce champ est utilisé comme optimisation si la valeur * disponible, null sinon. ce champ est utilisé comme optimisation si la valeur
@ -26,7 +26,7 @@ abstract class Result implements IteratorAggregate {
const KEYS = [ const KEYS = [
"resultAvailable", "resultAvailable",
"present", "available", "null", "valid", "normalized", "present", "available", "null", "valid", "normalized",
"messageKey", "message", "messageKey", "message", "exception",
"origValue", "normalizedValue", "origValue", "normalizedValue",
]; ];
@ -34,10 +34,6 @@ abstract class Result implements IteratorAggregate {
$this->reset(); $this->reset();
} }
function isAssoc(?AssocResult &$result=null): bool { return false; }
function isList(?ListResult &$result=null): bool { return false; }
function isScalar(?ScalarResult &$result=null): bool { return false; }
/** /**
* Obtenir la liste des clés valides pour les valeurs accessibles via cet * Obtenir la liste des clés valides pour les valeurs accessibles via cet
* objet * objet

View File

@ -17,6 +17,29 @@ use nur\sery\wip\schema\types\tcontent;
use nur\sery\wip\schema\types\tpkey; use nur\sery\wip\schema\types\tpkey;
use nur\sery\wip\schema\types\trawstring; use nur\sery\wip\schema\types\trawstring;
/**
* Class Schema
*
* @property-read array|IType $type
* @property-read mixed $default
* @property-read string|null $title
* @property-read bool $required
* @property-read bool $nullable
* @property-read string|array|null $desc
* @property-read callable|null $analyzerFunc
* @property-read callable|null $extractorFunc
* @property-read callable|null $parserFunc
* @property-read callable|null $normalizerFunc
* @property-read array|null $messages
* @property-read callable|null $formatterFunc
* @property-read mixed $format
* @property-read array $nature
* @property-read array|null $schema
* @property-read string|int|null $name
* @property-read string|array|null $pkey
* @property-read string|null $header
* @property-read bool|null $computed
*/
abstract class Schema implements ArrayAccess { abstract class Schema implements ArrayAccess {
/** /**
* créer le cas échéant une nouvelle instance de {@link Schema} à partir d'une * créer le cas échéant une nouvelle instance de {@link Schema} à partir d'une
@ -57,7 +80,7 @@ abstract class Schema implements ArrayAccess {
# schéma d'un scalaire quelconque), on ne l'autorise pas ici # schéma d'un scalaire quelconque), on ne l'autorise pas ici
throw SchemaException::invalid_schema("definition is required"); throw SchemaException::invalid_schema("definition is required");
} }
return self::ns($schema, $definition)->getWrapper($value, $valueKey, $wrapper); return self::ns($schema, $definition)->getWrapper($value, $valueKey, null, $wrapper);
} }
protected static function have_nature(array $definition, ?string &$nature=null): bool { protected static function have_nature(array $definition, ?string &$nature=null): bool {
@ -75,18 +98,18 @@ abstract class Schema implements ArrayAccess {
return false; return false;
} }
protected static function _normalize(&$definition, $definitionKey=null): void { protected static function _normalize_definition(&$definition, $definitionKey=null, ?array $natureMetaschema=null): void {
if (!is_array($definition)) $definition = [$definition]; if (!is_array($definition)) $definition = [$definition];
# s'assurer que toutes les clés existent avec leur valeur par défaut # s'assurer que toutes les clés existent avec leur valeur par défaut
$index = 0; $index = 0;
foreach (array_keys(ref_schema::SCALAR_METASCHEMA) as $key) { foreach (array_keys(ref_schema::VALUE_METASCHEMA) as $key) {
if (!array_key_exists($key, $definition)) { if (!array_key_exists($key, $definition)) {
if (array_key_exists($index, $definition)) { if (array_key_exists($index, $definition)) {
$definition[$key] = $definition[$index]; $definition[$key] = $definition[$index];
unset($definition[$index]); unset($definition[$index]);
$index++; $index++;
} else { } else {
$definition[$key] = ref_schema::SCALAR_METASCHEMA[$key][1]; $definition[$key] = ref_schema::VALUE_METASCHEMA[$key][1];
} }
} }
} }
@ -138,6 +161,12 @@ abstract class Schema implements ArrayAccess {
# nature # nature
$nature = $definition[""]; $nature = $definition[""];
tarray::ensure_array($nature); tarray::ensure_array($nature);
$natureMetaschema ??= ref_schema::NATURE_METASCHEMA;
foreach (array_keys($natureMetaschema) as $key) {
if (!array_key_exists($key, $nature)) {
$nature[$key] = $natureMetaschema[$key][1];
}
}
$definition[""] = $nature; $definition[""] = $nature;
# name, pkey, header # name, pkey, header
$name = $definition["name"]; $name = $definition["name"];
@ -169,11 +198,11 @@ abstract class Schema implements ArrayAccess {
switch ($nature[0] ?? null) { switch ($nature[0] ?? null) {
case "assoc": case "assoc":
foreach ($definition["schema"] as $key => &$keydef) { foreach ($definition["schema"] as $key => &$keydef) {
self::_normalize($keydef, $key); self::_normalize_definition($keydef, $key);
}; unset($keydef); }; unset($keydef);
break; break;
case "list": case "list":
self::_normalize($definition["schema"]); self::_normalize_definition($definition["schema"]);
break; break;
} }
} }
@ -246,6 +275,13 @@ abstract class Schema implements ArrayAccess {
return $this->_definition; return $this->_definition;
} }
/**
* retourner la liste des clés valides pour l'accès aux valeurs et résultats
*/
abstract function getKeys(): array;
abstract function getSchema($key): Schema;
/** retourner true si le schéma est de nature tableau associatif */ /** retourner true si le schéma est de nature tableau associatif */
function isAssoc(?AssocSchema &$schema=null): bool { return false; } function isAssoc(?AssocSchema &$schema=null): bool { return false; }
/** retourner true si le schéma est de nature liste */ /** retourner true si le schéma est de nature liste */
@ -253,7 +289,9 @@ abstract class Schema implements ArrayAccess {
/** retourner true si le schéma est de nature scalaire */ /** retourner true si le schéma est de nature scalaire */
function isScalar(?ScalarSchema &$schema=null): bool { return false; } function isScalar(?ScalarSchema &$schema=null): bool { return false; }
abstract function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): Wrapper; abstract protected function newWrapper(): Wrapper;
abstract function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): Wrapper;
############################################################################# #############################################################################
# key & properties # key & properties
@ -272,7 +310,15 @@ abstract class Schema implements ArrayAccess {
throw AccessException::read_only(null, $offset); throw AccessException::read_only(null, $offset);
} }
const _PROPERTY_PKEYS = []; const _PROPERTY_PKEYS = [
"analyzerFunc" => "analyzer_func",
"extractorFunc" => "extractor_func",
"parserFunc" => "parser_func",
"normalizerFunc" => "normalizer_func",
"formatterFunc" => "formatter_func",
"nature" => ["", 0],
];
function __get($name) { function __get($name) {
$pkey = cl::get(static::_PROPERTY_PKEYS, $name, $name); $pkey = cl::get(static::_PROPERTY_PKEYS, $name, $name);
return cl::pget($this->definition, $pkey); return cl::pget($this->definition, $pkey);

View File

@ -1,20 +1,32 @@
# nulib\schema # nulib\schema
* instance de WrapperContext directement dans le schéma * ensureKeys() et orderKeys() se fait au niveau de access (ou input?)
* plus de {key} ni {orig} dans messages * access/input ne pouvant pas connaître les valeurs appropriées, c'est le
* les messages standard ne sont utilisés que s'il n'y a pas de message dans schéma qui les génère. ensureKeys($values)
l'exception * méthode ensureAssoc() transforme les clés séquentielles en clés associatives
* si instance de UserException, prendre le message "non technique" pour * l'ordre est `ensureAssoc [--> ensureKeys] [--> orderKeys]`
résultat
* rajouter l'attribut "size" pour spécifier la taille maximale des valeurs
* cela pourrait servir pour générer automatiquement des tables SQL
* ou pour modéliser un schéma FSV
* valeurs composite/computed * valeurs composite/computed
* analyse / vérification de la valeur complète après calcul du résultat, si * analyse / vérification de la valeur complète après calcul du résultat, si
tous les résultats sont bons tous les résultats sont bons
* calcul des valeurs composites/computed par une fonction avant/après l'analyse * calcul des valeurs composites/computed par une fonction avant/après l'analyse
globale si résultat ok globale si résultat ok
* fonction getter_func, setter_func, deleter_func pour les propriétés de type
computed
* tdate et tdatetime. qu'en est-il des autres classes (delay, etc.) * tdate et tdatetime. qu'en est-il des autres classes (delay, etc.)
* possibilité de spécifier le format de la date à analyser * possibilité de spécifier le format de la date à analyser
* ScalarSchema::from_property() * ScalarSchema::from_property()
* l'argument $format de AssocWrapper::format() est un tableau associatif
`[$key => $format]`
cela permet de spécifier des format spécifiques pour certains champs.
* cela signifie que la valeur de retour n'est pas string :-(
retourner string|array
* dans AssocSchema, support `[key_prefix]` qui permet de spécifier un préfixe * dans AssocSchema, support `[key_prefix]` qui permet de spécifier un préfixe
commun aux champs dans le tableau destination, e.g commun aux champs dans le tableau destination, e.g
~~~php ~~~php
@ -58,6 +70,8 @@
la définition de ces "circonstances" est encore à faire: soit un paramètre la définition de ces "circonstances" est encore à faire: soit un paramètre
lors de la définition du schéma, soit un truc magique du genre "toutes les lors de la définition du schéma, soit un truc magique du genre "toutes les
valeurs séquentielles sont des clés du schéma" valeurs séquentielles sont des clés du schéma", soit un mode automatique
activé par un paramètre où une valeur "val" devient "val"=>true si la clé
"val" existe dans le schéma
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

View File

@ -3,9 +3,12 @@ namespace nur\sery\wip\schema;
use ArrayAccess; use ArrayAccess;
use IteratorAggregate; use IteratorAggregate;
use nulib\php\func;
use nur\sery\wip\schema\_assoc\AssocWrapper; use nur\sery\wip\schema\_assoc\AssocWrapper;
use nur\sery\wip\schema\_list\ListWrapper; use nur\sery\wip\schema\_list\ListWrapper;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarWrapper; use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\types\IType; use nur\sery\wip\schema\types\IType;
abstract class Wrapper implements ArrayAccess, IteratorAggregate { abstract class Wrapper implements ArrayAccess, IteratorAggregate {
@ -13,8 +16,92 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate {
function isList(?ListWrapper &$wrapper=null): bool { return false; } function isList(?ListWrapper &$wrapper=null): bool { return false; }
function isScalar(?ScalarWrapper &$wrapper=null): bool { return false; } function isScalar(?ScalarWrapper &$wrapper=null): bool { return false; }
/** spécifier la valeur destination gérée par cet objet */ protected WrapperContext $context;
abstract function reset(&$value, $valueKey=null, ?bool $verifix=null): self;
/** changer les paramètres de gestion des valeurs */
function resetParams(?array $params): void {
$this->context->resetParams($params);
}
protected function resetContext($resetSelectedKey): void {
$context = $this->context;
$context->type = null;
$context->result->reset();
$context->analyzed = false;
$context->normalized = false;
}
protected function afterModify(?array $params, $resetSelectedKey=false): void {
$context = $this->context;
$this->resetContext($resetSelectedKey);
if ($params["analyze"] ?? $context->analyze) {
$this->analyze($params);
}
if ($context->analyzed) {
if ($params["normalize"] ?? $context->normalize) {
$this->normalize($params);
}
}
}
protected function newInput(&$value): Input {
return new Input($value);
}
/**
* spécifier la valeur destination gérée par cet objet.
*
* @param ?array $params paramètres spécifique à cet appel, qui peuvent être
* différent des paramètres par défaut
*/
function reset(&$value, $valueKey=null, ?array $params=null): Wrapper {
$context = $this->context;
if ($value instanceof Input) $input = $value;
else $input = $this->newInput($value);
$context->input = $input;
$context->valueKey = $valueKey;
$this->afterModify($params, true);
return $this;
}
/** analyser la valeur */
abstract static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int;
function analyze(?array $params=null): bool {
$context = $this->context;
$reanalyze = $params["reanalyze"] ?? false;
if ($context->analyzed && !$reanalyze) return false;
static::_analyze($context, $this, $params);
$context->analyzed = true;
return true;
}
/** normaliser la valeur */
abstract static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool;
function normalize(?array $params=null): bool {
$context = $this->context;
// il faut que la valeur soit analysée avant de la normaliser
static::analyze($params);
if (!$context->analyzed) return false;
$renormalize = $params["renormalize"] ?? false;
if ($renormalize || !$context->normalized) {
$modified = static::_normalize($context, $this, $params);
$context->normalized = true;
} else {
$modified = false;
}
/** @var ScalarResult $result */
$result = $context->result;
if (!$result->valid) {
$result->throw($params["throw"] ?? $context->throw);
}
return $modified;
}
/** /**
* Obtenir la liste des clés valides pour les valeurs accessibles via cet * Obtenir la liste des clés valides pour les valeurs accessibles via cet
@ -38,37 +125,80 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate {
} }
/** /**
* obtenir le résultat de l'appel d'une des fonctions {@link set()} ou * obtenir le résultat de l'analyse de la valeur du wrapper sélectionné
* {@link unset()} *
* cette fonction doit être appelée après {@link set()} ou {@link unset()} et
* après que le wrapper aie été sélectionné avec {@link select()}
*/ */
abstract function getResult(): Result; function getResult($key=false): Result {
return $this->context->result;
}
/** retourner true si la valeur existe */ /** retourner true si la valeur existe */
abstract function isPresent(): bool; function isPresent($key=false): bool {
return $this->getResult($key)->present;
}
/** retourner le type associé à la valeur */ /** retourner le type associé à la valeur */
abstract function getType(): IType; function getType($key=false): IType {
return $this->context->type;
}
/** retourner true si la valeur est disponible */ /** retourner true si la valeur est disponible */
abstract function isAvailable(): bool; function isAvailable($key=false): bool {
return $this->getResult($key)->available;
}
/** retourner true si la valeur est valide */ /** retourner true si la valeur est valide */
abstract function isValid(): bool; function isValid($key=false): bool {
return $this->getResult($key)->valid;
}
/** retourner true si la valeur est dans sa forme normalisée */ /** retourner true si la valeur est dans sa forme normalisée */
abstract function isNormalized(): bool; function isNormalized($key=false): bool {
return $this->getResult($key)->normalized;
}
/** obtenir la valeur */ function get($default=null, $key=false) {
abstract function get($default=null); $context = $this->context;
if (!$context->result->available) return $default;
return $context->input->get($context->valueKey);
}
/** remplacer la valeur */ function set($value, ?array $params=null, $key=false): self {
abstract function set($value): self; $context = $this->context;
$context->input->set($value, $context->valueKey);
$this->afterModify($params);
return $this;
}
/** supprimer la valeur */ function unset(?array $params=null, $key=false): self {
abstract function unset(): self; $context = $this->context;
$context->input->unset($context->valueKey);
$this->afterModify($params);
return $this;
}
protected function _format(WrapperContext $context, $format=null): string {
$value = $context->input->get($context->valueKey);
/** @var func $formatterFunc */
$formatterFunc = $context->schema->formatterFunc;
if ($formatterFunc !== null) {
# la fonction formatter n'a pas forcément accès au format de la définition
# le lui fournir ici
$format ??= $context->schema->format;
return $formatterFunc->invoke([$value, $format, $context, $this]);
} else {
# on assume que le type a été initialisé avec le format de la définition
# le cas échéant
return $context->type->format($value, $format);
}
}
/** formatter la valeur pour affichage */ /** formatter la valeur pour affichage */
abstract function format($format=null): string; function format($format=null, $key=false): string {
return $this->_format($this->context, $format);
}
############################################################################# #############################################################################
# key & properties # key & properties
@ -78,14 +208,14 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate {
} }
function offsetGet($offset) { function offsetGet($offset) {
return $this->select($offset); return $this->get(null, $offset);
} }
function offsetSet($offset, $value): void { function offsetSet($offset, $value): void {
$this->select($offset)->set($value); $this->set($value, null, $offset);
} }
function offsetUnset($offset): void { function offsetUnset($offset): void {
$this->select($offset)->unset(); $this->unset(null, $offset);
} }
} }

View File

@ -5,26 +5,44 @@ use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\types\IType; use nur\sery\wip\schema\types\IType;
class WrapperContext { class WrapperContext {
function __construct(Schema $schema, Wrapper $wrapper, Input $input, $valueKey, Result $result) { const DEFAULT_ANALYZE = true;
const DEFAULT_NORMALIZE = true;
const DEFAULT_THROW = true;
function __construct(Schema $schema, ?Input $input, $valueKey, ?array $params) {
$this->resetParams($params);
$this->schema = $schema; $this->schema = $schema;
$this->wrapper = $wrapper; if ($input !== null) $this->input = $input;
$this->input = $input;
$this->result = $result;
$this->type = null;
$this->origValue = null;
$this->value = null;
$this->valueKey = $valueKey; $this->valueKey = $valueKey;
} }
public ?array $params;
public bool $analyze, $analyzed = false;
public bool $normalize, $normalized = false;
public ?bool $throw;
function resetParams(?array $params): void {
$this->params = $params;
$this->analyze = $params["analyze"] ?? self::DEFAULT_ANALYZE;
$this->normalize = $params["normalize"] ?? self::DEFAULT_NORMALIZE;
$this->throw = $params["throw"] ?? self::DEFAULT_THROW;
}
/** schéma de la valeur */
public Schema $schema; public Schema $schema;
public Wrapper $wrapper; /** source et destination de la valeur */
public Input $input; public Input $input;
public Result $result; /** @var string|int|null clé de la valeur dans le tableau destination */
public ?IType $type;
/** @var mixed */
public $origValue;
/** @var mixed */
public $value;
/** @var int|string|null */
public $valueKey; public $valueKey;
/** @var mixed */
public $origValue = null;
/** @var mixed */
public $value = null;
/** @var string|int|null clé sélectionnée */
public $selectedKey = null;
/** type de la valeur de la clé sélectionnée après analyse */
public ?IType $type = null;
/** résultat de l'analyse de la valeur de la clé sélectionnée */
public ?Result $result = null;
} }

View File

@ -1,53 +0,0 @@
<?php
namespace nur\sery\wip\schema\_assoc;
use nulib\ValueException;
use nur\sery\wip\schema\Result;
class AssocResult extends Result {
function __construct(Result $arrayResult, array &$keyResults) {
$this->arrayResult = $arrayResult;
$this->keyResults =& $keyResults;
$this->result =& $this->arrayResult;
parent::__construct();
}
function isAssoc(?AssocResult &$result=null): bool { $result = $this; return true;}
protected Result $arrayResult;
/** @var Result[] */
protected array $keyResults;
function getKeys(): array {
return array_keys($this->keyResults);
}
protected Result $result;
function select($key): Result {
if ($key === null) {
$this->result =& $this->arrayResult;
} elseif (array_key_exists($key, $this->keyResults)) {
$this->result =& $this->keyResults[$key];
} else {
throw ValueException::invalid_key($key);
}
return $this;
}
function reset(): void {
$this->arrayResult->reset();
foreach ($this->keyResults as $result) {
$result->reset();
}
}
function __get(string $name) {
return $this->result[$name];
}
function __set(string $name, $value): void {
$this->result[$name] = $value;
}
}

View File

@ -3,6 +3,7 @@ namespace nur\sery\wip\schema\_assoc;
use nulib\cl; use nulib\cl;
use nulib\ref\schema\ref_schema; use nulib\ref\schema\ref_schema;
use nulib\ValueException;
use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\Wrapper; use nur\sery\wip\schema\Wrapper;
@ -10,12 +11,9 @@ use nur\sery\wip\schema\Wrapper;
* Class AssocSchema * Class AssocSchema
*/ */
class AssocSchema extends Schema { class AssocSchema extends Schema {
/** @var array meta-schema d'un schéma de nature tableau associatif */
const METASCHEMA = ref_schema::ASSOC_METASCHEMA;
/** /**
* indiquer si $definition est une définition de schéma de nature tableau * indiquer si $definition est une définition de schéma de nature tableau
* associatif que {@link normalize()} pourrait normaliser * associatif que {@link normalize_definition()} pourrait normaliser
*/ */
static function isa_definition($definition): bool { static function isa_definition($definition): bool {
if (!is_array($definition)) return false; if (!is_array($definition)) return false;
@ -27,7 +25,7 @@ class AssocSchema extends Schema {
return !cl::have_num_keys($definition); return !cl::have_num_keys($definition);
} }
static function normalize($definition, $definitionKey=null): array { static function normalize_definition($definition, $definitionKey=null): array {
if (!is_array($definition)) $definition = [$definition]; if (!is_array($definition)) $definition = [$definition];
if (!self::have_nature($definition)) { if (!self::have_nature($definition)) {
$definition = [ $definition = [
@ -36,7 +34,8 @@ class AssocSchema extends Schema {
"schema" => $definition, "schema" => $definition,
]; ];
} }
self::_normalize($definition, $definitionKey); $natureMetaschema = array_merge(ref_schema::NATURE_METASCHEMA, ref_schema::ASSOC_NATURE_METASCHEMA);
self::_normalize_definition($definition, $definitionKey, $natureMetaschema);
self::_ensure_nature($definition, "assoc", "array"); self::_ensure_nature($definition, "assoc", "array");
return $definition; return $definition;
} }
@ -44,12 +43,17 @@ class AssocSchema extends Schema {
function __construct($definition=null, $definitionKey=null, bool $normalize=true) { function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
if ($definition === null) $definition = static::SCHEMA; if ($definition === null) $definition = static::SCHEMA;
if ($normalize) { if ($normalize) {
$definition = self::normalize($definition, $definitionKey); $definition = self::normalize_definition($definition, $definitionKey);
$this->_definition = $definition; $this->_definition = $definition;
self::_ensure_type($definition); self::_ensure_type($definition);
self::_ensure_schema_instances($definition); self::_ensure_schema_instances($definition);
} }
$this->definition = $definition; $this->definition = $definition;
$keys = [];
foreach ($definition["schema"] as $key => $schema) {
if (!$schema["computed"]) $keys[] = $key;
}
$this->keys = $keys;
} }
function isAssoc(?AssocSchema &$schema=null): bool { function isAssoc(?AssocSchema &$schema=null): bool {
@ -57,12 +61,29 @@ class AssocSchema extends Schema {
return true; return true;
} }
protected array $keys;
function getKeys(): array {
return $this->keys;
}
function getSchema($key): Schema {
if ($key === null) return $this;
$schema = $this->definition["schema"][$key] ?? null;
if ($schema === null) throw ValueException::invalid_key($key);
return $schema;
}
protected function newWrapper(): AssocWrapper { protected function newWrapper(): AssocWrapper {
return new AssocWrapper($this); return new AssocWrapper($this);
} }
function getWrapper(&$array=null, $arrayKey=null, ?Wrapper &$wrapper=null): AssocWrapper { function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): AssocWrapper {
# si pas de valeur ni de wrapper, pas d'analyse et donc pas d'exception
# cf le code similaire dans AssocWrapper::__construct()
$dontAnalyze = $value === null && $wrapper === null;
if (!($wrapper instanceof AssocWrapper)) $wrapper = $this->newWrapper(); if (!($wrapper instanceof AssocWrapper)) $wrapper = $this->newWrapper();
return $wrapper->reset($array, $arrayKey); if ($params !== null) $wrapper->resetParams($params);
return $wrapper->reset($value, $valueKey, $dontAnalyze? ["analyze" => false]: null);
} }
} }

View File

@ -1,6 +1,7 @@
<?php <?php
namespace nur\sery\wip\schema\_assoc; namespace nur\sery\wip\schema\_assoc;
use nulib\ref\schema\ref_analyze;
use nulib\ValueException; use nulib\ValueException;
use nur\sery\wip\schema\_scalar\ScalarResult; use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarWrapper; use nur\sery\wip\schema\_scalar\ScalarWrapper;
@ -8,133 +9,150 @@ use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\Result; use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\types\IType; use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\Wrapper; use nur\sery\wip\schema\Wrapper;
use nur\sery\wip\schema\WrapperContext;
class AssocWrapper extends Wrapper { class AssocWrapper extends Wrapper {
function __construct(AssocSchema $schema, &$array=null, $arrayKey=null, ?array $params=null) { function __construct(AssocSchema $schema, &$value=null, $valueKey=null, ?array $params=null) {
$verifix = $params["verifix"] ?? true; $keys = $schema->getKeys();
$throw = $params["throw"] ?? null; $keyWrappers = [];
if ($array !== null && $throw === null) { foreach ($keys as $key) {
# Si $value est null, ne pas lancer d'exception, parce qu'on considère que $keyWrappers[$key] = $schema->getSchema($key)->getWrapper();
# c'est une initialisation sans conséquences
$throw = true;
} }
$this->schema = $schema; $this->context = $context = new AssocWrapperContext($schema, null, null, $params);
$this->verifix = $verifix; $context->arrayWrapper = new ScalarWrapper($schema, $dummy, null, null, $context);
$this->throw = $throw ?? false; $context->keys = $keys;
$this->result = new AssocResult(); $context->keyWrappers = $keyWrappers;
$this->reset($array, $arrayKey);
$this->throw = $throw ?? true; # calculer manuellemet throw ici parce que WrapperContext le met à true par
# défaut. on veut pouvoir mettre temporairement throw à false si jamais il
# n'est pas spécifié par l'utilisateur
$throw = $params["throw"] ?? null;
# Si $value est null, ne pas lancer d'exception, parce qu'on considère que
# c'est une initialisation sans conséquences
if ($throw === null && $value !== null) $throw = true;
$context->throw = $throw ?? false;
$this->reset($value, $valueKey);
$context->throw = $throw ?? true;
} }
function isAssoc(?AssocWrapper &$wrapper=null): bool { $wrapper = $this; return true; } function isAssoc(?AssocWrapper &$wrapper=null): bool { $wrapper = $this; return true; }
protected bool $verifix; /** @var AssocWrapperContext */
protected WrapperContext $context;
protected bool $throw; protected function resetContext($resetSelectedKey): void {
$context = $this->context;
/** schéma de ce tableau */ $context->arrayWrapper->getResult()->reset();
protected AssocSchema $schema; foreach ($context->keyWrappers as $wrapper) {
$wrapper->getResult()->reset();
/** source et destination de la valeur */ }
protected Input $input; $context->analyzed = false;
$context->normalized = false;
/** @var string|int|null clé du tableau dans le tableau destination */ if ($resetSelectedKey) $context->selectedKey = null;
protected $arrayKey;
protected IType $arrayType;
protected ScalarResult $arrayResult;
/** @var IType[] */
protected array $keyTypes;
/** @var Result[] */
protected array $keyResults;
protected AssocResult $result;
protected ?array $keys;
protected ?array $wrappers;
protected function newInput(&$value): Input {
return new Input($value);
} }
function reset(&$array, $arrayKey=null, ?bool $verifix=null): Wrapper { function reset(&$value, $valueKey=null, ?array $params=null): Wrapper {
if ($array instanceof Input) $input = $array; $context = $this->context;
else $input = $this->newInput($array); if ($value instanceof Input) $input = $value;
$this->input = $input; else $input = $this->newInput($value);
$this->arrayKey = $arrayKey; $context->input = $input;
$this->analyze(); $context->valueKey = $valueKey;
if ($verifix ?? $this->verifix) $this->verifix(); foreach ($context->keyWrappers as $key => $keyWrapper) {
$keyInput = $input->addKey($valueKey);
$keyWrapper->reset($keyInput, $key, ["analyze" => false]);
}
$this->afterModify($params, true);
return $this; return $this;
} }
function getKeys(): array { function getKeys(): array {
return $this->keys; return $this->context->keys;
} }
function select($key=null): ScalarWrapper { protected function _getWrapper($key): Wrapper {
$wrapper = $this->wrappers[$key] ?? null; if ($key === null) return $this->context->arrayWrapper;
if ($key !== null) return $wrapper; $wrapper = $context->keyWrappers[$key] ?? null;
throw ValueException::invalid_key($key); if ($wrapper === null) throw ValueException::invalid_key($key);
return $wrapper;
} }
/** @param Result[] $results */ /** @param string|int|null $key */
function verifix(?bool $throw=null, ?array &$results=null): bool { function select($key=null): Wrapper {
$wrapper = $this->_getWrapper($key);
$this->context->selectedKey = $key;
return $wrapper;
} }
/**
function getResult(): AssocResult { * @param AssocWrapperContext $context
return $this->result; * @param AssocWrapper $wrapper
*/
static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int {
if ($context->ensureArray) {
$valueKey = $context->valueKey;
$array = $context->input->get($valueKey);
if ($array === null) $context->input->set([], $valueKey);
}
$what = ScalarWrapper::_analyze($context, $wrapper, $params);
/** @var ScalarResult $result */
$result = $context->result;
if (!$result->valid) return $what;
foreach ($context->keyWrappers as $keyWrapper) {
$keyWrapper->analyze($params);
if (!$keyWrapper->isValid()) {
$what = ref_analyze::INVALID;
$result->addInvalidMessage($keyWrapper);
}
}
return $what;
} }
function isPresent(): bool { /**
return $this->result->present; * @param AssocWrapperContext $context
* @param AssocWrapper $wrapper
*/
static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool {
$modified = ScalarWrapper::_normalize($context, $wrapper, $params);
foreach ($context->keyWrappers as $keyWrapper) {
if ($keyWrapper->normalize($params)) $modified = true;
}
return $modified;
} }
function getType(): IType { function getResult($key=false): Result {
return $this->arrayType; if ($key === false) $key = $this->context->selectedKey;
return $this->_getWrapper($key)->getResult();
} }
function isAvailable(): bool { function getType($key=false): IType {
return $this->result->available; if ($key === false) $key = $this->context->selectedKey;
return $this->_getWrapper($key)->getType();
} }
function isValid(): bool { function get($default=null, $key=false) {
return $this->result->valid; $context = $this->context;
if (!$context->arrayWrapper->isAvailable()) return $default;
if ($key === false) $key = $context->selectedKey;
return $this->_getWrapper($key)->get($default);
} }
function isNormalized(): bool { function set($value, ?array $params=null, $key=false): Wrapper {
return $this->result->normalized; $context = $this->context;
} if ($key === false) $key = $context->selectedKey;
$this->_getWrapper($key)->set($value);
function get($default=null) {
if ($this->result->available) return $this->input->get($this->arrayKey);
else return $default;
}
function set($value, ?bool $verifix=null): AssocWrapper {
$this->input->set($value, $this->arrayKey);
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this; return $this;
} }
function unset(?bool $verifix=null): AssocWrapper { function unset(?array $params=null, $key=false): Wrapper {
$this->input->unset($this->arrayKey); $context = $this->context;
$this->analyze(); if ($key === false) $key = $context->selectedKey;
if ($verifix ?? $this->verifix) $this->verifix(); $this->_getWrapper($key)->unset();
return $this; return $this;
} }
function format($format = null): string { function format($format=null, $key=false): string {
// TODO: Implement format() method. $context = $this->context;
} if ($key === false) $key = $context->selectedKey;
return $this->_getWrapper($key)->format($format);
function ensureKeys(): bool {
}
function orderKeys(): bool {
} }
} }

View File

@ -0,0 +1,33 @@
<?php
namespace nur\sery\wip\schema\_assoc;
use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\Wrapper;
use nur\sery\wip\schema\WrapperContext;
class AssocWrapperContext extends WrapperContext {
const DEFAULT_ENSURE_ARRAY = false;
const DEFAULT_ENSURE_KEYS = true;
const DEFAULT_ENSURE_ORDER = true;
function __construct(Schema $schema, ?Input $input, $valueKey, ?array $params) {
parent::__construct($schema, $input, $valueKey, $params);
$this->ensureArray = $params["ensure_array"] ?? self::DEFAULT_ENSURE_ARRAY;
$this->ensureKeys = $params["ensure_keys"] ?? self::DEFAULT_ENSURE_KEYS;
$this->ensureOrder = $params["ensure_order"] ?? self::DEFAULT_ENSURE_ORDER;
}
public bool $ensureArray;
public bool $ensureKeys;
public bool $ensureOrder;
public ?ScalarWrapper $arrayWrapper = null;
/** liste des clés valides */
public array $keys;
/** @var Wrapper[] */
public array $keyWrappers;
}

View File

@ -2,6 +2,7 @@
namespace nur\sery\wip\schema\_list; namespace nur\sery\wip\schema\_list;
use nulib\ref\schema\ref_schema; use nulib\ref\schema\ref_schema;
use nulib\ValueException;
use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\Wrapper; use nur\sery\wip\schema\Wrapper;
@ -34,7 +35,7 @@ class ListSchema extends Schema {
"schema" => $definition[0], "schema" => $definition[0],
]; ];
} }
self::_normalize($definition, $definitionKey); self::_normalize_definition($definition, $definitionKey);
self::_ensure_nature($definition, "list", "array"); self::_ensure_nature($definition, "list", "array");
return $definition; return $definition;
} }
@ -55,12 +56,26 @@ class ListSchema extends Schema {
return true; return true;
} }
const KEYS = [null];
function getKeys(): array {
return self::KEYS;
}
public function getSchema($key): Schema {
if ($key !== null) throw ValueException::invalid_key($key);
return $this;
}
protected function newWrapper(): ListWrapper { protected function newWrapper(): ListWrapper {
return new ListWrapper($this); return new ListWrapper($this);
} }
function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ListWrapper { function getWrapper(&$value=null, $valueKey=null, ?array $params = null, ?Wrapper &$wrapper=null): ListWrapper {
# si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception
# cf le code similaire dans ScalarWrapper::__construct()
$verifix = $value !== null || $wrapper !== null;
if (!($wrapper instanceof ListWrapper)) $wrapper = $this->newWrapper(); if (!($wrapper instanceof ListWrapper)) $wrapper = $this->newWrapper();
return $wrapper->reset($value, $valueKey); return $wrapper->reset($value, $valueKey, $verifix);
} }
} }

View File

@ -6,6 +6,8 @@ use nulib\ref\schema\ref_analyze;
use nulib\ref\schema\ref_schema; use nulib\ref\schema\ref_schema;
use nulib\ValueException; use nulib\ValueException;
use nur\sery\wip\schema\Result; use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\Wrapper;
use Throwable; use Throwable;
/** /**
@ -15,7 +17,7 @@ class ScalarResult extends Result {
function isScalar(?ScalarResult &$result=null): bool { $result = $this; return true; } function isScalar(?ScalarResult &$result=null): bool { $result = $this; return true; }
function getKeys(): array { function getKeys(): array {
return [null]; return ScalarSchema::KEYS;
} }
function select($key): Result { function select($key): Result {
@ -23,8 +25,7 @@ class ScalarResult extends Result {
return $this; return $this;
} }
/** @var array */ protected array $result;
protected $result;
function reset(): void { function reset(): void {
$this->result = array_merge( $this->result = array_merge(
@ -46,26 +47,13 @@ class ScalarResult extends Result {
$this->result[$name] = $value; $this->result[$name] = $value;
} }
protected static function replace_key(string &$message, ?string $key): void { protected function getMessage(string $key, Schema $schema): string {
if ($key) {
$message = str_replace("{key}", $key, $message);
} else {
$message = str_replace("{key}: ", "", $message);
$message = str_replace("cette valeur", "la valeur", $message);
}
}
protected static function replace_orig(string &$message, $origValue): void {
$message = str_replace("{orig}", strval($origValue), $message);
}
protected function getMessage(string $key, ScalarSchema $schema): string {
$message = cl::get($schema->messages, $key); $message = cl::get($schema->messages, $key);
if ($message !== null) return $message; if ($message !== null) return $message;
return cl::get(ref_schema::MESSAGES, $key); return cl::get(ref_schema::MESSAGES, $key);
} }
function setMissing(ScalarSchema $schema): int { function setMissing( Schema $schema): int {
$this->resultAvailable = true; $this->resultAvailable = true;
$this->present = false; $this->present = false;
$this->available = false; $this->available = false;
@ -75,15 +63,13 @@ class ScalarResult extends Result {
$this->normalized = true; $this->normalized = true;
return ref_analyze::NORMALIZED; return ref_analyze::NORMALIZED;
} else { } else {
$messageKey = $this->messageKey = "missing"; $this->messageKey = $messageKey = "missing";
$message = $this->getMessage($messageKey, $schema); $this->message = $this->getMessage($messageKey, $schema);
self::replace_key($message, $schema->name);
$this->message = $message;
return ref_analyze::MISSING; return ref_analyze::MISSING;
} }
} }
function setUnavailable(ScalarSchema $schema): int { function setUnavailable( Schema $schema): int {
$this->resultAvailable = true; $this->resultAvailable = true;
$this->present = true; $this->present = true;
$this->available = false; $this->available = false;
@ -93,15 +79,13 @@ class ScalarResult extends Result {
$this->normalized = true; $this->normalized = true;
return ref_analyze::NORMALIZED; return ref_analyze::NORMALIZED;
} else { } else {
$messageKey = $this->messageKey = "unavailable"; $this->messageKey = $messageKey = "unavailable";
$message = $this->getMessage($messageKey, $schema); $this->message = $this->getMessage($messageKey, $schema);
self::replace_key($message, $schema->name);
$this->message = $message;
return ref_analyze::UNAVAILABLE; return ref_analyze::UNAVAILABLE;
} }
} }
function setNull(ScalarSchema $schema): int { function setNull( Schema $schema): int {
$this->resultAvailable = true; $this->resultAvailable = true;
$this->present = true; $this->present = true;
$this->available = true; $this->available = true;
@ -111,33 +95,53 @@ class ScalarResult extends Result {
$this->normalized = true; $this->normalized = true;
return ref_analyze::NORMALIZED; return ref_analyze::NORMALIZED;
} else { } else {
$messageKey = $this->messageKey = "null"; $this->messageKey = $messageKey = "null";
$message = $this->getMessage($messageKey, $schema); $this->message = $this->getMessage($messageKey, $schema);
self::replace_key($message, $schema->name);
$this->message = $message;
return ref_analyze::NULL; return ref_analyze::NULL;
} }
} }
function setInvalid($value, ScalarSchema $schema, ?Throwable $t=null): int { function setInvalid($value, Schema $schema, ?Throwable $exception=null): int {
$this->resultAvailable = true; $this->resultAvailable = true;
$this->present = true; $this->present = true;
$this->available = true; $this->available = true;
$this->null = false; $this->null = false;
$this->valid = false; $this->valid = false;
$this->origValue = $value; $this->origValue = $value;
$messageKey = $this->messageKey = "invalid"; $this->messageKey = $messageKey = "invalid";
$message = $this->getMessage($messageKey, $schema); $message = $this->getMessage($messageKey, $schema);
self::replace_key($message, $schema->name); if ($exception !== null) {
self::replace_orig($message, $schema->orig); $tmessage = ValueException::get_message($exception);
if ($t !== null) { if ($tmessage) $message = $tmessage;
$tmessage = ValueException::get_message($t);
if ($tmessage) $message .= ": $tmessage";
} }
$this->message = $message; $this->message = $message;
$this->exception = $exception;
return ref_analyze::INVALID; return ref_analyze::INVALID;
} }
function addInvalidMessage(Wrapper $wrapper): void {
$this->resultAvailable = true;
$this->present = true;
$this->available = true;
$this->null = false;
$this->valid = false;
$this->messageKey = "invalid";
$result = $wrapper->getResult();
$resultException = $result->exception;
$resultMessage = $result->message;
if ($resultException !== null) {
$tmessage = ValueException::get_message($resultException);
if ($tmessage) {
if ($resultMessage !== null) $resultMessage .= ": ";
$resultMessage .= $tmessage;
}
}
$message = $this->message;
if ($message) $message .= "\n";
$message .= $resultMessage;
$this->message = $message;
}
function setValid($normalizedValue=null): int { function setValid($normalizedValue=null): int {
$this->resultAvailable = true; $this->resultAvailable = true;
$this->present = true; $this->present = true;
@ -159,6 +163,10 @@ class ScalarResult extends Result {
} }
function throw(bool $throw): void { function throw(bool $throw): void {
if ($throw) throw new ValueException($this->message); if ($throw) {
$exception = $this->exception;
if ($exception !== null) throw $exception;
else throw new ValueException($this->message);
}
} }
} }

View File

@ -1,41 +1,19 @@
<?php <?php
namespace nur\sery\wip\schema\_scalar; namespace nur\sery\wip\schema\_scalar;
use nulib\cl;
use nulib\ref\schema\ref_schema; use nulib\ref\schema\ref_schema;
use nulib\ValueException;
use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\Wrapper; use nur\sery\wip\schema\Wrapper;
/** /**
* Class ScalarSchema * Class ScalarSchema
*
* @property-read array|IType $type
* @property-read mixed $default
* @property-read string|null $title
* @property-read bool $required
* @property-read bool $nullable
* @property-read string|array|null $desc
* @property-read callable|null $analyzerFunc
* @property-read callable|null $extractorFunc
* @property-read callable|null $parserFunc
* @property-read callable|null $normalizerFunc
* @property-read array|null $messages
* @property-read callable|null $formatterFunc
* @property-read mixed $format
* @property-read array $nature
* @property-read array|null $schema
* @property-read string|int|null $name
* @property-read string|array|null $pkey
* @property-read string|null $header
* @property-read bool|null $composite
*/ */
class ScalarSchema extends Schema { class ScalarSchema extends Schema {
/** @var array meta-schema d'un schéma de nature scalaire */
const METASCHEMA = ref_schema::SCALAR_METASCHEMA;
/** /**
* indiquer si $definition est une définition de schéma scalaire que * indiquer si $definition est une définition de schéma scalaire que
* {@link normalize()} pourrait normaliser * {@link normalize_definition()} pourrait normaliser
*/ */
static function isa_definition($definition): bool { static function isa_definition($definition): bool {
# chaine ou null # chaine ou null
@ -64,8 +42,9 @@ class ScalarSchema extends Schema {
return $haveIndex0 && $count > 1; return $haveIndex0 && $count > 1;
} }
static function normalize($definition, $definitionKey=null): array { static function normalize_definition($definition, $definitionKey=null): array {
self::_normalize($definition, $definitionKey); $natureMetaschema = array_merge(ref_schema::NATURE_METASCHEMA, ref_schema::SCALAR_NATURE_METASCHEMA);
self::_normalize_definition($definition, $definitionKey, $natureMetaschema);
self::_ensure_nature($definition, "scalar"); self::_ensure_nature($definition, "scalar");
return $definition; return $definition;
} }
@ -73,7 +52,7 @@ class ScalarSchema extends Schema {
function __construct($definition=null, $definitionKey=null, bool $normalize=true) { function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
if ($definition === null) $definition = static::SCHEMA; if ($definition === null) $definition = static::SCHEMA;
if ($normalize) { if ($normalize) {
$definition = self::normalize($definition, $definitionKey); $definition = self::normalize_definition($definition, $definitionKey);
$this->_definition = $definition; $this->_definition = $definition;
self::_ensure_type($definition); self::_ensure_type($definition);
self::_ensure_schema_instances($definition); self::_ensure_schema_instances($definition);
@ -86,27 +65,27 @@ class ScalarSchema extends Schema {
return true; return true;
} }
const KEYS = [null];
function getKeys(): array {
return self::KEYS;
}
function getSchema($key): Schema {
if ($key !== null) throw ValueException::invalid_key($key);
return $this;
}
protected function newWrapper(): ScalarWrapper { protected function newWrapper(): ScalarWrapper {
return new ScalarWrapper($this); return new ScalarWrapper($this);
} }
function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ScalarWrapper { function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): ScalarWrapper {
# si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception # si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception
# cf le code similaire dans ScalarWrapper::__construct() # cf le code similaire dans ScalarWrapper::__construct()
$verifix = $value !== null || $wrapper !== null; $dontAnalyze = $value === null && $wrapper === null;
if (!($wrapper instanceof ScalarWrapper)) $wrapper = $this->newWrapper(); if (!($wrapper instanceof ScalarWrapper)) $wrapper = $this->newWrapper();
return $wrapper->reset($value, $valueKey, $verifix); if ($params !== null) $wrapper->resetParams($params);
return $wrapper->reset($value, $valueKey, $dontAnalyze? ["analyze" => false]: null);
} }
#############################################################################
# key & properties
const _PROPERTY_PKEYS = [
"analyzerFunc" => "analyzer_func",
"extractorFunc" => "extractor_func",
"parserFunc" => "parser_func",
"normalizerFunc" => "normalizer_func",
"formatterFunc" => "formatter_func",
"nature" => ["", 0],
];
} }

View File

@ -4,67 +4,46 @@ namespace nur\sery\wip\schema\_scalar;
use nulib\php\func; use nulib\php\func;
use nulib\ref\schema\ref_analyze; use nulib\ref\schema\ref_analyze;
use nulib\ValueException; use nulib\ValueException;
use nur\sery\wip\schema\WrapperContext; use nur\sery\wip\schema\_assoc\AssocWrapper;
use nur\sery\wip\schema\input\Input; use nur\sery\wip\schema\_assoc\AssocWrapperContext;
use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\types; use nur\sery\wip\schema\types;
use nur\sery\wip\schema\types\IType; use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\Wrapper; use nur\sery\wip\schema\Wrapper;
use nur\sery\wip\schema\WrapperContext;
/**
* Class ScalarWrapper
*
* @method ScalarWrapper reset(&$value, $valueKey=null, ?array $params=null)
* @method ScalarResult getResult($key=false)
* @method self set($value, ?array $params=null, $key=false)
* @method self unset(?array $params=null, $key=false)
*/
class ScalarWrapper extends Wrapper { class ScalarWrapper extends Wrapper {
function __construct(ScalarSchema $schema, &$value=null, $valueKey=null, ?array $params=null) { function __construct(Schema $schema, &$value=null, $valueKey=null, ?array $params=null, ?WrapperContext $context=null) {
$verifix = $params["verifix"] ?? true; if ($context === null) $context = new WrapperContext($schema, null, null, $params);
$context->result = new ScalarResult();
$this->context = $context;
# calculer manuellemet throw ici parce que WrapperContext le met à true par
# défaut. on veut pouvoir mettre temporairement throw à false si jamais il
# n'est pas spécifié par l'utilisateur
$throw = $params["throw"] ?? null; $throw = $params["throw"] ?? null;
if ($value !== null && $throw === null) { # Si $value est null, ne pas lancer d'exception, parce qu'on considère que
# Si $value est null, ne pas lancer d'exception, parce qu'on considère que # c'est une initialisation sans conséquences
# c'est une initialisation sans conséquences if ($throw === null && $value !== null) $throw = true;
$throw = true; $context->throw = $throw ?? false;
}
$this->verifix = $verifix;
$this->throw = $throw ?? false;
$this->schema = $schema;
$this->result = new ScalarResult();
$this->reset($value, $valueKey); $this->reset($value, $valueKey);
$this->throw = $throw ?? true; $context->throw = $throw ?? true;
} }
function isScalar(?ScalarWrapper &$wrapper=null): bool { $wrapper = $this; return true; } function isScalar(?ScalarWrapper &$wrapper=null): bool { $wrapper = $this; return true; }
protected bool $verifix; protected WrapperContext $context;
protected bool $throw;
/** schéma de cette valeur */
protected ScalarSchema $schema;
/** source et destination de la valeur */
protected Input $input;
/** @var string|int|null clé de la valeur dans le tableau destination */
protected $valueKey;
/** type de la valeur après analyse */
protected ?IType $type;
/** résultat de l'analyse de la valeur */
protected ScalarResult $result;
protected function newInput(&$value): Input {
return new Input($value);
}
function reset(&$value, $valueKey=null, ?bool $verifix=null): Wrapper {
if ($value instanceof Input) $input = $value;
else $input = $this->newInput($value);
$this->input = $input;
$this->valueKey = $valueKey;
$this->type = null;
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function getKeys(): array { function getKeys(): array {
return [null]; return ScalarSchema::KEYS;
} }
/** @param string|int|null $key */ /** @param string|int|null $key */
@ -74,7 +53,7 @@ class ScalarWrapper extends Wrapper {
} }
/** analyser la valeur et résoudre son type */ /** analyser la valeur et résoudre son type */
protected function analyze0(WrapperContext $context): int { protected static function _analyze0(WrapperContext $context): int {
/** @var ScalarSchema $schema */ /** @var ScalarSchema $schema */
$schema = $context->schema; $schema = $context->schema;
$input = $context->input; $input = $context->input;
@ -112,7 +91,7 @@ class ScalarWrapper extends Wrapper {
$args = $name; $args = $name;
$name = $key; $name = $key;
} }
$type = types::get($schema->nullable, $name, $args, $this->schema->getDefinition()); $type = types::get($schema->nullable, $name, $args, $schema->getDefinition());
if ($firstType === null) $firstType = $type; if ($firstType === null) $firstType = $type;
$types[] = $type; $types[] = $type;
if ($type->isAvailable($input, $valueKey)) { if ($type->isAvailable($input, $valueKey)) {
@ -140,7 +119,7 @@ class ScalarWrapper extends Wrapper {
$type = $firstType; $type = $firstType;
} }
} }
$context->type = $this->type = $type; $context->type = $type;
if (!$type->isAvailable($input, $valueKey)) { if (!$type->isAvailable($input, $valueKey)) {
if ($default !== null) { if ($default !== null) {
@ -165,25 +144,28 @@ class ScalarWrapper extends Wrapper {
} }
} }
protected function analyze(): int { /**
$schema = $this->schema; * @param ScalarWrapper $wrapper
$input = $this->input; */
$valueKey = $this->valueKey; static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int {
$result = $this->result; /** @var ScalarSchema $schema */
$result->reset(); $schema = $context->schema;
$context = new WrapperContext($schema, $this, $input, $valueKey, $result); $input = $context->input;
$valueKey = $context->valueKey;
/** @var ScalarResult $result */
$result = $context->result;
/** @var func $analyzerFunc */ /** @var func $analyzerFunc */
$analyzerFunc = $schema->analyzerFunc; $analyzerFunc = $schema->analyzerFunc;
if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context]); if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context, $wrapper]);
else $what = $this->analyze0($context); else $what = self::_analyze0($context);
if ($what !== ref_analyze::STRING) return $what; if ($what !== ref_analyze::STRING) return $what;
$value = $context->value; $value = $context->value;
try { try {
/** @var func $extractorFunc */ /** @var func $extractorFunc */
$extractorFunc = $schema->extractorFunc; $extractorFunc = $schema->extractorFunc;
if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context]); if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context, $wrapper]);
else $extracted = $context->type->extract($value); else $extracted = $context->type->extract($value);
$context->value = $extracted; $context->value = $extracted;
} catch (ValueException $e) { } catch (ValueException $e) {
@ -194,7 +176,7 @@ class ScalarWrapper extends Wrapper {
try { try {
/** @var func $parserFunc */ /** @var func $parserFunc */
$parserFunc = $schema->parserFunc; $parserFunc = $schema->parserFunc;
if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context]); if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context, $wrapper]);
else $parsed = $context->type->parse($extracted); else $parsed = $context->type->parse($extracted);
$context->value = $parsed; $context->value = $parsed;
} catch (ValueException $e) { } catch (ValueException $e) {
@ -211,107 +193,53 @@ class ScalarWrapper extends Wrapper {
} }
} }
function verifix(?bool $throw=null): bool { /**
$result = $this->result; * @param ScalarWrapper $wrapper
$valueKey = $this->valueKey; */
$verifix = false; static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool {
/** @var ScalarSchema $schema */
$schema = $context->schema;
$input = $context->input;
$valueKey = $context->valueKey;
/** @var ScalarResult $result */
$result = $context->result;
$normalize = false;
$modified = false; $modified = false;
if ($result->resultAvailable) { if ($result->resultAvailable) {
if ($result->null) { if ($result->null) {
# forcer la valeur null, parce que la valeur actuelle est peut-être une # forcer la valeur null, parce que la valeur actuelle est peut-être une
# valeur assimilée à null # valeur assimilée à null
$this->input->set(null, $valueKey); $input->set(null, $valueKey);
} elseif ($result->valid && !$result->normalized) { } elseif ($result->valid && !$result->normalized) {
$normalizedValue = $result->normalizedValue; $normalizedValue = $result->normalizedValue;
if ($normalizedValue !== null) { if ($normalizedValue !== null) {
# la valeur normalisée est disponible # la valeur normalisée est disponible
$this->input->set($normalizedValue); $input->set($normalizedValue, $valueKey);
$result->normalizedValue = null; $result->normalizedValue = null;
$modified = true; $modified = true;
} else { } else {
# normaliser la valeur # normaliser la valeur
$verifix = true; $normalize = true;
} }
} }
} else { } else {
$verifix = true; $normalize = true;
} }
if ($verifix) { if ($normalize) {
$value = $this->input->get($valueKey); $value = $input->get($valueKey);
$schema = $this->schema;
/** @var func $normalizerFunc */ /** @var func $normalizerFunc */
$normalizerFunc = $schema->normalizerFunc; $normalizerFunc = $schema->normalizerFunc;
if ($normalizerFunc !== null) { if ($normalizerFunc !== null) {
$context = new WrapperContext($schema, $this, $this->input, $valueKey, $result);
$orig = $value; $orig = $value;
$value = $normalizerFunc->invoke([$orig, $context]); $value = $normalizerFunc->invoke([$orig, $context, $wrapper]);
$modified = $value !== $orig; $modified = $value !== $orig;
} else { } else {
$modified = $this->type->verifix($value, $result, $this->schema); $modified = $context->type->normalize($value, $result, $schema);
} }
if ($result->valid) $this->input->set($value, $valueKey); if ($result->valid) $input->set($value, $valueKey);
} }
if (!$result->valid) $result->throw($throw ?? $this->throw);
return $modified; return $modified;
} }
function getResult(): ScalarResult {
return $this->result;
}
function isPresent(): bool {
return $this->result->present;
}
function getType(): IType {
return $this->type;
}
function isAvailable(): bool {
return $this->result->available;
}
function isValid(): bool {
return $this->result->valid;
}
function isNormalized(): bool {
return $this->result->normalized;
}
function get($default=null) {
if ($this->result->available) return $this->input->get($this->valueKey);
else return $default;
}
function set($value, ?bool $verifix=null): ScalarWrapper {
$this->input->set($value, $this->valueKey);
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function unset(?bool $verifix=null): ScalarWrapper {
$this->input->unset($this->valueKey);
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function format($format=null): string {
$value = $this->input->get($this->valueKey);
/** @var func $formatterFunc */
$formatterFunc = $this->schema->formatterFunc;
if ($formatterFunc !== null) {
# la fonction formatter n'a pas forcément accès au format de la définition
# le lui fournir ici
$format ??= $this->schema->format;
return $formatterFunc->invoke([$value, $format]);
} else {
# on assume que le type a été initialisé avec le format de la définition
# le cas échéant
return $this->type->format($value, $format);
}
}
} }

View File

@ -5,7 +5,6 @@ namespace nur\sery\wip\schema\input;
# construire des querystring et paramètres de formulaires # construire des querystring et paramètres de formulaires
use nur\sery\wip\php\access\FormAccess; use nur\sery\wip\php\access\FormAccess;
use nur\sery\wip\php\access\IAccess; use nur\sery\wip\php\access\IAccess;
use nur\sery\wip\php\access\KeyAccess;
use nur\sery\wip\php\access\ShadowAccess; use nur\sery\wip\php\access\ShadowAccess;
/** /**
@ -18,15 +17,14 @@ use nur\sery\wip\php\access\ShadowAccess;
class FormInput extends Input { class FormInput extends Input {
const ALLOW_EMPTY = false; const ALLOW_EMPTY = false;
protected function formAccess($key): IAccess { function __construct(&$dest=null, ?array $params=null) {
return new FormAccess($key, [ parent::__construct($dest, $params);
"allow_empty" => $this->allowEmpty, $this->access = new ShadowAccess($this->formAccess($this->access), $this->access);
]);
} }
protected function access($key): IAccess { protected function formAccess(IAccess $access): IAccess {
return $this->keyAccess[$key] ??= new ShadowAccess($this->formAccess($key), new KeyAccess($this->value, $key, [ return new FormAccess(null, [
"allow_empty" => $this->allowEmpty, "allow_empty" => $access->isAllowEmpty(),
])); ]);
} }
} }

View File

@ -11,9 +11,9 @@ use nur\sery\wip\php\access\IAccess;
* une référence * une référence
*/ */
class GetInput extends FormInput { class GetInput extends FormInput {
protected function formAccess($key): IAccess { protected function formAccess(IAccess $access): IAccess {
return new GetAccess($key, [ return new GetAccess(null, [
"allow_empty" => $this->allowEmpty, "allow_empty" => $access->isAllowEmpty(),
]); ]);
} }
} }

View File

@ -1,9 +1,10 @@
<?php <?php
namespace nur\sery\wip\schema\input; namespace nur\sery\wip\schema\input;
use nulib\StateException;
use nur\sery\wip\php\access\IAccess; use nur\sery\wip\php\access\IAccess;
use nur\sery\wip\php\access\KeyAccess; use nur\sery\wip\php\access\KeyAccess;
use nur\sery\wip\php\access\ValueAccess; use nur\sery\wip\php\access\PropertyAccess;
/** /**
* Class Input: accès à une valeur * Class Input: accès à une valeur
@ -13,54 +14,62 @@ use nur\sery\wip\php\access\ValueAccess;
class Input { class Input {
const ALLOW_EMPTY = true; const ALLOW_EMPTY = true;
function __construct(&$value=null, ?array $params=null) { const ACCESS_AUTO = 0, ACCESS_KEY = 1, ACCESS_PROPERTY = 2;
$this->value =& $value;
$this->allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY; private static function unexpected_access_type(): StateException {
return StateException::unexpected_state("access_type");
} }
/** @var mixed */ function __construct(&$dest=null, ?array $params=null) {
protected $value; $accessType = $params["access_type"] ?? self::ACCESS_AUTO;
if ($accessType === self::ACCESS_AUTO) {
$accessType = is_object($dest)? self::ACCESS_PROPERTY: self::ACCESS_KEY;
}
/** $allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY;
* @var bool comment considérer une chaine vide: "" si allowEmpty, null sinon if ($accessType == self::ACCESS_PROPERTY) {
*/ $this->access = new PropertyAccess($dest, null, [
protected $allowEmpty; "allow_empty" => $allowEmpty,
"allow_null" => true,
protected ?ValueAccess $valueAccess = null; ]);
protected ?array $keyAccess = null; } elseif ($accessType == self::ACCESS_KEY) {
$this->access = new KeyAccess($dest, null, [
protected function access($key): IAccess { "allow_empty" => $allowEmpty,
if ($key === null) {
return $this->valueAccess ??= new ValueAccess($this->value, [
"allow_null" => true, "allow_null" => true,
"allow_empty" => $this->allowEmpty,
]); ]);
} else { } else {
return $this->keyAccess[$key] ??= new KeyAccess($this->value, $key, [ throw self::unexpected_access_type();
"allow_empty" => $this->allowEmpty,
]);
} }
} }
protected IAccess $access;
/** tester si la valeur existe sans tenir compte de $allowEmpty */ /** tester si la valeur existe sans tenir compte de $allowEmpty */
function isPresent($key=null): bool { function isPresent($key=null): bool {
return $this->access($key)->exists(); return $this->access->resetKey($key)->exists();
} }
/** tester si la valeur est disponible en tenant compte de $allowEmpty */ /** tester si la valeur est disponible en tenant compte de $allowEmpty */
function isAvailable($key=null): bool { function isAvailable($key=null): bool {
return $this->access($key)->available(); return $this->access->resetKey($key)->available();
} }
function get($key=null) { function get($key=null) {
return $this->access($key)->get(); return $this->access->resetKey($key)->get();
} }
function set($value, $key=null): void { function set($value, $key=null): void {
$this->access($key)->set($value); $this->access->resetKey($key)->set($value);
} }
function unset($key=null): void { function unset($key=null): void {
$this->access($key)->del(); $this->access->resetKey($key)->del();
}
function addKey($key): Input {
if ($key === null) return $this;
$input = clone $this;
$input->access = $this->access->addKey($key);
return $input;
} }
} }

View File

@ -11,9 +11,9 @@ use nur\sery\wip\php\access\PostAccess;
* une référence * une référence
*/ */
class PostInput extends FormInput { class PostInput extends FormInput {
protected function formAccess($key): IAccess { protected function formAccess(IAccess $access): IAccess {
return new PostAccess($key, [ return new PostAccess(null, [
"allow_empty" => $this->allowEmpty, "allow_empty" => $access->isAllowEmpty(),
]); ]);
} }
} }

View File

@ -23,7 +23,7 @@ interface IType {
/** /**
* @return string la classe des objets gérés par ce format: le type attendu * @return string la classe des objets gérés par ce format: le type attendu
* par {@link format()} et le type retourné par {@link verifix()} * par {@link format()} et le type retourné par {@link normalize()}
* *
* Les valeurs "mixed", "bool", "float", "int", "string" et "array" peuvent * Les valeurs "mixed", "bool", "float", "int", "string" et "array" peuvent
* aussi être retournées, bien qu'elles ne soient pas à proprement parler des * aussi être retournées, bien qu'elles ne soient pas à proprement parler des
@ -98,15 +98,18 @@ interface IType {
function parse(string $value); function parse(string $value);
/** /**
* analyser, corriger éventuellement et normaliser la valeur * normaliser la valeur. elle *doit* déjà être valide.
* * Si $value est un string. elle *doit* avoir déjà été traitée au préalable
* NB: si $value est un string. elle doit avoir déjà été traitée au préalable
* par extract() et parse() * par extract() et parse()
* *
* si la valeur était déjà normalisée, ou si une erreur s'est produite, * - si $result indique que la valeur est déjà normalisée, cette méthode ne
* retourner false. * fait rien
* - si la valeur était déjà normalisée, mettre à jour $result pour indiquer
* que la valeur est normalisée et retourner false
* - sinon, retourner true pour indiquer qu'il a fallut normaliser la valeur.
* $result n'est pas modifié
*/ */
function verifix(&$value, Result $result, Schema $schema): bool; function normalize(&$value, Result $result, Schema $schema): bool;
/** /**
* formatter la valeur pour affichage. si $value n'est pas null, elle est * formatter la valeur pour affichage. si $value n'est pas null, elle est

View File

@ -37,7 +37,7 @@ class tarray extends _tstring {
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_array($value); $normalized = is_array($value);
return is_scalar($value) || is_array($value); return $normalized || is_scalar($value);
} }
function parse(string $value) { function parse(string $value) {
@ -49,23 +49,14 @@ class tarray extends _tstring {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
if (is_array($value)) { if ($result->normalized) {
} elseif (is_array($value)) {
$result->setNormalized(); $result->setNormalized();
return false;
} elseif (is_string($value)) {
try {
$value = $this->parse($value);
$result->setValid();
return true;
} catch (ValueException $e) {
}
} elseif (is_scalar($value)) { } elseif (is_scalar($value)) {
$value = cl::with($value); $value = cl::with($value);
$result->setValid();
return true; return true;
} }
$result->setInvalid($value, $schema);
return false; return false;
} }

View File

@ -99,23 +99,14 @@ class tbool extends _tformatable {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
if (is_bool($value)) { if ($result->normalized) {
} elseif (is_bool($value)) {
$result->setNormalized(); $result->setNormalized();
return false;
} elseif (is_string($value)) {
try {
$value = $this->parse($value);
$result->setValid();
return true;
} catch (ValueException $e) {
}
} elseif (is_scalar($value)) { } elseif (is_scalar($value)) {
$value = boolval($value); $value = boolval($value);
$result->setValid();
return true; return true;
} }
$result->setInvalid($value, $schema);
return false; return false;
} }

View File

@ -27,7 +27,7 @@ class tcallable extends _tsimple {
} }
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_callable($value); $normalized = $value instanceof func;
return func::check($value); return func::check($value);
} }
@ -43,23 +43,14 @@ class tcallable extends _tsimple {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
if ($value instanceof func) { if ($result->normalized) {
} elseif ($value instanceof func) {
$result->setNormalized(); $result->setNormalized();
return false;
} elseif (is_callable($value)) { } elseif (is_callable($value)) {
$value = func::with($value); $value = func::with($value);
$result->setNormalized();
return true; return true;
} elseif (is_string($value)) {
try {
$value = $this->parse($value);
$result->setValid();
return true;
} catch (ValueException $e) {
}
} }
$result->setInvalid($value, $schema);
return false; return false;
} }

View File

@ -25,7 +25,7 @@ abstract class tcontent extends _tunion {
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_string($value) || is_array($value); $normalized = is_string($value) || is_array($value);
return is_scalar($value) || is_array($value); return $normalized || is_scalar($value);
} }
function parse(string $value) { function parse(string $value) {
@ -36,18 +36,15 @@ abstract class tcontent extends _tunion {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
if (is_string($value) || is_array($value)) { if ($result->normalized) {
} elseif (is_string($value) || is_array($value)) {
$result->setNormalized(); $result->setNormalized();
return false;
} elseif (is_scalar($value)) { } elseif (is_scalar($value)) {
$value = strval($value); $value = strval($value);
$result->setValid();
return true; return true;
} else {
$result->setInvalid($value, $schema);
return false;
} }
return false;
} }
function format($value, $format=null): string { function format($value, $format=null): string {

View File

@ -43,23 +43,14 @@ class tfloat extends _tformatable {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
if (is_float($value)) { if ($result->normalized) {
} elseif (is_float($value)) {
$result->setNormalized(); $result->setNormalized();
return false;
} elseif (is_string($value)) {
try {
$value = $this->parse($value);
$result->setValid();
return true;
} catch (ValueException $e) {
}
} elseif (is_scalar($value)) { } elseif (is_scalar($value)) {
$value = floatval($value); $value = floatval($value);
$result->setValid();
return true; return true;
} }
$result->setInvalid($value, $schema);
return false; return false;
} }
} }

View File

@ -29,8 +29,8 @@ class tgeneric extends _tsimple {
} }
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = true; $normalized = $value instanceof $this->class;
return $value instanceof $this->class; return $normalized;
} }
function parse(string $value) { function parse(string $value) {
@ -41,8 +41,8 @@ class tgeneric extends _tsimple {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
$result->setNormalized(); if (!$result->normalized) $result->setNormalized();
return false; return false;
} }

View File

@ -45,23 +45,14 @@ class tint extends _tformatable {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
if (is_int($value)) { if ($result->normalized) {
} elseif (is_int($value)) {
$result->setNormalized(); $result->setNormalized();
return false;
} elseif (is_string($value)) {
try {
$value = $this->parse($value);
$result->setValid();
return true;
} catch (ValueException $e) {
}
} elseif (is_scalar($value)) { } elseif (is_scalar($value)) {
$value = intval($value); $value = intval($value);
$result->setValid();
return true; return true;
} }
$result->setInvalid($value, $schema);
return false; return false;
} }
} }

View File

@ -25,7 +25,7 @@ class tkey extends _tunion {
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_string($value) || is_int($value); $normalized = is_string($value) || is_int($value);
return is_scalar($value); return $normalized || is_scalar($value);
} }
function parse(string $value) { function parse(string $value) {
@ -36,18 +36,15 @@ class tkey extends _tunion {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
if (is_string($value) || is_int($value)) { if ($result->normalized) {
} elseif (is_string($value) || is_int($value)) {
$result->setNormalized(); $result->setNormalized();
return false;
} elseif (is_scalar($value)) { } elseif (is_scalar($value)) {
$value = strval($value); $value = strval($value);
$result->setValid();
return true; return true;
} else {
$result->setInvalid($value, $schema);
return false;
} }
return false;
} }
function format($value, $format=null): string { function format($value, $format=null): string {

View File

@ -35,8 +35,8 @@ class tmixed extends _tsimple {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
$result->setNormalized(); if (!$result->normalized) $result->setNormalized();
return false; return false;
} }

View File

@ -30,7 +30,7 @@ class tpkey extends _tunion {
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_string($value) || is_int($value) || is_array($value); $normalized = is_string($value) || is_int($value) || is_array($value);
return is_scalar($value) || is_array($value); return $normalized || is_scalar($value);
} }
function parse(string $value) { function parse(string $value) {
@ -41,18 +41,15 @@ class tpkey extends _tunion {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
if (is_string($value) || is_int($value) || is_array($value)) { if ($result->normalized) {
} elseif (is_string($value) || is_int($value) || is_array($value)) {
$result->setNormalized(); $result->setNormalized();
return false;
} elseif (is_scalar($value)) { } elseif (is_scalar($value)) {
$value = strval($value); $value = strval($value);
$result->setValid();
return true; return true;
} else {
$result->setInvalid($value, $schema);
return false;
} }
return false;
} }
function format($value, $format=null): string { function format($value, $format=null): string {

View File

@ -44,18 +44,15 @@ class trawstring extends _tstring {
* @var ScalarResult $result * @var ScalarResult $result
* @var ScalarSchema $schema * @var ScalarSchema $schema
*/ */
function verifix(&$value, Result $result, Schema $schema): bool { function normalize(&$value, Result $result, Schema $schema): bool {
if (is_string($value)) { if ($result->normalized) {
} elseif (is_string($value)) {
$result->setNormalized(); $result->setNormalized();
return false;
} elseif (is_scalar($value)) { } elseif (is_scalar($value)) {
$value = strval($value); $value = strval($value);
$result->setValid();
return true; return true;
} else {
$result->setInvalid($value, $schema);
return false;
} }
return false;
} }
function format($value, $format=null): string { function format($value, $format=null): string {

View File

@ -5,7 +5,69 @@ use nulib\tests\TestCase;
use stdClass; use stdClass;
class KeyAccessTest extends TestCase { class KeyAccessTest extends TestCase {
function testAccess() { function testValueAccess() {
$default = new stdClass();
#
$i = null;
$a = new KeyAccess($i);
self::assertFalse($a->exists());
self::assertFalse($a->available());
self::assertSame($default, $a->get($default));
$i = false;
$a = new KeyAccess($i);
self::assertTrue($a->exists());
self::assertTrue($a->available());
self::assertSame(false, $a->get($default));
$i = "";
$a = new KeyAccess($i);
self::assertTrue($a->exists());
self::assertTrue($a->available());
self::assertSame("", $a->get($default));
#
$i = null;
$a = new KeyAccess($i, null, ["allow_null" => false]);
self::assertFalse($a->exists());
self::assertFalse($a->available());
self::assertSame($default, $a->get($default));
$i = null;
$a = new KeyAccess($i, null, ["allow_null" => true]);
self::assertTrue($a->exists());
self::assertTrue($a->available());
self::assertSame(null, $a->get($default));
#
$i = false;
$a = new KeyAccess($i, null, ["allow_false" => false]);
self::assertTrue($a->exists());
self::assertFalse($a->available());
self::assertSame($default, $a->get($default));
$i = false;
$a = new KeyAccess($i, null, ["allow_false" => true]);
self::assertTrue($a->exists());
self::assertTrue($a->available());
self::assertSame(false, $a->get($default));
#
$i = "";
$a = new KeyAccess($i, null, ["allow_empty" => false]);
self::assertTrue($a->exists());
self::assertFalse($a->available());
self::assertSame($default, $a->get($default));
$i = "";
$a = new KeyAccess($i, null, ["allow_empty" => true]);
self::assertTrue($a->exists());
self::assertTrue($a->available());
self::assertSame("", $a->get($default));
}
function testArrayAccess() {
$default = new stdClass(); $default = new stdClass();
$array = ["null" => null, "false" => false, "empty" => ""]; $array = ["null" => null, "false" => false, "empty" => ""];
@ -63,4 +125,91 @@ class KeyAccessTest extends TestCase {
self::assertTrue($a->available()); self::assertTrue($a->available());
self::assertSame("", $a->get($default)); self::assertSame("", $a->get($default));
} }
private function _ensureAssoc(?array $orig, ?array $expected, array $keys, ?array $params=null) {
$v = $orig; $a = new KeyAccess($v);
$a->ensureAssoc($keys, $params);
self::assertSame($expected, $v);
}
function testEnsureAssoc() {
$keys = ["a", "b", "c"];
$this->_ensureAssoc(null, null, $keys);
$this->_ensureAssoc([], [], $keys);
$this->_ensureAssoc([1], ["a" => 1], $keys);
$this->_ensureAssoc([1, 2, 3], ["a" => 1, "b" => 2, "c" => 3], $keys);
$this->_ensureAssoc([1, 2, 3, 4], [3 => 4, "a" => 1, "b" => 2, "c" => 3], $keys);
$this->_ensureAssoc(["c" => 3, 1], ["c" => 3, "a" => 1], $keys);
$this->_ensureAssoc(["c" => 3, "b" => 2, 1], ["c" => 3, "b" => 2, "a" => 1], $keys);
$this->_ensureAssoc(["c" => 3, "b" => 2, "a" => 1], ["c" => 3, "b" => 2, "a" => 1], $keys);
$this->_ensureAssoc(["a" => 1, 2], ["a" => 1, "b" => 2], $keys);
$this->_ensureAssoc([2, "a" => 1], ["a" => 1, "b" => 2], $keys);
$keys = [0, "a", "b"];
$this->_ensureAssoc([1], [1], $keys);
$this->_ensureAssoc([1, 2], [1, "a" => 2], $keys);
}
private function _ensureKeys(?array $orig, ?array $expected, array $defaults, ?array $params=null) {
$v = $orig; $a = new KeyAccess($v);
$a->ensureKeys($defaults, $params);
self::assertSame($expected, $v);
}
function testEnsureKeys() {
$defaults = ["a" => false, "b" => false, "c" => false];
$this->_ensureKeys(null, ["a" => false, "b" => false, "c" => false], $defaults);
$this->_ensureKeys([], ["a" => false, "b" => false, "c" => false], $defaults);
$this->_ensureKeys(["a" => 1], ["a" => 1, "b" => false, "c" => false], $defaults);
$this->_ensureKeys(["a" => 1, "b" => 2, "c" => 3], ["a" => 1, "b" => 2, "c" => 3], $defaults);
$this->_ensureKeys(["x"], ["x", "a" => false, "b" => false, "c" => false], $defaults);
$this->_ensureKeys(["x", "a" => 1], ["x", "a" => 1, "b" => false, "c" => false], $defaults);
$this->_ensureKeys(["a" => 1, "x"], ["a" => 1, "x", "b" => false, "c" => false], $defaults);
$this->_ensureKeys(["a" => 1, "b" => 2, "c" => 3, "x"], ["a" => 1, "b" => 2, "c" => 3, "x"], $defaults);
}
private function _ensureOrder(?array $orig, ?array $expected, array $keys, ?array $params=null) {
$v = $orig; $a = new KeyAccess($v);
$a->ensureOrder($keys, $params);
self::assertSame($expected, $v);
}
function testEnsureOrder() {
$keys = ["a", "b", "c"];
$this->_ensureOrder(null, null, $keys);
$this->_ensureOrder([], [], $keys);
$this->_ensureOrder([1], [1], $keys);
$this->_ensureOrder(["b" => 2, "a" => 1], ["a" => 1, "b" => 2], $keys);
$this->_ensureOrder(["c" => 3, "a" => 1], ["a" => 1, "c" => 3], $keys);
}
private function _ensureAssocKeysOrder(?array $orig, ?array $expected, array $defaults, ?array $params=null) {
$v = $orig; $a = new KeyAccess($v);
$keys = array_keys($defaults);
$a->ensureAssoc($keys, $params);
$a->ensureKeys($defaults, $params);
$a->ensureOrder($keys, $params);
self::assertSame($expected, $v);
}
function testEnsureAssocKeysOrder() {
$defaults = ["a" => false, "b" => false, "c" => false];
$this->_ensureAssocKeysOrder(null, ["a" => false, "b" => false, "c" => false], $defaults);
$this->_ensureAssocKeysOrder([], ["a" => false, "b" => false, "c" => false], $defaults);
$this->_ensureAssocKeysOrder([1], ["a" => 1, "b" => false, "c" => false], $defaults);
$this->_ensureAssocKeysOrder([1, 2, 3], ["a" => 1, "b" => 2, "c" => 3], $defaults);
$this->_ensureAssocKeysOrder([1, 2, 3, 4], ["a" => 1, "b" => 2, "c" => 3, 4], $defaults);
$this->_ensureAssocKeysOrder([1, 2, 3, 4], ["a" => 1, "b" => 2, "c" => 3, 3 => 4], $defaults, [
"preserve_keys" => true,
]);
$this->_ensureAssocKeysOrder(["c" => 3, 1], ["a" => 1, "b" => false, "c" => 3], $defaults);
$this->_ensureAssocKeysOrder(["c" => 3, "b" => 2, 1], ["a" => 1, "b" => 2, "c" => 3], $defaults);
$this->_ensureAssocKeysOrder(["c" => 3, "b" => 2, "a" => 1], ["a" => 1, "b" => 2, "c" => 3], $defaults);
$this->_ensureAssocKeysOrder(["a" => 1, 2], ["a" => 1, "b" => 2, "c" => false], $defaults);
$this->_ensureAssocKeysOrder([2, "a" => 1], ["a" => 1, "b" => 2, "c" => false], $defaults);
$this->_ensureAssocKeysOrder([1], ["x_a" => 1, "x_b" => false, "x_c" => false], $defaults, [
"key_prefix" => "x_",
]);
}
} }

View File

@ -1,69 +0,0 @@
<?php
namespace nur\sery\wip\php\access;
use nulib\tests\TestCase;
use stdClass;
class ValueAccessTest extends TestCase {
function testAccess() {
$default = new stdClass();
#
$i = null;
$a = new ValueAccess($i);
self::assertFalse($a->exists());
self::assertFalse($a->available());
self::assertSame($default, $a->get($default));
$i = false;
$a = new ValueAccess($i);
self::assertTrue($a->exists());
self::assertTrue($a->available());
self::assertSame(false, $a->get($default));
$i = "";
$a = new ValueAccess($i);
self::assertTrue($a->exists());
self::assertTrue($a->available());
self::assertSame("", $a->get($default));
#
$i = null;
$a = new ValueAccess($i, ["allow_null" => false]);
self::assertFalse($a->exists());
self::assertFalse($a->available());
self::assertSame($default, $a->get($default));
$i = null;
$a = new ValueAccess($i, ["allow_null" => true]);
self::assertTrue($a->exists());
self::assertTrue($a->available());
self::assertSame(null, $a->get($default));
#
$i = false;
$a = new ValueAccess($i, ["allow_false" => false]);
self::assertTrue($a->exists());
self::assertFalse($a->available());
self::assertSame($default, $a->get($default));
$i = false;
$a = new ValueAccess($i, ["allow_false" => true]);
self::assertTrue($a->exists());
self::assertTrue($a->available());
self::assertSame(false, $a->get($default));
#
$i = "";
$a = new ValueAccess($i, ["allow_empty" => false]);
self::assertTrue($a->exists());
self::assertFalse($a->available());
self::assertSame($default, $a->get($default));
$i = "";
$a = new ValueAccess($i, ["allow_empty" => true]);
self::assertTrue($a->exists());
self::assertTrue($a->available());
self::assertSame("", $a->get($default));
}
}

View File

@ -7,7 +7,14 @@ use nur\sery\wip\schema\_scalar\ScalarSchemaTest;
class AssocSchemaTest extends TestCase { class AssocSchemaTest extends TestCase {
const NULL_SCHEMA = [ const NULL_SCHEMA = [
"" => ["assoc"], "" => [
"assoc",
"compute_func" => null,
"validate_func" => null,
"ensure_array" => false,
"ensure_keys" => true,
"ensure_order" => true,
],
"schema" => null, "schema" => null,
"type" => [null], "type" => [null],
"default" => null, "default" => null,
@ -44,7 +51,7 @@ class AssocSchemaTest extends TestCase {
"type" => ["string"], "nullable" => false, "type" => ["string"], "nullable" => false,
"name" => "a", "pkey" => "a", "header" => "a", "name" => "a", "pkey" => "a", "header" => "a",
], ],
]), AssocSchema::normalize(["a" => "string"])); ]), AssocSchema::normalize_definition(["a" => "string"]));
self::assertSame(self::schema([ self::assertSame(self::schema([
"type" => ["array"], "nullable" => true, "type" => ["array"], "nullable" => true,
@ -61,7 +68,7 @@ class AssocSchemaTest extends TestCase {
"type" => ["bool"], "nullable" => false, "type" => ["bool"], "nullable" => false,
"name" => "c", "pkey" => "c", "header" => "c", "name" => "c", "pkey" => "c", "header" => "c",
], ],
]), AssocSchema::normalize([ ]), AssocSchema::normalize_definition([
"a" => "string", "a" => "string",
"b" => "int", "b" => "int",
"c" => "bool", "c" => "bool",
@ -90,6 +97,72 @@ class AssocSchemaTest extends TestCase {
"name" => "c", "pkey" => "c", "header" => "c", "name" => "c", "pkey" => "c", "header" => "c",
], ],
]), $schema->getDefinition()); ]), $schema->getDefinition());
yaml::dump($schema->getDefinition()); //yaml::dump($schema->getDefinition());
}
function testWrapper() {
$schema = new AssocSchema([
"a" => "?string",
"b" => "?int",
"c" => "?bool",
]);
$array = ["a" => " string ", "b" => " 42 ", "c" => false];
$schema->getWrapper($array);
self::assertSame([
"a" => "string",
"b" => 42,
"c" => false,
], $array);
$schema = new AssocSchema([
"a" => "string",
"b" => "int",
"c" => "bool",
]);
$array = ["a" => " string "];
$schema->getWrapper($array);
self::assertSame([
"a" => "string",
"b" => false,
"c" => null,
], $array);
$array = ["c" => false, "a" => " string "];
$schema->getWrapper($array);
self::assertSame([
"a" => "string",
"b" => false,
"c" => false,
], $array);
$array = ["a" => " string "];
$schema->getWrapper($array, null, ["ensure_order" => false]);
self::assertSame([
"a" => "string",
"b" => false,
"c" => null,
], $array);
$array = ["c" => false, "a" => " string "];
$schema->getWrapper($array, null, ["ensure_order" => false]);
self::assertSame([
"c" => false,
"a" => "string",
"b" => false,
], $array);
$array = ["a" => " string "];
$schema->getWrapper($array, null, ["ensure_keys" => false]);
self::assertSame([
"a" => "string",
], $array);
$array = ["c" => false, "a" => " string "];
$schema->getWrapper($array, null, ["ensure_keys" => false]);
self::assertSame([
"a" => "string",
"c" => false,
], $array);
} }
} }

View File

@ -19,7 +19,11 @@ class ScalarSchemaTest extends TestCase {
"messages" => null, "messages" => null,
"formatter_func" => null, "formatter_func" => null,
"format" => null, "format" => null,
"" => ["scalar"], "" => [
"scalar",
"compute_func" => null,
"validate_func" => null,
],
"schema" => null, "schema" => null,
"name" => null, "name" => null,
"pkey" => null, "pkey" => null,
@ -32,33 +36,33 @@ class ScalarSchemaTest extends TestCase {
} }
function testNormalize() { function testNormalize() {
self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize(null)); self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition(null));
self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([])); self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition([]));
self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([null])); self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition([null]));
self::assertException(SchemaException::class, function () { self::assertException(SchemaException::class, function () {
ScalarSchema::normalize([[]]); ScalarSchema::normalize_definition([[]]);
}); });
self::assertException(SchemaException::class, function () { self::assertException(SchemaException::class, function () {
ScalarSchema::normalize([[null]]); ScalarSchema::normalize_definition([[null]]);
}); });
$string = self::schema(["type" => ["string"], "nullable" => false]); $string = self::schema(["type" => ["string"], "nullable" => false]);
self::assertSame($string, ScalarSchema::normalize("string")); self::assertSame($string, ScalarSchema::normalize_definition("string"));
self::assertSame($string, ScalarSchema::normalize(["string"])); self::assertSame($string, ScalarSchema::normalize_definition(["string"]));
$nstring = self::schema(["type" => ["string"]]); $nstring = self::schema(["type" => ["string"]]);
self::assertSame($nstring, ScalarSchema::normalize(["?string"])); self::assertSame($nstring, ScalarSchema::normalize_definition(["?string"]));
self::assertSame($nstring, ScalarSchema::normalize(["?string|null"])); self::assertSame($nstring, ScalarSchema::normalize_definition(["?string|null"]));
self::assertSame($nstring, ScalarSchema::normalize(["string|null"])); self::assertSame($nstring, ScalarSchema::normalize_definition(["string|null"]));
self::assertSame($nstring, ScalarSchema::normalize([["?string", "null"]])); self::assertSame($nstring, ScalarSchema::normalize_definition([["?string", "null"]]));
self::assertSame($nstring, ScalarSchema::normalize([["string", "null"]])); self::assertSame($nstring, ScalarSchema::normalize_definition([["string", "null"]]));
self::assertSame($nstring, ScalarSchema::normalize([["string", null]])); self::assertSame($nstring, ScalarSchema::normalize_definition([["string", null]]));
$key = self::schema(["type" => ["string", "int"], "nullable" => false]); $key = self::schema(["type" => ["string", "int"], "nullable" => false]);
self::assertSame($key, ScalarSchema::normalize("string|int")); self::assertSame($key, ScalarSchema::normalize_definition("string|int"));
$nkey = self::schema(["type" => ["string", "int"], "nullable" => true]); $nkey = self::schema(["type" => ["string", "int"], "nullable" => true]);
self::assertSame($nkey, ScalarSchema::normalize("?string|int")); self::assertSame($nkey, ScalarSchema::normalize_definition("?string|int"));
self::assertSame($nkey, ScalarSchema::normalize("string|?int")); self::assertSame($nkey, ScalarSchema::normalize_definition("string|?int"));
} }
} }

View File

@ -15,19 +15,21 @@ class ScalarWrapperTest extends TestCase {
self::assertSame($normalized, $wrapper->isNormalized(), "normalized"); self::assertSame($normalized, $wrapper->isNormalized(), "normalized");
} }
function checkVerifix(ScalarSchema $schema, $orig, bool $verifix, $value, bool $present, bool $available, bool $valid, bool $normalized, ?array $inputParams=null): void { function checkVerifix(ScalarSchema $schema, $orig, bool $normalize, $value, bool $present, bool $available, bool $valid, bool $normalized, ?array $inputParams=null): void {
$wrapper = $schema->getWrapper(); $wrapper = $schema->getWrapper();
$wrapper->resetParams(["normalize" => $normalize]);
if ($inputParams !== null) $input = new Input($orig, $inputParams); if ($inputParams !== null) $input = new Input($orig, $inputParams);
else $input = $orig; else $input = $orig;
$wrapper->reset($input, null, $verifix); $wrapper->reset($input);
$this->checkValue($wrapper, $value, $present, $available, $valid, $normalized); $this->checkValue($wrapper, $value, $present, $available, $valid, $normalized);
} }
function checkException(ScalarSchema $schema, $orig, bool $verifix, string $exceptionClass, ?array $inputParams=null) { function checkException(ScalarSchema $schema, $orig, bool $normalize, string $exceptionClass, ?array $inputParams=null) {
$wrapper = $schema->getWrapper(); $wrapper = $schema->getWrapper();
if ($inputParams !== null) $orig = new Input($orig, $inputParams); if ($inputParams !== null) $orig = new Input($orig, $inputParams);
self::assertException($exceptionClass, function() use ($wrapper, &$orig, $verifix) { self::assertException($exceptionClass, function() use ($wrapper, &$orig, $normalize) {
$wrapper->reset($orig, null, $verifix); $wrapper->resetParams(["normalize" => $normalize]);
$wrapper->reset($orig);
}); });
} }