modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2023-11-27 22:39:35 +04:00
parent ea1b774d0f
commit 3028fe12f7
27 changed files with 330 additions and 121 deletions

View File

@ -2,7 +2,6 @@
## schema ## schema
* [ ] properties dans ScalarSchema, avec aliases pour notamment "nature" => ""
* [ ] remplacer IValue et TValue par une classe abstraite Value * [ ] remplacer IValue et TValue par une classe abstraite Value
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

View File

@ -1,5 +1,5 @@
<?php <?php
namespace nur\sery\schema\values; namespace nur\sery\schema;
/** /**
* Class Result: résultat de l'analyse ou de la normalisation d'une valeur * Class Result: résultat de l'analyse ou de la normalisation d'une valeur

View File

@ -2,28 +2,29 @@
namespace nur\sery\schema; namespace nur\sery\schema;
use ArrayAccess; use ArrayAccess;
use BadMethodCallException;
use LogicException; use LogicException;
use nulib\cl; use nulib\cl;
use nur\sery\schema\values\IValue; use nur\sery\schema\schemas\AssocSchema;
use nur\sery\schema\schemas\ListSchema;
use nur\sery\schema\schemas\ScalarSchema;
abstract class Schema implements ArrayAccess { abstract class Schema implements ArrayAccess {
/** /**
* 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
*/ */
static function new(&$md, $definition): self { static function ns(?Schema &$schema, $definition): self {
if ($md === null) { if ($schema === null) {
if (AssocSchema::isa_definition($definition)) { if (AssocSchema::isa_definition($definition)) {
$md = new AssocSchema($definition); $schema = new AssocSchema($definition);
} elseif (ListSchema::isa_definition($definition)) { } elseif (ListSchema::isa_definition($definition)) {
$md = new ListSchema($definition); $schema = new ListSchema($definition);
} elseif (ScalarSchema::isa_definition($definition)) { } elseif (ScalarSchema::isa_definition($definition)) {
$md = new ScalarSchema($definition); $schema = new ScalarSchema($definition);
} else { } else {
throw SchemaException::invalid_schema(); throw SchemaException::invalid_schema();
} }
} }
return $md; return $schema;
} }
/** /**
@ -42,8 +43,11 @@ abstract class Schema implements ArrayAccess {
/** 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 &$scalar=null): bool { return false; }
abstract function getValue(&$value, $key=null): IValue; /**
* Créer si besoin une nouvelle instance de {@link Value} qui référence la
* variable $dest
*/
abstract function nv(?Value &$value=null, &$dest=null, $key=null): Value;
############################################################################# #############################################################################
# key & properties # key & properties

27
src/schema/Value.php Normal file
View File

@ -0,0 +1,27 @@
<?php
namespace nur\sery\schema;
use nur\sery\schema\types\IType;
use nur\sery\schema\values\AssocValue;
use nur\sery\schema\values\ListValue;
use nur\sery\schema\values\ScalarValue;
abstract class Value {
function isAssoc(?AssocValue &$assoc=null): bool { return false; }
function isList(?ListValue &$list=null): bool { return false; }
function isScalar(?ScalarValue &$scalar=null): bool { return false; }
abstract function reset(&$dest, $key=null, bool $verifix=true): self;
/** retourner le type associé à cette valeur */
abstract function getType(): IType;
/** retourner true si la valeur existe */
abstract function exists(): bool;
/** retourner true si la valeur est valide */
abstract function valid(): bool;
/** retourner true si la valeur est dans sa forme normalisée */
abstract function normalized(): bool;
}

View File

@ -1,32 +1,36 @@
<?php <?php
namespace nur\sery\schema\input; namespace nur\sery\schema\input;
#XXX available permet de préciser qu'une valeur doit exister et être non vide
# pour être considérée. en faire un paramètre, à moins que ce soit au niveau du
# type qu'il faille le gérer?
/** /**
* Class Input: accès à une valeur * Class Input: accès à une valeur
* *
* cette implémentation lit depuis et écrit dans une référence * cette implémentation lit depuis et écrit dans une référence
*/ */
class Input { class Input {
function __construct(&$value=null) { function __construct(&$dest=null) {
$this->value =& $value; $this->dest =& $dest;
} }
protected $value; protected $dest;
/** tester si la valeur existe */
function exists($key=null): bool { function exists($key=null): bool {
if ($key === null) return true; if ($key === null) return true;
return $this->value !== null && array_key_exists($key, $this->value); return $this->dest !== null && array_key_exists($key, $this->dest);
} }
function get($key=null) { function get($key=null) {
if ($key === null) return $this->value; if ($key === null) return $this->dest;
elseif ($this->value === null) return null; elseif ($this->dest === null) return null;
elseif (!array_key_exists($key, $this->value)) return null; elseif (!array_key_exists($key, $this->dest)) return null;
else return $this->value[$key]; else return $this->dest[$key];
} }
function set($value, $key=null): void { function set($value, $key=null): void {
if ($key === null) $this->value = $value; if ($key === null) $this->dest = $value;
else $this->value[$key] = $value; else $this->dest[$key] = $value;
} }
} }

View File

@ -1,7 +1,12 @@
<?php <?php
namespace nur\sery\schema; namespace nur\sery\schema\schemas;
use nulib\cl; use nulib\cl;
use nur\sery\schema\input\Input;
use nur\sery\schema\Schema;
use nur\sery\schema\Value;
use nur\sery\schema\values\AssocValue;
use nur\sery\schema\values\ScalarValue;
class AssocSchema extends Schema { class AssocSchema extends Schema {
/** @var array meta-schema d'un schéma de nature tableau associatif */ /** @var array meta-schema d'un schéma de nature tableau associatif */
@ -29,7 +34,6 @@ class AssocSchema extends Schema {
} }
static function normalize($definition): array { static function normalize($definition): array {
} }
function __construct($definition=null, bool $normalize=true) { function __construct($definition=null, bool $normalize=true) {
@ -42,4 +46,11 @@ class AssocSchema extends Schema {
$assoc = $this; $assoc = $this;
return true; return true;
} }
function nv(?Value &$value=null, &$dest=null, $key=null): Value {
if (!($value instanceof AssocValue)) $value = new AssocValue($this);
if ($dest instanceof Input) $input = $dest;
else $input = new Input($dest);
return $value->reset($input, $key);
}
} }

View File

@ -1,5 +1,10 @@
<?php <?php
namespace nur\sery\schema; namespace nur\sery\schema\schemas;
use nur\sery\schema\input\Input;
use nur\sery\schema\Schema;
use nur\sery\schema\Value;
use nur\sery\schema\values\ListValue;
class ListSchema extends Schema { class ListSchema extends Schema {
/** @var array meta-schema d'un schéma de nature liste */ /** @var array meta-schema d'un schéma de nature liste */
@ -29,7 +34,6 @@ class ListSchema extends Schema {
} }
static function normalize($definition): array { static function normalize($definition): array {
} }
function __construct($definition=null, bool $normalize=true) { function __construct($definition=null, bool $normalize=true) {
@ -42,4 +46,11 @@ class ListSchema extends Schema {
$list = $this; $list = $this;
return true; return true;
} }
function nv(?Value &$value=null, &$dest=null, $key=null): Value {
if (!($value instanceof ListValue)) $value = new ListValue($this);
if ($dest instanceof Input) $input = $dest;
else $input = new Input($dest);
return $value->reset($input, $key);
}
} }

View File

@ -1,17 +1,19 @@
<?php <?php
namespace nur\sery\schema; namespace nur\sery\schema\schemas;
use nulib\cl; use nulib\cl;
use nur\sery\schema\input\Input; use nur\sery\schema\input\Input;
use nur\sery\schema\ref\ref_schema; use nur\sery\schema\ref\ref_schema;
use nur\sery\schema\ref\ref_types; use nur\sery\schema\ref\ref_types;
use nur\sery\schema\Schema;
use nur\sery\schema\SchemaException;
use nur\sery\schema\types\tarray; use nur\sery\schema\types\tarray;
use nur\sery\schema\types\tbool; use nur\sery\schema\types\tbool;
use nur\sery\schema\types\tcallable; use nur\sery\schema\types\tcallable;
use nur\sery\schema\types\tcontent; use nur\sery\schema\types\tcontent;
use nur\sery\schema\types\tpkey; use nur\sery\schema\types\tpkey;
use nur\sery\schema\types\tstring; use nur\sery\schema\types\tstring;
use nur\sery\schema\values\IValue; use nur\sery\schema\Value;
use nur\sery\schema\values\ScalarValue; use nur\sery\schema\values\ScalarValue;
/** /**
@ -180,10 +182,9 @@ class ScalarSchema extends Schema {
return true; return true;
} }
function getValue(&$value, $key=null): IValue { function nv(?Value &$value=null, &$dest=null, $key=null): Value {
if ($value instanceof Input) $input = $value; if (!($value instanceof ScalarValue)) $value = new ScalarValue($this);
else $input = new Input($value); return $value->reset($dest, $key);
return new ScalarValue($input, $this, $key);
} }
############################################################################# #############################################################################

37
src/schema/types.php Normal file
View File

@ -0,0 +1,37 @@
<?php
namespace nur\sery\schema;
use nur\sery\schema\types\IType;
use nur\sery\schema\types\Registry;
use nur\sery\schema\types\tarray;
use nur\sery\schema\types\tbool;
use nur\sery\schema\types\tcallable;
use nur\sery\schema\types\tfloat;
use nur\sery\schema\types\tint;
use nur\sery\schema\types\tstring;
/**
* Class types: classe outil pour gérer le registre de types
*/
class types {
/** @var Registry */
private static $registry;
static function registry(): Registry {
if (self::$registry === null) {
self::$registry = new Registry();
}
return self::$registry;
}
static function get(string $name): IType {
return self::registry()->get($name);
}
static function string(): tstring { return self::get("string"); }
static function bool(): tbool { return self::get("bool"); }
static function int(): tint { return self::get("int"); }
static function float(): tfloat { return self::get("float"); }
static function array(): tarray { return self::get("array"); }
static function callable(): tcallable { return self::get("callable"); }
}

View File

@ -1,7 +1,7 @@
<?php <?php
namespace nur\sery\schema\types; namespace nur\sery\schema\types;
use nur\sery\schema\values\Result; use nur\sery\schema\Result;
/** /**
* Interface IType: un type de données * Interface IType: un type de données

View File

@ -0,0 +1,26 @@
<?php
namespace nur\sery\schema\types;
class Registry {
const TYPES = [
"string" => tstring::class,
"bool" => tbool::class, "boolean" => tbool::class,
"int" => tint::class, "integer" => tint::class,
"float" => tfloat::class, "flt" => tfloat::class,
"double" => tfloat::class, "dbl" => tfloat::class,
"array" => tarray::class,
"callable" => tcallable::class,
];
function __construct() {
$this->types = [];
}
/** @var IType[] */
protected $types;
function get(string $name): IType {
#XXX les instancier
return $this->types[$name];
}
}

View File

@ -3,7 +3,7 @@ namespace nur\sery\schema\types;
use nulib\cl; use nulib\cl;
class tarray { class tarray implements IType {
static function ensure_array(&$array): void { static function ensure_array(&$array): void {
if (!is_array($array)) $array = cl::with($array); if (!is_array($array)) $array = cl::with($array);
} }

View File

@ -1,7 +1,7 @@
<?php <?php
namespace nur\sery\schema\types; namespace nur\sery\schema\types;
class tbool { class tbool implements IType {
static function ensure_bool(&$bool): void { static function ensure_bool(&$bool): void {
if (!is_bool($bool)) $bool = boolval($bool); if (!is_bool($bool)) $bool = boolval($bool);
} }

View File

@ -3,7 +3,7 @@ namespace nur\sery\schema\types;
use nur\sery\schema\SchemaException; use nur\sery\schema\SchemaException;
class tcallable { class tcallable implements IType {
static function ensure_callable(&$callable): void { static function ensure_callable(&$callable): void {
if (!is_callable($callable)) throw SchemaException::invalid_callable($callable); if (!is_callable($callable)) throw SchemaException::invalid_callable($callable);
} }

View File

@ -1,7 +1,7 @@
<?php <?php
namespace nur\sery\schema\types; namespace nur\sery\schema\types;
class tcontent { class tcontent implements IType {
static function ensure_content(&$content): void { static function ensure_content(&$content): void {
if (!is_string($content) && !is_array($content)) $content = strval($content); if (!is_string($content) && !is_array($content)) $content = strval($content);
} }

View File

@ -0,0 +1,5 @@
<?php
namespace nur\sery\schema\types;
class tfloat implements IType {
}

View File

@ -0,0 +1,5 @@
<?php
namespace nur\sery\schema\types;
class tint implements IType {
}

View File

@ -1,7 +1,7 @@
<?php <?php
namespace nur\sery\schema\types; namespace nur\sery\schema\types;
class tkey { class tkey implements IType {
static function ensure_key(&$key): void { static function ensure_key(&$key): void {
if (!is_string($key) && !is_int($key)) $key = strval($key); if (!is_string($key) && !is_int($key)) $key = strval($key);
} }

View File

@ -1,7 +1,7 @@
<?php <?php
namespace nur\sery\schema\types; namespace nur\sery\schema\types;
class tpkey { class tpkey implements IType {
static function ensure_pkey(&$pkey): void { static function ensure_pkey(&$pkey): void {
if (!is_string($pkey) && !is_int($pkey) && !is_array($pkey)) $pkey = strval($pkey); if (!is_string($pkey) && !is_int($pkey) && !is_array($pkey)) $pkey = strval($pkey);
if (is_array($pkey)) { if (is_array($pkey)) {

View File

@ -1,7 +1,7 @@
<?php <?php
namespace nur\sery\schema\types; namespace nur\sery\schema\types;
class tstring { class tstring implements IType {
static function ensure_string(&$string): void { static function ensure_string(&$string): void {
if (!is_string($string)) $string = strval($string); if (!is_string($string)) $string = strval($string);
} }

View File

@ -1,11 +1,10 @@
<?php <?php
namespace nur\sery\schema\values; namespace nur\sery\schema\values;
class AssocValue implements IValue { use nur\sery\schema\Value;
use TValue; use nur\sery\schema\Result;
function isScalar(?ScalarValue &$scalar=null): bool { return false; } class AssocValue extends Value {
function isList(?ListValue &$list=null): bool { return false; }
function isAssoc(?AssocValue &$assoc=null): bool { $assoc = $this; return true; } function isAssoc(?AssocValue &$assoc=null): bool { $assoc = $this; return true; }
function ensureKeys(): bool { function ensureKeys(): bool {

View File

@ -1,29 +0,0 @@
<?php
namespace nur\sery\schema\values;
use nur\sery\schema\types\IType;
interface IValue {
/** retourner true si cette valeur est scalaire */
function isScalar(?ScalarValue &$scalar=null): bool;
/** retourner true cette valeur est une liste */
function isList(?ListValue &$list=null): bool;
/** retourner true cette valeur est un tableau associatif */
function isAssoc(?AssocValue &$assoc=null): bool;
function reset(&$value, $key=null, bool $verifix=true): void;
/** retourner true si la valeur existe */
function exists(): bool;
/** retourner true si la valeur est valide */
function isValid(): bool;
/** retourner true si la valeur est dans sa forme normalisée */
function isNormalized();
/** retourner le type associé à cette valeur */
function getType(): IType;
}

View File

@ -1,12 +1,11 @@
<?php <?php
namespace nur\sery\schema\values; namespace nur\sery\schema\values;
class ListValue implements IValue { use nur\sery\schema\Result;
use TValue; use nur\sery\schema\Value;
function isScalar(?ScalarValue &$scalar=null): bool { return false; } class ListValue extends Value {
function isList(?ListValue &$list=null): bool { $list = $this; return true; } function isList(?ListValue &$list=null): bool { $list = $this; return true; }
function isAssoc(?AssocValue &$assoc=null): bool { return false; }
function ensureKeys(): bool { function ensureKeys(): bool {
} }

View File

@ -1,52 +1,71 @@
<?php <?php
namespace nur\sery\schema\values; namespace nur\sery\schema\values;
use nur\sery\schema\ScalarSchema; use nur\sery\schema\input\Input;
use nur\sery\schema\Result;
use nur\sery\schema\schemas\ScalarSchema;
use nur\sery\schema\types\IType; use nur\sery\schema\types\IType;
use nur\sery\schema\Value;
class ScalarValue implements IValue { class ScalarValue extends Value {
use TValue; function __construct(ScalarSchema $schema, &$dest=null, $key=null, bool $verifix=true) {
$this->schema = $schema;
$this->reset($dest, $key, $verifix);
}
function isScalar(?ScalarValue &$scalar=null): bool { $scalar = $this; return true; }
/** @var ScalarSchema schéma de cette valeur */ /** @var ScalarSchema schéma de cette valeur */
protected $schema; protected $schema;
/** @var Input source et destination de la valeur */
protected $input;
/** @var string|int clé de la valeur dans le tableau destination */
protected $key;
/** @var ?Result résultat de l'analyse de la valeur */ /** @var ?Result résultat de l'analyse de la valeur */
protected $result; protected $result;
function __construct(&$value, ScalarSchema $schema, $key=null, bool $verifix=true) { function reset(&$dest, $key=null, bool $verifix=true): Value {
$this->schema = $schema; if ($dest instanceof Input) $input = $dest;
$this->reset($value, $key, $verifix); else $input = new Input($dest);
#XXX résoudre les type dans reset()? $this->input = $input;
$this->key = $key;
$this->result = null;
#XXX résoudre les types ici?
if ($verifix) $this->verifix();
return $this;
} }
function isScalar(?ScalarValue &$scalar=null): bool { $scalar = $this; return true; } function exists(): bool {
function isList(?ListValue &$list=null): bool { return false; } return $this->input->exists($this->key);
function isAssoc(?AssocValue &$assoc=null): bool { return false; } }
function get($default=null) { function get($default=null) {
$key = $this->key; $key = $this->key;
if ($key === null) return $this->value; $input = $this->input;
elseif (array_key_exists($key, $this->value)) return $this->value[$key]; if ($input->exists($key)) return $input->get($key);
else return $default; else return $default;
} }
function set($value): self { function set($value): self {
$key = $this->key; $this->input->set($value, $this->key);
if ($key === null) $this->value = $value;
else $this->value[$key] = $value;
return $this; return $this;
} }
/** @var IType */ /** @var IType */
protected $type; protected $type;
function type(): IType { function getType(): IType {
if ($this->type === null) $this->type = $this->schema->getType($this->key); if ($this->type === null) $this->type = $this->schema->getType($this->key);
return $this->type; return $this->type;
} }
function exists(): bool { function valid(): bool {
return $this->type()->exists($this->value, $this->key); }
function normalized(): bool {
} }
/** /**
@ -55,25 +74,25 @@ class ScalarValue implements IValue {
* si la valeur était déjà normalisée, retourner false. * si la valeur était déjà normalisée, retourner false.
*/ */
function verifix(bool $throw=true, ?Result &$result=null): bool { function verifix(bool $throw=true, ?Result &$result=null): bool {
$type = $this->type(); $type = $this->getType();
$key = $this->key; $key = $this->key;
if ($key === null) $modified = $type->verifix($this->value, $throw, $result); if ($key === null) $modified = $type->verifix($this->input, $throw, $result);
else $modified = $type->verifix($this->value[$key], $throw, $result); else $modified = $type->verifix($this->input[$key], $throw, $result);
$this->result = $result; $this->result = $result;
return $modified; return $modified;
} }
function parse($value, bool $throw=true, ?Result &$result=null) { function parse($value, bool $throw=true, ?Result &$result=null) {
$this->type()->verifix($value, $throw, $result); $this->getType()->verifix($value, $throw, $result);
$this->set($value); $this->set($value);
$this->result = $result; $this->result = $result;
return $value; return $value;
} }
function format(?string $format=null): string { function format(?string $format=null): string {
$type = $this->type(); $type = $this->getType();
$key = $this->key; $key = $this->key;
if ($key === null) return $type->format($this->value, $format); if ($key === null) return $type->format($this->input, $format);
else return $type->format($this->value[$key], $format); else return $type->format($this->input[$key], $format);
} }
} }

View File

@ -1,17 +0,0 @@
<?php
namespace nur\sery\schema\values;
trait TValue {
/** @var mixed valeur ou référence d'un tableau contenant la valeur */
protected $value;
/** @var string|int clé de la valeur dans le tableau destination */
protected $key;
function reset(&$value, $key=null, bool $verifix=true): void {
$this->value =& $value;
$this->key = $key;
$this->result = null;
if ($verifix) $this->verifix();
}
}

106
tests/schema/schemaTest.php Normal file
View File

@ -0,0 +1,106 @@
<?php
namespace nur\sery\schema;
use Exception;
use nulib\tests\TestCase;
use nur\sery\schema\values\ScalarValue;
class schemaTest extends TestCase {
function testInt() {
Schema::ns($schema, "int");
/** @var ScalarValue $intv */
$schema->nv($intv, $int);
$f = function($value) use($intv) {
return function() use($intv, $value) {
$intv->set($value);
};
};
self::assertException(Exception::class, $f(null));
self::assertException(Exception::class, $f(""));
self::assertException(Exception::class, $f(" "));
$intv->set(12);
self::assertSame(12, $intv->get());
self::assertSame(12, $int);
self::assertSame("12", $intv->format());
self::assertSame("0012", $intv->format("%4u"));
$intv->set("12");
self::assertSame(12, $intv->get());
$intv->set(" 12 ");
self::assertSame(12, $intv->get());
self::assertException(Exception::class, $f(true));
self::assertException(Exception::class, $f(false));
self::assertException(Exception::class, $f("a"));
self::assertException(Exception::class, $f([]));
self::assertException(Exception::class, $f(["a"]));
}
function testNint() {
Schema::ns($schema, ["?int"]);
/** @var ScalarValue $intv */
$schema->nv($intv, $int);
$f = function($value) use($intv) {
return function() use($intv, $value) {
$intv->set($value);
};
};
$intv->set(null);
self::assertNull($intv->get());
self::assertNull($int);
self::assertSame("", $intv->format());
$intv->set("");
self::assertNull($intv->get());
self::assertNull($int);
self::assertSame("", $intv->format());
$intv->set(" ");
self::assertNull($intv->get());
self::assertNull($int);
self::assertSame("", $intv->format());
$intv->set(12);
self::assertSame(12, $intv->get());
self::assertSame(12, $int);
self::assertSame("12", $intv->format());
self::assertSame("0012", $intv->format("%4u"));
$intv->set("12");
self::assertSame(12, $int);
$intv->set(" 12 ");
self::assertSame(12, $int);
self::assertException(Exception::class, $f(true));
self::assertException(Exception::class, $f(false));
self::assertException(Exception::class, $f("a"));
self::assertException(Exception::class, $f([]));
self::assertException(Exception::class, $f(["a"]));
}
function testUnionTypes() {
# l'ordre des types est respecté
Schema::ns($schema, ["string|int"]);
/** @var ScalarValue $siv */
$schema->nv($siv, $si);
$siv->set("12");
self::assertSame("12", $si);
$siv->set(12);
self::assertSame(12, $si);
Schema::ns($schema, ["int|string"]);
/** @var ScalarValue $siv */
$schema->nv($siv, $si);
$siv->set("12");
self::assertSame(12, $si);
$siv->set(12);
self::assertSame(12, $si);
}
}

View File

@ -1,7 +1,9 @@
<?php <?php
namespace nur\sery\schema; namespace nur\sery\schema\schemas;
use nulib\tests\TestCase; use nulib\tests\TestCase;
use nur\sery\schema\SchemaException;
use nur\sery\schema\schemas\ScalarSchema;
class ScalarSchemaTest extends TestCase { class ScalarSchemaTest extends TestCase {
const NULL_SCHEMA = [ const NULL_SCHEMA = [
@ -20,7 +22,7 @@ class ScalarSchemaTest extends TestCase {
"format" => null, "format" => null,
"" => ["scalar"], "" => ["scalar"],
"name" => null, "name" => null,
"key" => null, "pkey" => null,
"header" => null, "header" => null,
"composite" => null, "composite" => null,
]; ];