modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2025-03-20 08:01:50 +04:00
parent 6e8245dbcb
commit f37a53cfbd
26 changed files with 214 additions and 134 deletions

View File

@ -9,6 +9,10 @@ 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;
@ -18,7 +22,7 @@ class ChainAccess extends AbstractAccess {
protected IAccess $access;
/** @var int|string|array */
/** @var null|int|string|array */
protected $key;
protected int $accessType;
@ -45,7 +49,7 @@ class ChainAccess extends AbstractAccess {
$class = new ReflectionClass($src);
return $class->hasProperty($key) || property_exists($src, $key);
} else {
throw StateException::unexpected_state();
throw self::unexpected_access_type();
}
}
@ -71,7 +75,7 @@ class ChainAccess extends AbstractAccess {
return $default;
}
} else {
throw StateException::unexpected_state();
throw self::unexpected_access_type();
}
}
@ -95,23 +99,30 @@ class ChainAccess extends AbstractAccess {
}
function exists(): bool {
if ($this->key === null) return false;
if (!$this->access->exists()) return false;
if ($this->key === null) return true;
return $this->_has();
}
function available(): bool {
if ($this->key === null) return false;
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);
@ -121,11 +132,15 @@ class ChainAccess extends AbstractAccess {
} elseif ($accessType === self::ACCESS_PROPERTY) {
$this->_pset($dest, $key, $value);
} else {
throw StateException::unexpected_state();
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);
@ -135,14 +150,16 @@ class ChainAccess extends AbstractAccess {
} elseif ($accessType === self::ACCESS_PROPERTY) {
$this->_pset($dest, $key, null);
} else {
throw StateException::unexpected_state();
throw self::unexpected_access_type();
}
}
function addKey($key, ?array $params=null): IAccess {
$accessType = $params["access_type"] ?? self::ACCESS_AUTO;
if ($key === null) return $this;
$accessType = $params["access_type"] ?? $this->accessType;
if ($accessType === self::ACCESS_KEY && $accessType === $this->accessType) {
return new ChainAccess($this->access, cl::merge($this->key, $key));
if ($this->key !== null) $key = cl::merge($this->key, $key);
return new ChainAccess($this->access, $key);
} else {
return new ChainAccess($this, $key);
}

View File

@ -14,12 +14,12 @@ class FormAccess extends AbstractAccess {
$this->key = $key;
}
/** @var int|string|array */
/** @var null|int|string|array */
protected $key;
protected function _exists(array $first, ?array $second=null): bool {
$key = $this->key;
if ($key === null) return false;
if ($key === null) return true;
if (cl::phas($first, $key)) return true;
return $second !== null && cl::phas($second, $key);
}
@ -30,7 +30,7 @@ class FormAccess extends AbstractAccess {
protected function _available(array $first, ?array $second=null): bool {
$key = $this->key;
if ($key === null) return false;
if ($key === null) return true;
if (cl::phas($first, $key)) {
return $this->allowEmpty || cl::pget($first, $key) !== "";
} elseif ($second !== null && cl::phas($second, $key)) {
@ -46,7 +46,7 @@ class FormAccess extends AbstractAccess {
protected function _get($default, array $first, ?array $second=null) {
$key = $this->key;
if ($key === null) return $default;
if ($key === null) return cl::merge($first, $second);
if (cl::phas($first, $key)) {
$value = cl::pget($first, $key);
if ($value !== "" || $this->allowEmpty) return $value;
@ -63,7 +63,10 @@ class FormAccess extends AbstractAccess {
function _set($value, array &$first, ?array &$second=null): void {
$key = $this->key;
if ($key === null) return;
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 {
@ -77,7 +80,10 @@ class FormAccess extends AbstractAccess {
function _del(array &$first, ?array &$second=null): void {
$key = $this->key;
if ($key === null) return;
if ($key === null) {
# interdire la modification de la destination
return;
}
if ($second !== null && !cl::phas($first, $key) && cl::phas($second, $key)) {
cl::pdel($second, $key);
} else {
@ -90,7 +96,9 @@ class FormAccess extends AbstractAccess {
}
function addKey($key): self {
return new static(cl::merge($this->key, $key), [
if ($key === null) return $this;
if ($this->key !== null) $key = cl::merge($this->key, $key);
return new static($key, [
"allow_empty" => $this->allowEmpty
]);
}

View File

@ -12,15 +12,19 @@ use nulib\cl;
class KeyAccess extends AbstractAccess {
const ALLOW_NULL = null;
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->allowNull = $params["allow_null"] ?? static::ALLOW_NULL;
$this->allowFalse = $params["allow_false"] ?? static::ALLOW_FALSE;
}
protected bool $protectDest;
/** @var mixed|array|ArrayAccess */
protected $dest;
@ -86,21 +90,30 @@ class KeyAccess extends AbstractAccess {
function set($value): void {
$key = $this->key;
if ($key === null) $this->dest = $value;
else cl::pset($this->dest, $key, $value);
if ($key === null) {
if (!$this->protectDest) $this->dest = $value;
} else {
cl::pset($this->dest, $key, $value);
}
}
function del(): void {
$key = $this->key;
if ($key === null) $this->dest = null;
else cl::pdel($this->dest, $key);
if ($key === null) {
if (!$this->protectDest) $this->dest = null;
} else {
cl::pdel($this->dest, $key);
}
}
function addKey($key): self {
return new KeyAccess($this->dest, cl::merge($this->key, $key), [
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,
]);
}
}

View File

@ -1,44 +1,65 @@
<?php
namespace nur\sery\wip\php\access;
use nulib\StateException;
use nulib\str;
use ReflectionClass;
use ReflectionException;
use ReflectionProperty;
class PropertyAccess extends AbstractAccess {
const PROTECT_DEST = true;
const MAP_NAMES = true;
const ALLOW_NULL = true;
const ALLOW_FALSE = false;
function __construct(object $object, ?string $name=null, ?array $params=null) {
function __construct(?object $dest, ?string $name=null, ?array $params=null) {
parent::__construct($params);
$this->object = $object;
$this->name = $name;
$class = new ReflectionClass($object);
try {
$property = $class->getProperty($this->name);
$property->setAccessible(true);
} catch (ReflectionException $e) {
$property = null;
}
$this->property = $property;
$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 object $object;
protected bool $protectDest;
protected ?object $dest;
protected bool $mapNames;
protected ?string $name;
protected ?ReflectionProperty $property;
function reset(object $object, ?string $name=null): self {
$this->object = $object;
private function _setName(?string $name): void {
if ($this->mapNames) $name = str::us2camel($name);
$this->name = $name;
}
private function _setDest(?object $dest): void {
$this->dest = $dest;
$property = null;
$name = $this->name;
if ($dest !== null && $name !== null) {
$class = new ReflectionClass($dest);
try {
$property = $class->getProperty($name);
$property->setAccessible(true);
} catch (ReflectionException $e) {
}
}
$this->property = $property;
}
function reset(?object $dest, ?string $name=null): self {
$this->_setName($name);
$this->_setDest($dest);
return $this;
}
function resetKey($name=null): self {
$this->name = $name;
$this->_setName($name);
return $this;
}
@ -48,20 +69,23 @@ class PropertyAccess extends AbstractAccess {
function exists(): bool {
$name = $this->name;
if ($this->dest === null) return false;
return $name === null
|| $this->property !== null
|| property_exists($this->object, $name);
|| property_exists($this->dest, $name);
}
protected function _get($default=null) {
$name = $this->name;
$property = $this->property;
if ($name === null) {
return $this->object;
if ($this->dest === null) {
return $default;
} elseif ($name === null) {
return $this->dest;
} elseif ($property !== null) {
return $property->getValue($this->object);
} elseif (property_exists($this->object, $name)) {
return $this->object->$name;
return $property->getValue($this->dest);
} elseif (property_exists($this->dest, $name)) {
return $this->dest->$name;
} else {
return $default;
}
@ -88,20 +112,14 @@ class PropertyAccess extends AbstractAccess {
protected function _set($value): void {
$name = $this->name;
$property = $this->property;
if ($name === null) {
$this->object = $value;
$class = new ReflectionClass($value);
try {
$property = $class->getProperty($this->name);
$property->setAccessible(true);
} catch (ReflectionException $e) {
$property = null;
}
$this->property = $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->object, $value);
$property->setValue($this->dest, $value);
} else {
$this->object->$name = $value;
$this->dest->$name = $value;
}
}
@ -114,6 +132,7 @@ class PropertyAccess extends AbstractAccess {
}
function addKey($key): IAccess {
if ($key === null) return $this;
return new ChainAccess($this, $key);
}
}

View File

@ -1,8 +1,5 @@
# nulib\schema
* faire PropertyAccess
* si l'argument de Input() est un objet, utiliser PropertyAccess au lieu de KeyAccess
* possibilité de forcer l'un ou l'autre (paramètre type=value|array|object)
* ensureKeys() et orderKeys() se fait au niveau de access (ou input?)
* valeurs composite/computed

View File

@ -57,8 +57,9 @@ class AssocWrapper extends Wrapper {
else $input = $this->newInput($value);
$context->input = $input;
$context->valueKey = $valueKey;
foreach ($context->keyWrappers as $key => $wrapper) {
$wrapper->reset($input->subInput($valueKey), $key);
foreach ($context->keyWrappers as $key => $keyWrapper) {
$keyInput = $input->addKey($valueKey);
$keyWrapper->reset($keyInput, $key, ["analyze" => false]);
}
$this->afterModify($params, true);
return $this;
@ -88,25 +89,34 @@ class AssocWrapper extends Wrapper {
*/
static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int {
if ($context->ensureArray) {
$array = $context->input->get();
if ($array === null) $context->input->set([]);
$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 $wrapper) {
$wrapper->analyze($params);
if (!$wrapper->isValid()) {
foreach ($context->keyWrappers as $keyWrapper) {
$keyWrapper->analyze($params);
if (!$keyWrapper->isValid()) {
$what = ref_analyze::INVALID;
$result->addInvalidMessage($wrapper);
$result->addInvalidMessage($keyWrapper);
}
}
return $what;
}
/**
* @param AssocWrapperContext $context
* @param AssocWrapper $wrapper
*/
static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool {
return ScalarWrapper::_normalize($context, $wrapper, $params);
$modified = ScalarWrapper::_normalize($context, $wrapper, $params);
foreach ($context->keyWrappers as $keyWrapper) {
if ($keyWrapper->normalize($params)) $modified = true;
}
return $modified;
}
function getResult($key=false): Result {

View File

@ -204,7 +204,7 @@ class ScalarWrapper extends Wrapper {
/** @var ScalarResult $result */
$result = $context->result;
$verifix = false;
$normalize = false;
$modified = false;
if ($result->resultAvailable) {
if ($result->null) {
@ -215,19 +215,19 @@ class ScalarWrapper extends Wrapper {
$normalizedValue = $result->normalizedValue;
if ($normalizedValue !== null) {
# la valeur normalisée est disponible
$input->set($normalizedValue);
$input->set($normalizedValue, $valueKey);
$result->normalizedValue = null;
$modified = true;
} else {
# normaliser la valeur
$verifix = true;
$normalize = true;
}
}
} else {
$verifix = true;
$normalize = true;
}
if ($verifix) {
if ($normalize) {
$value = $input->get($valueKey);
/** @var func $normalizerFunc */
$normalizerFunc = $schema->normalizerFunc;
@ -236,7 +236,7 @@ class ScalarWrapper extends Wrapper {
$value = $normalizerFunc->invoke([$orig, $context, $wrapper]);
$modified = $value !== $orig;
} else {
$modified = $context->type->verifix($value, $result, $schema);
$modified = $context->type->normalize($value, $result, $schema);
}
if ($result->valid) $input->set($value, $valueKey);
}

View File

@ -1,5 +0,0 @@
<?php
namespace nur\sery\wip\schema\input;
class ChainInput implements IInput {
}

View File

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

View File

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

View File

@ -1,18 +0,0 @@
<?php
namespace nur\sery\wip\schema\input;
interface IInput {
/** tester si la valeur existe sans tenir compte de $allowEmpty */
function isPresent($key=null): bool;
/** tester si la valeur est disponible en tenant compte de $allowEmpty */
function isAvailable($key=null): bool;
function get($key=null);
function set($value, $key=null): void;
function unset($key=null): void;
function addKey($key): IInput;
}

View File

@ -1,6 +1,7 @@
<?php
namespace nur\sery\wip\schema\input;
use nulib\StateException;
use nur\sery\wip\php\access\IAccess;
use nur\sery\wip\php\access\KeyAccess;
use nur\sery\wip\php\access\PropertyAccess;
@ -10,20 +11,34 @@ use nur\sery\wip\php\access\PropertyAccess;
*
* cette implémentation lit depuis et écrit dans une référence
*/
class Input implements IInput {
class Input {
const ALLOW_EMPTY = true;
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(&$dest=null, ?array $params=null) {
if (is_object($dest)) {
$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;
if ($accessType == self::ACCESS_PROPERTY) {
$this->access = new PropertyAccess($dest, null, [
"allow_empty" => $params["allow_empty"] ?? static::ALLOW_EMPTY,
"allow_empty" => $allowEmpty,
"allow_null" => true,
]);
} elseif ($accessType == self::ACCESS_KEY) {
$this->access = new KeyAccess($dest, null, [
"allow_empty" => $allowEmpty,
"allow_null" => true,
]);
} else {
$this->access = new KeyAccess($dest, null, [
"allow_empty" => $params["allow_empty"] ?? static::ALLOW_EMPTY,
"allow_null" => true,
]);
throw self::unexpected_access_type();
}
}
@ -51,7 +66,10 @@ class Input implements IInput {
$this->access->resetKey($key)->del();
}
function addKey($key): IInput {
return new ChainInput($this, $key);
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
*/
class PostInput extends FormInput {
protected function formAccess($key): IAccess {
return new PostAccess($key, [
"allow_empty" => $this->allowEmpty,
protected function formAccess(IAccess $access): IAccess {
return new PostAccess(null, [
"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
* 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
* aussi être retournées, bien qu'elles ne soient pas à proprement parler des
@ -106,7 +106,7 @@ interface IType {
* si la valeur était déjà normalisée, ou si une erreur s'est produite,
* retourner false.
*/
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

View File

@ -49,7 +49,7 @@ class tarray extends _tstring {
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
function normalize(&$value, Result $result, Schema $schema): bool {
if (is_array($value)) {
$result->setNormalized();
return false;

View File

@ -99,7 +99,7 @@ class tbool extends _tformatable {
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
function normalize(&$value, Result $result, Schema $schema): bool {
if (is_bool($value)) {
$result->setNormalized();
return false;

View File

@ -43,7 +43,7 @@ class tcallable extends _tsimple {
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
function normalize(&$value, Result $result, Schema $schema): bool {
if ($value instanceof func) {
$result->setNormalized();
return false;

View File

@ -36,7 +36,7 @@ abstract class tcontent extends _tunion {
* @var ScalarResult $result
* @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)) {
$result->setNormalized();
return false;

View File

@ -43,7 +43,7 @@ class tfloat extends _tformatable {
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
function normalize(&$value, Result $result, Schema $schema): bool {
if (is_float($value)) {
$result->setNormalized();
return false;

View File

@ -41,7 +41,7 @@ class tgeneric extends _tsimple {
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
function normalize(&$value, Result $result, Schema $schema): bool {
$result->setNormalized();
return false;
}

View File

@ -45,7 +45,7 @@ class tint extends _tformatable {
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
function normalize(&$value, Result $result, Schema $schema): bool {
if (is_int($value)) {
$result->setNormalized();
return false;

View File

@ -36,7 +36,7 @@ class tkey extends _tunion {
* @var ScalarResult $result
* @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)) {
$result->setNormalized();
return false;

View File

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

View File

@ -41,7 +41,7 @@ class tpkey extends _tunion {
* @var ScalarResult $result
* @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)) {
$result->setNormalized();
return false;

View File

@ -44,7 +44,7 @@ class trawstring extends _tstring {
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
function normalize(&$value, Result $result, Schema $schema): bool {
if (is_string($value)) {
$result->setNormalized();
return false;

View File

@ -120,20 +120,36 @@ class AssocSchemaTest extends TestCase {
"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" => null,
"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" => null,
"b" => false,
], $array);
$array = ["a" => " string "];
@ -141,5 +157,12 @@ class AssocSchemaTest extends TestCase {
self::assertSame([
"a" => "string",
], $array);
$array = ["c" => false, "a" => " string "];
$schema->getWrapper($array, null, ["ensure_keys" => false]);
self::assertSame([
"a" => "string",
"c" => false,
], $array);
}
}