From 9cf0e8045a27578770537f4c00dbc56ad006ed82 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 10 Mar 2025 11:08:36 +0400 Subject: [PATCH] modifs.mineures sans commentaires --- src/schema/README.md | 1 - src/schema/Schema.php | 155 ++++++++++++++++++++++++++++ src/schema/_assoc/AssocSchema.php | 42 +++++--- src/schema/_list/ListSchema.php | 37 +++++-- src/schema/_scalar/ScalarSchema.php | 109 +------------------ 5 files changed, 214 insertions(+), 130 deletions(-) diff --git a/src/schema/README.md b/src/schema/README.md index 200cee4..168c918 100644 --- a/src/schema/README.md +++ b/src/schema/README.md @@ -151,7 +151,6 @@ récursivement, avec cependant l'ajout de quelques clés supplémentaires: ~~~php VALUE_SCHEMA = [ ... - "key" => "nom du champ auquel assigner la valeur de la clé", #XXX "name" => "identifiant de la valeur", "pkey" => "chemin de clé de la valeur dans le tableau associatif", ]; diff --git a/src/schema/Schema.php b/src/schema/Schema.php index 5c15209..113a857 100644 --- a/src/schema/Schema.php +++ b/src/schema/Schema.php @@ -4,9 +4,17 @@ namespace nur\sery\wip\schema; use ArrayAccess; use nulib\AccessException; use nulib\cl; +use nulib\ref\schema\ref_schema; +use nulib\ref\schema\ref_types; use nur\sery\wip\schema\_assoc\AssocSchema; use nur\sery\wip\schema\_list\ListSchema; use nur\sery\wip\schema\_scalar\ScalarSchema; +use nur\sery\wip\schema\types\tarray; +use nur\sery\wip\schema\types\tbool; +use nur\sery\wip\schema\types\tcallable; +use nur\sery\wip\schema\types\tcontent; +use nur\sery\wip\schema\types\tpkey; +use nur\sery\wip\schema\types\trawstring; abstract class Schema implements ArrayAccess { /** @@ -51,6 +59,153 @@ abstract class Schema implements ArrayAccess { return self::ns($schema, $definition)->getWrapper($value, $valueKey, $wrapper); } + protected static function _normalize($definition, $definitionKey=null): array { + if (!is_array($definition)) $definition = [$definition]; + # s'assurer que toutes les clés existent avec leur valeur par défaut + $index = 0; + foreach (array_keys(ref_schema::SCALAR_METASCHEMA) as $key) { + if (!array_key_exists($key, $definition)) { + if (array_key_exists($index, $definition)) { + $definition[$key] = $definition[$index]; + unset($definition[$index]); + $index++; + } else { + $definition[$key] = ref_schema::SCALAR_METASCHEMA[$key][1]; + } + } + } + # réordonner les clés numériques + if (cl::have_num_keys($definition)) { + $keys = array_keys($definition); + $index = 0; + foreach ($keys as $key) { + if (!is_int($key)) continue; + if ($key !== $index) { + $definition[$index] = $definition[$key]; + unset($definition[$key]); + } + $index++; + } + } + # type + $types = []; + $deftype = $definition["type"]; + $nullable = $definition["nullable"] ?? false; + if ($deftype === null) { + $types[] = null; + $nullable = true; + } else { + if (!is_array($deftype)) { + if (!is_string($deftype)) throw SchemaException::invalid_type($deftype); + $deftype = explode("|", $deftype); + } + foreach ($deftype as $type) { + if ($type !== null) $type = trim($type); + if ($type === null || $type === "null") { + $nullable = true; + continue; + } + if (!is_string($type)) throw SchemaException::invalid_type($type); + if (substr($type, 0, 1) == "?") { + $type = substr($type, 1); + $nullable = true; + } + if ($type === "") throw SchemaException::invalid_type($type); + $type = cl::get(ref_types::ALIASES, $type, $type); + $types = array_merge($types, explode("|", $type)); + } + if (!$types) throw SchemaException::invalid_schema("scalar: type is required"); + $types = array_keys(array_fill_keys($types, true)); + } + $definition["type"] = $types; + $definition["nullable"] = $nullable; + # nature + $nature = $definition[""]; + tarray::ensure_array($nature); + $definition[""] = $nature; + # name, pkey, header + $name = $definition["name"]; + $pkey = $definition["pkey"]; + $header = $definition["header"]; + if ($name === null) $name = $definitionKey; + trawstring::ensure_nstring($name); + tpkey::ensure_npkey($pkey); + trawstring::ensure_nstring($header); + if ($pkey === null) $pkey = $name; + if ($header === null) $header = $name; + $definition["name"] = $name; + $definition["pkey"] = $pkey; + $definition["header"] = $header; + # autres éléments + tarray::ensure_narray($definition["schema"]); + trawstring::ensure_nstring($definition["title"]); + tbool::ensure_bool($definition["required"]); + tbool::ensure_bool($definition["nullable"]); + tcontent::ensure_ncontent($definition["desc"]); + tcallable::ensure_ncallable($definition["analyzer_func"]); + tcallable::ensure_ncallable($definition["extractor_func"]); + tcallable::ensure_ncallable($definition["parser_func"]); + tcallable::ensure_ncallable($definition["normalizer_func"]); + tarray::ensure_narray($definition["messages"]); + tcallable::ensure_ncallable($definition["formatter_func"]); + tbool::ensure_nbool($definition["composite"]); + + switch ($nature[0] ?? null) { + case "assoc": + foreach ($definition["schema"] as $definitionKey => &$definitionValue) { + self::_normalize($definitionValue, $definitionKey); + }; + unset($definitionValue); + break; + case "list": + self::_normalize($definition["schema"]); + break; + } + + return $definition; + } + + protected static function _ensure_nature(array $definition, string $expectedNature, ?string $expectedType=null): void { + $nature = $definition[""]; + if (!array_key_exists(0, $nature) || $nature[0] !== $expectedNature) { + throw SchemaException::invalid_schema("$nature: invalid nature. expected $expectedNature"); + } + if ($expectedType !== null) { + $types = $definition["type"]; + if (count($types) !== 1 || $types[0] !== $expectedType) { + throw new SchemaException("{$types[O]}: invalide type. expected $expectedType"); + } + } + } + + protected static function _ensure_type(array &$definition): void { + $types = $definition["type"]; + $nullable = $definition["nullable"]; + # s'il n'y a qu'une seul type, l'instancier tout de suite + if (count($types) == 1 && $types[0] !== null) { + foreach ($types as $key => $name) { + if ($key === 0) { + $args = null; + } else { + $args = $name; + $name = $key; + } + $definition["type"] = types::get($nullable, $name, $args, $definition); + } + } + switch ($definition[""][0]) { + case "assoc": + foreach ($definition["schema"] as &$definitionValue) { + self::_ensure_type($definitionValue); + }; + unset($definitionValue); + break; + case "list": + self::_ensure_type($definition["schema"]); + break; + } + } + /** * @var array définition du schéma, à redéfinir le cas échéant dans une classe * dérivée diff --git a/src/schema/_assoc/AssocSchema.php b/src/schema/_assoc/AssocSchema.php index 8e8b260..310f408 100644 --- a/src/schema/_assoc/AssocSchema.php +++ b/src/schema/_assoc/AssocSchema.php @@ -3,7 +3,11 @@ 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; /** @@ -13,33 +17,45 @@ class AssocSchema extends Schema { /** @var array meta-schema d'un schéma de nature tableau associatif */ const METASCHEMA = ref_schema::ASSOC_METASCHEMA; + private static function have_nature(array $definition): bool { + $nature = $definition[""] ?? null; + if ($nature === "assoc") return true; + if (is_array($nature) + && array_key_exists(0, $nature) + && $nature[0] === "assoc") { + return true; + } + return false; + } + /** * indiquer si $definition est une définition de schéma de nature tableau * associatif que {@link normalize()} pourrait normaliser */ static function isa_definition($definition): bool { if (!is_array($definition)) return false; - # nature explicitement spécifiée - if (array_key_exists("", $definition)) { - $nature = $definition[""]; - if ($nature === "assoc") return true; - if (is_array($nature) - && array_key_exists(0, $nature) - && $nature[0] === "assoc") { - return true; - } - return false; - } - # un tableau associatif - return !cl::have_num_keys($definition); + # nature explicitement spécifiée ou tableau associatif + return self::have_nature($definition) || !cl::have_num_keys($definition); } static function normalize($definition, $definitionKey=null): array { + if (!is_array($definition)) $definition = [$definition]; + if (!self::have_nature($definition)) { + $definition = [ + "?array", + "" => "assoc", + "schema" => $definition, + ]; + } + $definition = self::_normalize($definition, $definitionKey); + self::_ensure_nature($definition, "assoc", "array"); + return $definition; } function __construct($definition=null, $definitionKey=null, bool $normalize=true) { if ($definition === null) $definition = static::SCHEMA; if ($normalize) $definition = self::normalize($definition, $definitionKey); + self::_ensure_type($definition); $this->definition = $definition; } diff --git a/src/schema/_list/ListSchema.php b/src/schema/_list/ListSchema.php index f4bbfbc..66fee9c 100644 --- a/src/schema/_list/ListSchema.php +++ b/src/schema/_list/ListSchema.php @@ -2,13 +2,27 @@ 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 { /** @var array meta-schema d'un schéma de nature liste */ const METASCHEMA = ref_schema::LIST_METASCHEMA; + private static function have_nature(array $definition): bool { + $nature = $definition[""] ?? null; + if ($nature === "list") return true; + if (is_array($nature) + && array_key_exists(0, $nature) + && $nature[0] === "list") { + return true; + } + return false; + } + /** * indiquer si $definition est une définition de schéma de nature liste que * {@link normalize()} pourrait normaliser @@ -16,16 +30,7 @@ class ListSchema extends Schema { static function isa_definition($definition): bool { if (!is_array($definition)) return false; # nature explicitement spécifiée - if (array_key_exists("", $definition)) { - $nature = $definition[""]; - if ($nature === "list") return true; - if (is_array($nature) - && array_key_exists(0, $nature) - && $nature[0] === "list") { - return true; - } - return false; - } + if (self::have_nature($definition)) return true; # un unique élément tableau à l'index 0 $count = count($definition); $haveIndex0 = array_key_exists(0, $definition); @@ -33,11 +38,23 @@ class ListSchema extends Schema { } static function normalize($definition, $definitionKey=null): array { + if (!is_array($definition)) $definition = [$definition]; + if (!self::have_nature($definition)) { + $definition = [ + "?array", + "" => "list", + "schema" => $definition[0], + ]; + } + $definition = self::_normalize($definition, $definitionKey); + self::_ensure_nature($definition, "list", "array"); + return $definition; } function __construct($definition=null, $definitionKey=null, bool $normalize=true) { if ($definition === null) $definition = static::SCHEMA; if ($normalize) $definition = self::normalize($definition, $definitionKey); + self::_ensure_type($definition); $this->definition = $definition; } diff --git a/src/schema/_scalar/ScalarSchema.php b/src/schema/_scalar/ScalarSchema.php index 507d73c..a723c6a 100644 --- a/src/schema/_scalar/ScalarSchema.php +++ b/src/schema/_scalar/ScalarSchema.php @@ -75,118 +75,15 @@ class ScalarSchema extends Schema { } static function normalize($definition, $definitionKey=null): array { - if (!is_array($definition)) $definition = [$definition]; - # s'assurer que toutes les clés existent avec leur valeur par défaut - $index = 0; - foreach (array_keys(self::METASCHEMA) as $key) { - if (!array_key_exists($key, $definition)) { - if (array_key_exists($index, $definition)) { - $definition[$key] = $definition[$index]; - unset($definition[$index]); - $index++; - } else { - $definition[$key] = self::METASCHEMA[$key][1]; - } - } - } - # réordonner les clés numériques - if (cl::have_num_keys($definition)) { - $keys = array_keys($definition); - $index = 0; - foreach ($keys as $key) { - if (!is_int($key)) continue; - if ($key !== $index) { - $definition[$index] = $definition[$key]; - unset($definition[$key]); - } - $index++; - } - } - # type - $types = []; - $deftype = $definition["type"]; - $nullable = $definition["nullable"] ?? false; - if ($deftype === null) { - $types[] = null; - $nullable = true; - } else { - if (!is_array($deftype)) { - if (!is_string($deftype)) throw SchemaException::invalid_type($deftype); - $deftype = explode("|", $deftype); - } - foreach ($deftype as $type) { - if ($type !== null) $type = trim($type); - if ($type === null || $type === "null") { - $nullable = true; - continue; - } - if (!is_string($type)) throw SchemaException::invalid_type($type); - if (substr($type, 0, 1) == "?") { - $type = substr($type, 1); - $nullable = true; - } - if ($type === "") throw SchemaException::invalid_type($type); - $type = cl::get(ref_types::ALIASES, $type, $type); - $types = array_merge($types, explode("|", $type)); - } - if (!$types) throw SchemaException::invalid_schema("scalar: type is required"); - $types = array_keys(array_fill_keys($types, true)); - } - $definition["type"] = $types; - $definition["nullable"] = $nullable; - # nature - $nature = $definition[""]; - tarray::ensure_array($nature); - if (!array_key_exists(0, $nature) || $nature[0] !== "scalar") { - throw SchemaException::invalid_schema("expected scalar nature"); - } - $definition[""] = $nature; - # name, pkey, header - $name = $definition["name"]; - $pkey = $definition["pkey"]; - $header = $definition["header"]; - if ($name === null) $name = $definitionKey; - trawstring::ensure_nstring($name); - tpkey::ensure_npkey($pkey); - trawstring::ensure_nstring($header); - if ($pkey === null) $pkey = $name; - if ($header === null) $header = $name; - $definition["name"] = $name; - $definition["pkey"] = $pkey; - $definition["header"] = $header; - # autres éléments - tarray::ensure_narray($definition["schema"]); - trawstring::ensure_nstring($definition["title"]); - tbool::ensure_bool($definition["required"]); - tbool::ensure_bool($definition["nullable"]); - tcontent::ensure_ncontent($definition["desc"]); - tcallable::ensure_ncallable($definition["analyzer_func"]); - tcallable::ensure_ncallable($definition["extractor_func"]); - tcallable::ensure_ncallable($definition["parser_func"]); - tcallable::ensure_ncallable($definition["normalizer_func"]); - tarray::ensure_narray($definition["messages"]); - tcallable::ensure_ncallable($definition["formatter_func"]); - tbool::ensure_nbool($definition["composite"]); - - # s'il n'y a qu'une seul type, l'instancier tout de suite - if (count($types) == 1 && $types[0] !== null) { - foreach ($types as $key => $name) { - if ($key === 0) { - $args = null; - } else { - $args = $name; - $name = $key; - } - $definition["type"] = types::get($nullable, $name, $args, $definition); - } - } - + self::_normalize($definition, $definitionKey); + self::_ensure_nature($definition, "scalar", null); return $definition; } function __construct($definition=null, $definitionKey=null, bool $normalize=true) { if ($definition === null) $definition = static::SCHEMA; if ($normalize) $definition = self::normalize($definition, $definitionKey); + self::_ensure_type($definition); $this->definition = $definition; }