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) { switch ($nature[0] ?? null) {
case "assoc": case "assoc":
foreach ($definition["schema"] as $definitionKey => &$definitionValue) { foreach ($definition["schema"] as $definitionKey => &$subdef) {
self::_normalize($definitionValue, $definitionKey); $subdef = self::_normalize($subdef, $definitionKey);
}; }; unset($subdef);
unset($definitionValue);
break; break;
case "list": case "list":
self::_normalize($definition["schema"]); 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 * @var array définition du schéma, à redéfinir le cas échéant dans une classe
* dérivée * dérivée
@ -215,15 +245,15 @@ abstract class Schema implements ArrayAccess {
protected array $definition; protected array $definition;
function getDefinition(): array { function getDefinition(): array {
return $this->definition; return self::_ensure_schema_array($this->definition);
} }
/** 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 &$assoc=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 */
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 */ /** 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; 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; use nur\sery\wip\schema\types\IType;
abstract class Wrapper implements ArrayAccess, IteratorAggregate { abstract class Wrapper implements ArrayAccess, IteratorAggregate {
function isAssoc(?AssocWrapper &$assoc=null): bool { return false; } function isAssoc(?AssocWrapper &$wrapper=null): bool { return false; }
function isList(?ListWrapper &$list=null): bool { return false; } function isList(?ListWrapper &$wrapper=null): bool { return false; }
function isScalar(?ScalarWrapper &$scalar=null): bool { return false; } function isScalar(?ScalarWrapper &$wrapper=null): bool { return false; }
/** spécifier la valeur destination gérée par cet objet */ /** spécifier la valeur destination gérée par cet objet */
abstract function reset(&$value, $valueKey=null, ?bool $verifix=null): self; 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; 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;} 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\cl;
use nulib\ref\schema\ref_schema; 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\Schema;
use nur\sery\wip\schema\SchemaException;
use nur\sery\wip\schema\types;
use nur\sery\wip\schema\Wrapper; use nur\sery\wip\schema\Wrapper;
/** /**
@ -59,13 +55,17 @@ class AssocSchema extends Schema {
$this->definition = $definition; $this->definition = $definition;
} }
function isAssoc(?AssocSchema &$assoc=null): bool { function isAssoc(?AssocSchema &$schema=null): bool {
$assoc = $this; $schema = $this;
return true; return true;
} }
function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): AssocWrapper { protected function newWrapper(): AssocWrapper {
if ($wrapper instanceof AssocWrapper) return $wrapper->reset($value, $valueKey); return new AssocWrapper($this);
else return ($wrapper = new AssocWrapper($this, $value, $valueKey)); }
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 <?php
namespace nur\sery\wip\schema\_assoc; 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\Result;
use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\Wrapper; use nur\sery\wip\schema\Wrapper;
abstract/*XXX*/ class AssocWrapper extends Wrapper { class AssocWrapper extends Wrapper {
function isAssoc(?AssocWrapper &$assoc=null): bool { $assoc = $this; return true; } 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 ensureKeys(): bool {
} }
function orderKeys(): 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; 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\_scalar\ScalarSchema;
use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\SchemaException;
use nur\sery\wip\schema\Wrapper; use nur\sery\wip\schema\Wrapper;
class ListSchema extends Schema { class ListSchema extends Schema {
@ -58,13 +55,17 @@ class ListSchema extends Schema {
$this->definition = $definition; $this->definition = $definition;
} }
function isList(?ListSchema &$list=null): bool { function isList(?ListSchema &$schema=null): bool {
$list = $this; $schema = $this;
return true; return true;
} }
protected function newWrapper(): ListWrapper {
return new ListWrapper($this);
}
function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ListWrapper { function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ListWrapper {
if ($wrapper instanceof ListWrapper) return $wrapper->reset($value, $valueKey); if (!($wrapper instanceof ListWrapper)) $wrapper = $this->newWrapper();
else return ($wrapper = new ListWrapper($this, $value, $valueKey)); return $wrapper->reset($value, $valueKey);
} }
} }

View File

@ -5,7 +5,7 @@ use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Wrapper; use nur\sery\wip\schema\Wrapper;
abstract/*XXX*/ class ListWrapper extends 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 { function ensureKeys(): bool {
} }

View File

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

View File

@ -27,7 +27,7 @@ class ScalarWrapper extends Wrapper {
$this->throw = $throw ?? true; $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 */ /** schéma de cette valeur */
protected ScalarSchema $schema; 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 nulib\tests\TestCase;
use nur\sery\wip\schema\SchemaException; use nur\sery\wip\schema\SchemaException;
use nur\sery\wip\schema\types\IType;
class ScalarSchemaTest extends TestCase { class ScalarSchemaTest extends TestCase {
const NULL_SCHEMA = [ const NULL_SCHEMA = [
@ -32,13 +31,6 @@ class ScalarSchemaTest extends TestCase {
return array_merge(self::NULL_SCHEMA, $schema); 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() { function testNormalize() {
self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize(null)); self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize(null));
self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([])); self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([]));
@ -51,22 +43,22 @@ class ScalarSchemaTest extends TestCase {
}); });
$string = self::schema(["type" => ["string"], "nullable" => false]); $string = self::schema(["type" => ["string"], "nullable" => false]);
self::assertSame($string, self::sanitize(ScalarSchema::normalize("string"))); self::assertSame($string, ScalarSchema::normalize("string"));
self::assertSame($string, self::sanitize(ScalarSchema::normalize(["string"]))); self::assertSame($string, ScalarSchema::normalize(["string"]));
$nstring = self::schema(["type" => ["string"]]); $nstring = self::schema(["type" => ["string"]]);
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize(["?string"]))); self::assertSame($nstring, ScalarSchema::normalize(["?string"]));
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize(["?string|null"]))); self::assertSame($nstring, ScalarSchema::normalize(["?string|null"]));
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize(["string|null"]))); self::assertSame($nstring, ScalarSchema::normalize(["string|null"]));
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize([["?string", "null"]]))); self::assertSame($nstring, ScalarSchema::normalize([["?string", "null"]]));
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize([["string", "null"]]))); self::assertSame($nstring, ScalarSchema::normalize([["string", "null"]]));
self::assertSame($nstring, self::sanitize(ScalarSchema::normalize([["string", null]]))); self::assertSame($nstring, ScalarSchema::normalize([["string", null]]));
$key = self::schema(["type" => ["string", "int"], "nullable" => false]); $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]); $nkey = self::schema(["type" => ["string", "int"], "nullable" => true]);
self::assertSame($nkey, self::sanitize(ScalarSchema::normalize("?string|int"))); self::assertSame($nkey, ScalarSchema::normalize("?string|int"));
self::assertSame($nkey, self::sanitize(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 nur\sery\wip\schema\input\Input;
use stdClass; use stdClass;
class ScalarValueTest extends TestCase { class ScalarWrapperTest extends TestCase {
function checkValue(ScalarWrapper $wrapper, $dest, bool $present, bool $available, bool $valid, bool $normalized): void { function checkValue(ScalarWrapper $wrapper, $value, bool $present, bool $available, bool $valid, bool $normalized): void {
self::assertSame($dest, $wrapper->get(), "value"); self::assertSame($value, $wrapper->get(), "value");
self::assertSame($present, $wrapper->isPresent(), "present"); self::assertSame($present, $wrapper->isPresent(), "present");
self::assertSame($available, $wrapper->isAvailable(), "available"); self::assertSame($available, $wrapper->isAvailable(), "available");
self::assertSame($valid, $wrapper->isValid(), "valid"); self::assertSame($valid, $wrapper->isValid(), "valid");
self::assertSame($normalized, $wrapper->isNormalized(), "normalized"); 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 { function checkVerifix(ScalarSchema $schema, $orig, bool $verifix, $value, bool $present, bool $available, bool $valid, bool $normalized, ?array $inputParams=null): void {
$value = $schema->getWrapper(); $wrapper = $schema->getWrapper();
if ($inputParams !== null) $input = new Input($src, $inputParams); if ($inputParams !== null) $input = new Input($orig, $inputParams);
else $input = $src; else $input = $orig;
$value->reset($input, null, $verifix); $wrapper->reset($input, null, $verifix);
$this->checkValue($value, $dest, $present, $available, $valid, $normalized); $this->checkValue($wrapper, $value, $present, $available, $valid, $normalized);
} }
function checkException(ScalarSchema $schema, $src, bool $verifix, string $exceptionClass, ?array $inputParams=null) { function checkException(ScalarSchema $schema, $orig, bool $verifix, string $exceptionClass, ?array $inputParams=null) {
$value = $schema->getWrapper(); $wrapper = $schema->getWrapper();
if ($inputParams !== null) $src = new Input($src, $inputParams); if ($inputParams !== null) $orig = new Input($orig, $inputParams);
self::assertException($exceptionClass, function() use ($value, &$src, $verifix) { self::assertException($exceptionClass, function() use ($wrapper, &$orig, $verifix) {
$value->reset($src, null, $verifix); $wrapper->reset($orig, null, $verifix);
}); });
} }