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
* [ ] properties dans ScalarSchema, avec aliases pour notamment "nature" => ""
* [ ] 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

View File

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

View File

@ -2,28 +2,29 @@
namespace nur\sery\schema;
use ArrayAccess;
use BadMethodCallException;
use LogicException;
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 {
/**
* créer si besoin une nouvelle instance à partir d'une définition de schéma
*/
static function new(&$md, $definition): self {
if ($md === null) {
static function ns(?Schema &$schema, $definition): self {
if ($schema === null) {
if (AssocSchema::isa_definition($definition)) {
$md = new AssocSchema($definition);
$schema = new AssocSchema($definition);
} elseif (ListSchema::isa_definition($definition)) {
$md = new ListSchema($definition);
$schema = new ListSchema($definition);
} elseif (ScalarSchema::isa_definition($definition)) {
$md = new ScalarSchema($definition);
$schema = new ScalarSchema($definition);
} else {
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 */
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

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
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
*
* cette implémentation lit depuis et écrit dans une référence
*/
class Input {
function __construct(&$value=null) {
$this->value =& $value;
function __construct(&$dest=null) {
$this->dest =& $dest;
}
protected $value;
protected $dest;
/** tester si la valeur existe */
function exists($key=null): bool {
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) {
if ($key === null) return $this->value;
elseif ($this->value === null) return null;
elseif (!array_key_exists($key, $this->value)) return null;
else return $this->value[$key];
if ($key === null) return $this->dest;
elseif ($this->dest === null) return null;
elseif (!array_key_exists($key, $this->dest)) return null;
else return $this->dest[$key];
}
function set($value, $key=null): void {
if ($key === null) $this->value = $value;
else $this->value[$key] = $value;
if ($key === null) $this->dest = $value;
else $this->dest[$key] = $value;
}
}

View File

@ -1,7 +1,12 @@
<?php
namespace nur\sery\schema;
namespace nur\sery\schema\schemas;
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 {
/** @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 {
}
function __construct($definition=null, bool $normalize=true) {
@ -42,4 +46,11 @@ class AssocSchema extends Schema {
$assoc = $this;
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
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 {
/** @var array meta-schema d'un schéma de nature liste */
@ -29,7 +34,6 @@ class ListSchema extends Schema {
}
static function normalize($definition): array {
}
function __construct($definition=null, bool $normalize=true) {
@ -42,4 +46,11 @@ class ListSchema extends Schema {
$list = $this;
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
namespace nur\sery\schema;
namespace nur\sery\schema\schemas;
use nulib\cl;
use nur\sery\schema\input\Input;
use nur\sery\schema\ref\ref_schema;
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\tbool;
use nur\sery\schema\types\tcallable;
use nur\sery\schema\types\tcontent;
use nur\sery\schema\types\tpkey;
use nur\sery\schema\types\tstring;
use nur\sery\schema\values\IValue;
use nur\sery\schema\Value;
use nur\sery\schema\values\ScalarValue;
/**
@ -180,10 +182,9 @@ class ScalarSchema extends Schema {
return true;
}
function getValue(&$value, $key=null): IValue {
if ($value instanceof Input) $input = $value;
else $input = new Input($value);
return new ScalarValue($input, $this, $key);
function nv(?Value &$value=null, &$dest=null, $key=null): Value {
if (!($value instanceof ScalarValue)) $value = new ScalarValue($this);
return $value->reset($dest, $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
namespace nur\sery\schema\types;
use nur\sery\schema\values\Result;
use nur\sery\schema\Result;
/**
* 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;
class tarray {
class tarray implements IType {
static function ensure_array(&$array): void {
if (!is_array($array)) $array = cl::with($array);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,52 +1,71 @@
<?php
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\Value;
class ScalarValue implements IValue {
use TValue;
class ScalarValue extends Value {
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 */
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 */
protected $result;
function __construct(&$value, ScalarSchema $schema, $key=null, bool $verifix=true) {
$this->schema = $schema;
$this->reset($value, $key, $verifix);
#XXX résoudre les type dans reset()?
function reset(&$dest, $key=null, bool $verifix=true): Value {
if ($dest instanceof Input) $input = $dest;
else $input = new Input($dest);
$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 isList(?ListValue &$list=null): bool { return false; }
function isAssoc(?AssocValue &$assoc=null): bool { return false; }
function exists(): bool {
return $this->input->exists($this->key);
}
function get($default=null) {
$key = $this->key;
if ($key === null) return $this->value;
elseif (array_key_exists($key, $this->value)) return $this->value[$key];
$input = $this->input;
if ($input->exists($key)) return $input->get($key);
else return $default;
}
function set($value): self {
$key = $this->key;
if ($key === null) $this->value = $value;
else $this->value[$key] = $value;
$this->input->set($value, $this->key);
return $this;
}
/** @var IType */
protected $type;
function type(): IType {
function getType(): IType {
if ($this->type === null) $this->type = $this->schema->getType($this->key);
return $this->type;
}
function exists(): bool {
return $this->type()->exists($this->value, $this->key);
function valid(): bool {
}
function normalized(): bool {
}
/**
@ -55,25 +74,25 @@ class ScalarValue implements IValue {
* si la valeur était déjà normalisée, retourner false.
*/
function verifix(bool $throw=true, ?Result &$result=null): bool {
$type = $this->type();
$type = $this->getType();
$key = $this->key;
if ($key === null) $modified = $type->verifix($this->value, $throw, $result);
else $modified = $type->verifix($this->value[$key], $throw, $result);
if ($key === null) $modified = $type->verifix($this->input, $throw, $result);
else $modified = $type->verifix($this->input[$key], $throw, $result);
$this->result = $result;
return $modified;
}
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->result = $result;
return $value;
}
function format(?string $format=null): string {
$type = $this->type();
$type = $this->getType();
$key = $this->key;
if ($key === null) return $type->format($this->value, $format);
else return $type->format($this->value[$key], $format);
if ($key === null) return $type->format($this->input, $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
namespace nur\sery\schema;
namespace nur\sery\schema\schemas;
use nulib\tests\TestCase;
use nur\sery\schema\SchemaException;
use nur\sery\schema\schemas\ScalarSchema;
class ScalarSchemaTest extends TestCase {
const NULL_SCHEMA = [
@ -20,7 +22,7 @@ class ScalarSchemaTest extends TestCase {
"format" => null,
"" => ["scalar"],
"name" => null,
"key" => null,
"pkey" => null,
"header" => null,
"composite" => null,
];