modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2023-11-24 22:36:33 +04:00
parent c1cdfdb855
commit 80877faba1
10 changed files with 280 additions and 93 deletions

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpTestFrameworkVersionCache">
<tools_cache>
<tool tool_name="PHPUnit">
<cache>
<versions>
<info id="Local/home/jclain/wop/php/nur-sery/vendor/autoload.php" version="9.6.13" />
</versions>
</cache>
</tool>
</tools_cache>
</component>
</project>

View File

@ -37,4 +37,8 @@ class AssocSchema extends Schema {
if ($normalize) $definition = self::normalize($definition); if ($normalize) $definition = self::normalize($definition);
$this->definition = $definition; $this->definition = $definition;
} }
function isAssoc(): bool {
return true;
}
} }

View File

@ -37,4 +37,8 @@ class ListSchema extends Schema {
if ($normalize) $definition = self::normalize($definition); if ($normalize) $definition = self::normalize($definition);
$this->definition = $definition; $this->definition = $definition;
} }
function isList(): bool {
return true;
}
} }

View File

@ -21,60 +21,6 @@ use nulib\StateException;
# format() et [formatter_func] --> formatter la valeur et retourner une chaine # format() et [formatter_func] --> formatter la valeur et retourner une chaine
class OldSchema { class OldSchema {
/**
* créer une nouvelle instance de cette classe à partir d'un schéma
*
* @param bool $normalized indique si le schéma est normalisé
* normalisé
*/
static function new(&$md, $schema, bool $normalized=false): self {
if ($md === null) $md = new static($schema, !$normalized);
return $md;
}
static function normalize($schema): array {
if ($schema === null) throw new SchemaException("schema is required");
elseif (!is_array($schema)) $schema = [$schema];
#XXX
return $schema;
}
const SCHEMA = null;
function __construct($schema=null, bool $normalize=true) {
if ($schema === null) $schema = static::SCHEMA;
if ($normalize) $schema = self::normalize($schema);
$this->schema = $schema;
}
/**
* @var array schéma normalisé
*
* il y a 3 formes pour un schéma:
* - schéma valeur scalaire [0 => type, 1 => default, ...]
* - schéma tableau séquentiel [schema]
* - schéma tableau associatif [key => schema, ...]
*/
protected $schema;
/**
* retourner true si le schéma est pour une valeur scalaire, false s'il s'agit
* du schéma d'un tableau (séquentiel ou associatif)
*/
function isScalar(): bool {
return array_key_exists(0, $this->schema) && array_key_exists(1, $this->schema);
}
/** retourner true si le schéma est pour un tableau séquentiel */
function isSeq(): bool {
return array_key_exists(0, $this->schema) && !array_key_exists(1, $this->schema);
}
/** retourner true si le schéma est pour un tableau associatif */
function isAssoc(): bool {
return !array_key_exists(0, $this->schema) && !array_key_exists(1, $this->schema);
}
/** /**
* @var bool true si toutes les clés du schéma doivent exister, avec leur * @var bool true si toutes les clés du schéma doivent exister, avec leur
* valeur par défaut le cas échéant * valeur par défaut le cas échéant

View File

@ -1,9 +1,44 @@
<?php <?php
namespace nur\sery\schema; namespace nur\sery\schema;
use nulib\cl;
use nur\sery\schema\ref\ref_schema;
use nur\sery\schema\ref\ref_types;
/**
* Class ScalarSchema
*
* @property-read array $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 string|int|null $name
*/
class ScalarSchema extends Schema { class ScalarSchema extends Schema {
/** @var array meta-schema d'un schéma de nature scalaire */ /** @var array meta-schema d'un schéma de nature scalaire */
const METASCHEMA = ref_schema::SCALAR_METASCHEMA; const METASCHEMA = ref_schema::SCALAR_METASCHEMA;
const METASCHEMA_KEYS = [
"type", "default", "title", "required", "nullable", "desc",
"analyzer_func", "extractor_func", "parser_func", "normalizer_func", "messages",
"formatter_func", "format",
"", "name",
];
const METASCHEMA_INDEXES = [
"type" => 0, "default" => 1, "title" => 2, "required" => 3, "nullable" => 4, "desc" => 5,
"analyzer_func" => 6, "extractor_func" => 7, "parser_func" => 8, "normalizer_func" => 9, "messages" => 10,
"formatter_func" => 11, "format" => 12,
"" => 13, "name" => 14,
];
/** /**
* indiquer si $definition est une définition de schéma scalaire que * indiquer si $definition est une définition de schéma scalaire que
@ -37,7 +72,82 @@ class ScalarSchema extends Schema {
} }
static function normalize($definition): array { static function normalize($definition): array {
if (!is_array($definition)) $definition = [$definition];
# s'assurer que toutes les clés existent avec leur valeur par défaut
$index = 0;
foreach (self::METASCHEMA_KEYS 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;
$definition[$index] = $definition[$key];
unset($definition[$key]);
$index++;
}
}
# type
$types = [];
$deftype = $definition["type"];
$nullable = $definition["nullable"];
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 === "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[""];
self::ensure_array($nature);
if (!array_key_exists(0, $nature) || $nature[0] !== "scalar") {
throw SchemaException::invalid_schema("expected scalar nature");
}
$definition[""] = $nature;
# autres éléments
self::ensure_nstring($definition["title"]);
self::ensure_bool($definition["required"]);
self::ensure_bool($definition["nullable"]);
self::ensure_ncontent($definition["desc"]);
self::ensure_ncallable($definition["analyzer_func"]);
self::ensure_ncallable($definition["extractor_func"]);
self::ensure_ncallable($definition["parser_func"]);
self::ensure_ncallable($definition["normalizer_func"]);
self::ensure_narray($definition["messages"]);
self::ensure_ncallable($definition["formatter_func"]);
self::ensure_nkey($definition["name"]);
return $definition;
} }
function __construct($definition=null, bool $normalize=true) { function __construct($definition=null, bool $normalize=true) {
@ -45,4 +155,8 @@ class ScalarSchema extends Schema {
if ($normalize) $definition = self::normalize($definition); if ($normalize) $definition = self::normalize($definition);
$this->definition = $definition; $this->definition = $definition;
} }
function isScalar(): bool {
return true;
}
} }

View File

@ -1,6 +1,8 @@
<?php <?php
namespace nur\sery\schema; namespace nur\sery\schema;
use nulib\cl;
abstract class Schema { abstract class Schema {
/** /**
* créer si besoin une nouvelle instance à partir d'une définition de schéma * créer si besoin une nouvelle instance à partir d'une définition de schéma
@ -20,6 +22,42 @@ abstract class Schema {
return $md; return $md;
} }
protected static function ensure_nstring(&$string): void {
if ($string !== null && !is_string($string)) $string = strval($string);
}
protected static function ensure_string(&$string): void {
if (!is_string($string)) $string = strval($string);
}
protected static function ensure_nbool(&$bool): void {
if ($bool !== null && !is_bool($bool)) $bool = boolval($bool);
}
protected static function ensure_bool(&$bool): void {
if (!is_bool($bool)) $bool = boolval($bool);
}
protected static function ensure_ncallable(&$callable): void {
if ($callable !== null && !is_callable($callable)) {
throw SchemaException::invalid_callable($callable);
}
}
protected static function ensure_narray(&$array): void {
if ($array !== null && !is_array($array)) $array = cl::with($array);
}
protected static function ensure_array(&$array): void {
if (!is_array($array)) $array = cl::with($array);
}
protected static function ensure_nkey(&$key): void {
if ($key !== null && !is_string($key) && !is_int($key)) $key = strval($key);
}
protected static function ensure_key(&$key): void {
if (!is_string($key) && !is_int($key)) $key = strval($key);
}
protected static function ensure_ncontent(&$key): void {
if ($key !== null && !is_string($key) && !is_array($key)) $key = strval($key);
}
protected static function ensure_content(&$key): void {
if (!is_string($key) && !is_array($key)) $key = strval($key);
}
/** /**
* @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
@ -28,4 +66,11 @@ abstract class Schema {
/** @var array */ /** @var array */
protected $definition; protected $definition;
/** retourner true si le schéma est de nature tableau associatif */
function isAssoc(): bool { return false; }
/** retourner true si le schéma est de nature liste */
function isList(): bool { return false; }
/** retourner true si le schéma est de nature scalaire */
function isScalar(): bool { return false; }
} }

View File

@ -4,7 +4,21 @@ namespace nur\sery\schema;
use Exception; use Exception;
class SchemaException extends Exception { class SchemaException extends Exception {
static final function invalid_schema(): self { static final function invalid_schema(?string $message=null): self {
return new static("invalid schema"); $invalid_schema = "invalid schema";
if ($message !== null) $invalid_schema .= ": $message";
return new static($invalid_schema);
}
static final function invalid_type($value, ?string $message=null): self {
$invalid_type = var_export($value, true).": invalid type";
if ($message !== null) $invalid_type .= ": $message";
return new static($invalid_type);
}
static final function invalid_callable($value, ?string $message=null): self {
$invalid_callable = var_export($value, true).": invalid callable";
if ($message !== null) $invalid_callable .= ": $message";
return new static($invalid_callable);
} }
} }

View File

@ -1,41 +1,9 @@
<?php <?php
namespace nur\sery\schema; namespace nur\sery\schema\ref;
class ref_schema { class ref_schema {
const SCHEMA_SCHEMA = [
"type" => ["string", null, "" => 0,
"required" => true,
"desc" => "type de la valeur",
],
"default" => [null, null, "" => 1,
"desc" => "valeur par défaut",
],
"key" => ["?key", null, "" => "",
"desc" => "clé de la valeur si elle est dans un tableau",
],
"required" => ["bool", false,
"desc" => "cette valeur est-elle requise?",
],
"formatter" => ["?callable", null,
"desc" => "signature de la fonction: (value, format, type) => string",
],
"parser" => ["?callable", null,
"desc" => "signature de la fonction: (value, type) => [value, result]",
],
"messages" => ["?array", null,
"desc" => "message à afficher en cas d'erreur d'analyse",
],
];
const MESSAGES = [
"absent" => "{key}: Vous devez spécifier cette valeur",
"null" => "{key}: Cette valeur ne doit pas être nulle",
"empty" => "{key}: Cette valeur ne doit pas être vide",
"invalid" => "{key}: {orig}: cette valeur est invalide",
];
/** @var array schéma des natures de schéma */ /** @var array schéma des natures de schéma */
public const NATURE_METASCHEMA = [ const NATURE_METASCHEMA = [
"nature" => ["string", null, "nature du schéma", "nature" => ["string", null, "nature du schéma",
"key" => 0, "key" => 0,
"allowed_values" => ["assoc", "list", "scalar"], "allowed_values" => ["assoc", "list", "scalar"],
@ -49,7 +17,7 @@ class ref_schema {
]; ];
/** @var array meta-schema d'un schéma de nature scalaire */ /** @var array meta-schema d'un schéma de nature scalaire */
public const SCALAR_METASCHEMA = [ const SCALAR_METASCHEMA = [
"type" => ["array", null, "types possibles de la valeur", "required" => true], "type" => ["array", null, "types possibles de la valeur", "required" => true],
"default" => [null, null, "valeur par défaut si la valeur n'existe pas"], "default" => [null, null, "valeur par défaut si la valeur n'existe pas"],
"title" => ["?string", null, "libellé de la valeur"], "title" => ["?string", null, "libellé de la valeur"],
@ -63,7 +31,16 @@ class ref_schema {
"messages" => ["?array", null, "messages à afficher en cas d'erreur d'analyse"], "messages" => ["?array", null, "messages à afficher en cas d'erreur d'analyse"],
"formatter_func" => ["?callable", null, "fonction qui formatte la valeur pour affichage"], "formatter_func" => ["?callable", null, "fonction qui formatte la valeur pour affichage"],
"format" => [null, null, "format à utiliser pour l'affichage"], "format" => [null, null, "format à utiliser pour l'affichage"],
"" => ["" => ["assoc", "schema" => self::NATURE_METASCHEMA]], "" => ["array", "scalar", "nature du schéma",
"" => ["assoc", "schema" => self::NATURE_METASCHEMA],
],
"name" => ["?key", null, "identifiant de la valeur"], "name" => ["?key", null, "identifiant de la valeur"],
]; ];
const MESSAGES = [
"absent" => "{key}: Vous devez spécifier cette valeur",
"null" => "{key}: Cette valeur ne doit pas être nulle",
"empty" => "{key}: Cette valeur ne doit pas être vide",
"invalid" => "{key}: {orig}: cette valeur est invalide",
];
} }

View File

@ -0,0 +1,9 @@
<?php
namespace nur\sery\schema\ref;
class ref_types {
const ALIASES = [
"key" => "string|int",
"content" => "string|array",
];
}

View File

@ -0,0 +1,60 @@
<?php
namespace nur\sery\schema;
use nulib\tests\TestCase;
class ScalarSchemaTest extends TestCase {
const NULL_SCHEMA = [
"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,
"" => ["scalar"],
"name" => null,
];
static function schema(array $schema): array {
return array_merge(self::NULL_SCHEMA, $schema);
}
function testNormalize() {
self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize(null));
self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([]));
self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([null]));
self::assertException(SchemaException::class, function () {
ScalarSchema::normalize([[]]);
});
self::assertException(SchemaException::class, function () {
ScalarSchema::normalize([[null]]);
});
$string = self::schema(["type" => ["string"], "nullable" => false]);
self::assertSame($string, ScalarSchema::normalize("string"));
self::assertSame($string, ScalarSchema::normalize(["string"]));
$nstring = self::schema(["type" => ["string"]]);
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, ScalarSchema::normalize("key"));
self::assertSame($key, ScalarSchema::normalize("key|string"));
$nkey = self::schema(["type" => ["string", "int"], "nullable" => true]);
self::assertSame($nkey, ScalarSchema::normalize("?key"));
self::assertSame($nkey, ScalarSchema::normalize("?key|string"));
}
}