début assocSchema

This commit is contained in:
Jephté Clain 2025-03-10 16:40:55 +04:00
parent 9cf0e8045a
commit 4b84f11f99
12 changed files with 289 additions and 75 deletions

View File

@ -152,10 +152,9 @@ abstract class Schema implements ArrayAccess {
switch ($nature[0] ?? null) {
case "assoc":
foreach ($definition["schema"] as $definitionKey => &$definitionValue) {
self::_normalize($definitionValue, $definitionKey);
};
unset($definitionValue);
foreach ($definition["schema"] as $definitionKey => &$subdef) {
$subdef = self::_normalize($subdef, $definitionKey);
}; unset($subdef);
break;
case "list":
self::_normalize($definition["schema"]);
@ -206,6 +205,37 @@ abstract class Schema implements ArrayAccess {
}
}
protected static function _ensure_schema_instances(array $definition): array {
switch ($definition[""][0]) {
case "assoc":
foreach ($definition["schema"] as &$subdef) {
self::_ensure_schema_instances($subdef);
Schema::ns($subdef);
}; unset($subdef);
break;
case "list":
Schema::ns($definition["schema"]);
break;
}
return $definition;
}
protected static function _ensure_schema_array(array $definition): array {
switch ($definition[""][0]) {
case "assoc":
foreach ($definition["schema"] as &$subdef) {
if ($subdef instanceof Schema) $subdef = $subdef->getDefinition();
}; unset($subdef);
break;
case "list":
$subdef = $definition["schema"];
if ($subdef instanceof Schema) $subdef = $subdef->getDefinition();
$definition["schema"] = $subdef;
break;
}
return $definition;
}
/**
* @var array définition du schéma, à redéfinir le cas échéant dans une classe
* dérivée
@ -215,15 +245,15 @@ abstract class Schema implements ArrayAccess {
protected array $definition;
function getDefinition(): array {
return $this->definition;
return self::_ensure_schema_array($this->definition);
}
/** retourner true si le schéma est de nature tableau associatif */
function isAssoc(?AssocSchema &$assoc=null): bool { return false; }
function isAssoc(?AssocSchema &$schema=null): bool { return false; }
/** retourner true si le schéma est de nature liste */
function isList(?ListSchema &$list=null): bool { return false; }
function isList(?ListSchema &$schema=null): bool { return false; }
/** retourner true si le schéma est de nature scalaire */
function isScalar(?ScalarSchema &$scalar=null): bool { return false; }
function isScalar(?ScalarSchema &$schema=null): bool { return false; }
abstract function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): Wrapper;

View File

@ -9,9 +9,9 @@ use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\types\IType;
abstract class Wrapper implements ArrayAccess, IteratorAggregate {
function isAssoc(?AssocWrapper &$assoc=null): bool { return false; }
function isList(?ListWrapper &$list=null): bool { return false; }
function isScalar(?ScalarWrapper &$scalar=null): bool { return false; }
function isAssoc(?AssocWrapper &$wrapper=null): bool { return false; }
function isList(?ListWrapper &$wrapper=null): bool { return false; }
function isScalar(?ScalarWrapper &$wrapper=null): bool { return false; }
/** spécifier la valeur destination gérée par cet objet */
abstract function reset(&$value, $valueKey=null, ?bool $verifix=null): self;

View File

@ -3,6 +3,6 @@ namespace nur\sery\wip\schema\_assoc;
use nur\sery\wip\schema\Result;
abstract/*XXX*/ class AssocResult extends Result {
class AssocResult extends Result {
function isAssoc(?AssocResult &$assoc=null): bool { $assoc = $this; return true;}
}

View File

@ -3,11 +3,7 @@ namespace nur\sery\wip\schema\_assoc;
use nulib\cl;
use nulib\ref\schema\ref_schema;
use nulib\ValueException;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\SchemaException;
use nur\sery\wip\schema\types;
use nur\sery\wip\schema\Wrapper;
/**
@ -59,13 +55,17 @@ class AssocSchema extends Schema {
$this->definition = $definition;
}
function isAssoc(?AssocSchema &$assoc=null): bool {
$assoc = $this;
function isAssoc(?AssocSchema &$schema=null): bool {
$schema = $this;
return true;
}
function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): AssocWrapper {
if ($wrapper instanceof AssocWrapper) return $wrapper->reset($value, $valueKey);
else return ($wrapper = new AssocWrapper($this, $value, $valueKey));
protected function newWrapper(): AssocWrapper {
return new AssocWrapper($this);
}
function getWrapper(&$array=null, $arrayKey=null, ?Wrapper &$wrapper=null): AssocWrapper {
if (!($wrapper instanceof AssocWrapper)) $wrapper = $this->newWrapper();
return $wrapper->reset($array, $arrayKey);
}
}

View File

@ -1,18 +1,135 @@
<?php
namespace nur\sery\wip\schema\_assoc;
use nulib\ValueException;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\Wrapper;
abstract/*XXX*/ class AssocWrapper extends Wrapper {
function isAssoc(?AssocWrapper &$assoc=null): bool { $assoc = $this; return true; }
class AssocWrapper extends Wrapper {
function __construct(AssocSchema $schema, &$array=null, $arrayKey=null, ?array $params=null) {
$verifix = $params["verifix"] ?? true;
$throw = $params["throw"] ?? null;
if ($array !== null && $throw === null) {
# Si $value est null, ne pas lancer d'exception, parce qu'on considère que
# c'est une initialisation sans conséquences
$throw = true;
}
$this->schema = $schema;
$this->verifix = $verifix;
$this->throw = $throw ?? false;
$this->result = new ScalarResult();
$this->reset($array, $arrayKey);
$this->throw = $throw ?? true;
}
function isAssoc(?AssocWrapper &$wrapper=null): bool { $wrapper = $this; return true; }
protected bool $verifix;
protected bool $throw;
/** schéma de ce tableau */
protected AssocSchema $schema;
/** source et destination de la valeur */
protected Input $input;
/** @var string|int|null clé du tableau dans le tableau destination */
protected $arrayKey;
protected IType $type;
protected ScalarResult $result;
protected ?array $keys;
protected ?array $wrappers;
/** résultat de l'analyse de la valeur */
protected ?array $results;
protected function newInput(&$value): Input {
return new Input($value);
}
function reset(&$array, $arrayKey=null, ?bool $verifix=null): Wrapper {
if ($array instanceof Input) $input = $array;
else $input = $this->newInput($array);
$this->input = $input;
$this->arrayKey = $arrayKey;
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function getKeys(): array {
return $this->keys;
}
function getWrapper($key=null): ScalarWrapper {
$wrapper = $this->wrappers[$key] ?? null;
if ($key !== null) return $wrapper;
throw ValueException::invalid_key($key);
}
/** @param Result[] $results */
function verifix(?bool $throw=null, ?array &$results=null): bool {
}
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->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;
}
function unset(?bool $verifix=null): AssocWrapper {
$this->input->unset($this->arrayKey);
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function format($format = null): string {
// TODO: Implement format() method.
}
function ensureKeys(): bool {
}
function orderKeys(): bool {
}
/** @param Result[] $results */
function verifix(bool $throw=true, ?array &$results=null): bool {
}
}

View File

@ -2,10 +2,7 @@
namespace nur\sery\wip\schema\_list;
use nulib\ref\schema\ref_schema;
use nulib\ValueException;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\SchemaException;
use nur\sery\wip\schema\Wrapper;
class ListSchema extends Schema {
@ -58,13 +55,17 @@ class ListSchema extends Schema {
$this->definition = $definition;
}
function isList(?ListSchema &$list=null): bool {
$list = $this;
function isList(?ListSchema &$schema=null): bool {
$schema = $this;
return true;
}
protected function newWrapper(): ListWrapper {
return new ListWrapper($this);
}
function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ListWrapper {
if ($wrapper instanceof ListWrapper) return $wrapper->reset($value, $valueKey);
else return ($wrapper = new ListWrapper($this, $value, $valueKey));
if (!($wrapper instanceof ListWrapper)) $wrapper = $this->newWrapper();
return $wrapper->reset($value, $valueKey);
}
}

View File

@ -5,7 +5,7 @@ use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Wrapper;
abstract/*XXX*/ class ListWrapper extends Wrapper {
function isList(?ListWrapper &$list=null): bool { $list = $this; return true; }
function isList(?ListWrapper &$wrapper=null): bool { $wrapper = $this; return true; }
function ensureKeys(): bool {
}

View File

@ -75,8 +75,8 @@ class ScalarSchema extends Schema {
}
static function normalize($definition, $definitionKey=null): array {
self::_normalize($definition, $definitionKey);
self::_ensure_nature($definition, "scalar", null);
$definition = self::_normalize($definition, $definitionKey);
self::_ensure_nature($definition, "scalar");
return $definition;
}
@ -87,14 +87,18 @@ class ScalarSchema extends Schema {
$this->definition = $definition;
}
function isScalar(?ScalarSchema &$scalar=null): bool {
$scalar = $this;
function isScalar(?ScalarSchema &$schema=null): bool {
$schema = $this;
return true;
}
protected function newWrapper(): ScalarWrapper {
return new ScalarWrapper($this);
}
function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ScalarWrapper {
if ($wrapper instanceof ScalarWrapper) return $wrapper->reset($value, $valueKey);
else return ($wrapper = new ScalarWrapper($this, $value, $valueKey));
if (!($wrapper instanceof ScalarWrapper)) $wrapper = $this->newWrapper();
return $wrapper->reset($value, $valueKey);
}
#############################################################################

View File

@ -27,7 +27,7 @@ class ScalarWrapper extends Wrapper {
$this->throw = $throw ?? true;
}
function isScalar(?ScalarWrapper &$scalar=null): bool { $scalar = $this; return true; }
function isScalar(?ScalarWrapper &$wrapper=null): bool { $wrapper = $this; return true; }
/** schéma de cette valeur */
protected ScalarSchema $schema;

View File

@ -0,0 +1,70 @@
<?php
namespace nur\sery\wip\schema\_assoc;
use nulib\tests\TestCase;
use nur\sery\wip\schema\_scalar\ScalarSchemaTest;
use nur\sery\wip\schema\types\IType;
class AssocSchemaTest extends TestCase {
const NULL_SCHEMA = [
"" => ["assoc"],
"schema" => null,
"type" => [null],
"default" => null,
"title" => null,
"required" => false,
"nullable" => true,
"desc" => null,
"analyzer_func" => null,
"extractor_func" => null,
"parser_func" => null,
"normalizer_func" => null,
"messages" => null,
"formatter_func" => null,
"format" => null,
"name" => null,
"pkey" => null,
"header" => null,
"composite" => null,
];
static function schema(array $definition, array $keyDefinitions): array {
$definition = array_merge(self::NULL_SCHEMA, $definition, ["schema" => []]);
foreach ($keyDefinitions as $key => $keydef) {
$definition["schema"][$key] = array_merge(ScalarSchemaTest::NULL_SCHEMA, $keydef);
}; unset($subdef);
return $definition;
}
function testNormalize() {
self::assertSame(self::schema([
"type" => ["array"], "nullable" => true,
], [
"a" => [
"type" => ["string"], "nullable" => false,
"name" => "a", "pkey" => "a", "header" => "a",
],
]), AssocSchema::normalize(["a" => "string"]));
self::assertSame(self::schema([
"type" => ["array"], "nullable" => true,
], [
"a" => [
"type" => ["string"], "nullable" => false,
"name" => "a", "pkey" => "a", "header" => "a",
],
"b" => [
"type" => ["int"], "nullable" => false,
"name" => "b", "pkey" => "b", "header" => "b",
],
"c" => [
"type" => ["bool"], "nullable" => false,
"name" => "c", "pkey" => "c", "header" => "c",
],
]), AssocSchema::normalize([
"a" => "string",
"b" => "int",
"c" => "bool",
]));
}
}

View File

@ -3,7 +3,6 @@ namespace nur\sery\wip\schema\_scalar;
use nulib\tests\TestCase;
use nur\sery\wip\schema\SchemaException;
use nur\sery\wip\schema\types\IType;
class ScalarSchemaTest extends TestCase {
const NULL_SCHEMA = [
@ -32,13 +31,6 @@ class ScalarSchemaTest extends TestCase {
return array_merge(self::NULL_SCHEMA, $schema);
}
static function sanitize(array $schema): array {
if ($schema["type"] instanceof IType) {
$schema["type"] = [$schema["type"]->getClass()];
}
return $schema;
}
function testNormalize() {
self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize(null));
self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([]));
@ -51,22 +43,22 @@ class ScalarSchemaTest extends TestCase {
});
$string = self::schema(["type" => ["string"], "nullable" => false]);
self::assertSame($string, self::sanitize(ScalarSchema::normalize("string")));
self::assertSame($string, self::sanitize(ScalarSchema::normalize(["string"])));
self::assertSame($string, ScalarSchema::normalize("string"));
self::assertSame($string, ScalarSchema::normalize(["string"]));
$nstring = self::schema(["type" => ["string"]]);
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize(["?string"])));
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize(["?string|null"])));
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize(["string|null"])));
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize([["?string", "null"]])));
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize([["string", "null"]])));
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize([["string", null]])));
self::assertSame($nstring, ScalarSchema::normalize(["?string"]));
self::assertSame($nstring, ScalarSchema::normalize(["?string|null"]));
self::assertSame($nstring, ScalarSchema::normalize(["string|null"]));
self::assertSame($nstring, ScalarSchema::normalize([["?string", "null"]]));
self::assertSame($nstring, ScalarSchema::normalize([["string", "null"]]));
self::assertSame($nstring, ScalarSchema::normalize([["string", null]]));
$key = self::schema(["type" => ["string", "int"], "nullable" => false]);
self::assertSame($key, self::sanitize(ScalarSchema::normalize("string|int")));
self::assertSame($key, ScalarSchema::normalize("string|int"));
$nkey = self::schema(["type" => ["string", "int"], "nullable" => true]);
self::assertSame($nkey, self::sanitize(ScalarSchema::normalize("?string|int")));
self::assertSame($nkey, self::sanitize(ScalarSchema::normalize("string|?int")));
self::assertSame($nkey, ScalarSchema::normalize("?string|int"));
self::assertSame($nkey, ScalarSchema::normalize("string|?int"));
}
}

View File

@ -6,28 +6,28 @@ use nulib\ValueException;
use nur\sery\wip\schema\input\Input;
use stdClass;
class ScalarValueTest extends TestCase {
function checkValue(ScalarWrapper $wrapper, $dest, bool $present, bool $available, bool $valid, bool $normalized): void {
self::assertSame($dest, $wrapper->get(), "value");
class ScalarWrapperTest extends TestCase {
function checkValue(ScalarWrapper $wrapper, $value, bool $present, bool $available, bool $valid, bool $normalized): void {
self::assertSame($value, $wrapper->get(), "value");
self::assertSame($present, $wrapper->isPresent(), "present");
self::assertSame($available, $wrapper->isAvailable(), "available");
self::assertSame($valid, $wrapper->isValid(), "valid");
self::assertSame($normalized, $wrapper->isNormalized(), "normalized");
}
function checkVerifix(ScalarSchema $schema, $src, bool $verifix, $dest, bool $present, bool $available, bool $valid, bool $normalized, ?array $inputParams=null): void {
$value = $schema->getWrapper();
if ($inputParams !== null) $input = new Input($src, $inputParams);
else $input = $src;
$value->reset($input, null, $verifix);
$this->checkValue($value, $dest, $present, $available, $valid, $normalized);
function checkVerifix(ScalarSchema $schema, $orig, bool $verifix, $value, bool $present, bool $available, bool $valid, bool $normalized, ?array $inputParams=null): void {
$wrapper = $schema->getWrapper();
if ($inputParams !== null) $input = new Input($orig, $inputParams);
else $input = $orig;
$wrapper->reset($input, null, $verifix);
$this->checkValue($wrapper, $value, $present, $available, $valid, $normalized);
}
function checkException(ScalarSchema $schema, $src, bool $verifix, string $exceptionClass, ?array $inputParams=null) {
$value = $schema->getWrapper();
if ($inputParams !== null) $src = new Input($src, $inputParams);
self::assertException($exceptionClass, function() use ($value, &$src, $verifix) {
$value->reset($src, null, $verifix);
function checkException(ScalarSchema $schema, $orig, bool $verifix, string $exceptionClass, ?array $inputParams=null) {
$wrapper = $schema->getWrapper();
if ($inputParams !== null) $orig = new Input($orig, $inputParams);
self::assertException($exceptionClass, function() use ($wrapper, &$orig, $verifix) {
$wrapper->reset($orig, null, $verifix);
});
}