nur-ture/src/schema/Wrapper.php

222 lines
6.4 KiB
PHP

<?php
namespace nulib\schema;
use ArrayAccess;
use IteratorAggregate;
use nulib\php\func;
use nulib\schema\_assoc\AssocWrapper;
use nulib\schema\_list\ListWrapper;
use nulib\schema\_scalar\ScalarResult;
use nulib\schema\_scalar\ScalarWrapper;
use nulib\schema\input\Input;
use nulib\schema\types\IType;
abstract class Wrapper implements ArrayAccess, IteratorAggregate {
protected WrapperContext $context;
/** changer les paramètres de gestion des valeurs */
function resetParams(?array $params): void {
$this->context->resetParams($params);
}
protected function resetContext($resetSelectedKey): void {
$context = $this->context;
$type = $context->schema->type;
if (is_array($type)) $type = $type[0];
if (is_string($type)) $type = types::get($context->schema->nullable, $type);
$context->type = $type;
$context->result->reset();
$context->analyzed = false;
$context->normalized = false;
if ($resetSelectedKey) $context->selectedKey = null;
}
protected function afterModify(?array $params, $resetSelectedKey=false): void {
$context = $this->context;
$this->resetContext($resetSelectedKey);
if ($params["analyze"] ?? $context->analyze) {
$this->analyze($params);
}
if ($context->analyzed) {
if ($params["normalize"] ?? $context->normalize) {
$this->normalize($params);
}
}
}
protected function newInput(&$value): Input {
return new Input($value);
}
/**
* spécifier la valeur destination gérée par cet objet.
*
* @param ?array $params paramètres spécifique à cet appel, qui peuvent être
* différent des paramètres par défaut
*/
function reset(&$value, $valueKey=null, ?array $params=null): Wrapper {
$context = $this->context;
if ($value instanceof Input) $input = $value;
else $input = $this->newInput($value);
$context->input = $input;
$context->valueKey = $valueKey;
$this->afterModify($params, true);
return $this;
}
/** analyser la valeur */
abstract static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int;
function analyze(?array $params=null): bool {
$context = $this->context;
$reanalyze = $params["reanalyze"] ?? false;
if ($context->analyzed && !$reanalyze) return false;
static::_analyze($context, $this, $params);
$context->analyzed = true;
return true;
}
/** normaliser la valeur */
abstract static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool;
function normalize(?array $params=null): bool {
$context = $this->context;
// il faut que la valeur soit analysée avant de la normaliser
static::analyze($params);
if (!$context->analyzed) return false;
$renormalize = $params["renormalize"] ?? false;
if ($renormalize || !$context->normalized) {
$modified = static::_normalize($context, $this, $params);
$context->normalized = true;
} else {
$modified = false;
}
/** @var ScalarResult $result */
$result = $context->result;
if (!$result->valid) {
$result->throw($params["throw"] ?? $context->throw);
}
return $modified;
}
/**
* Obtenir la liste des clés valides pour les valeurs accessibles via cet
* objet
*/
abstract function getKeys(): array;
/**
* sélectionner le wrapper associé à la clé spécifiée
*
* @param string|int|null $key
* @return Wrapper $this
*/
abstract function select($key): Wrapper;
function getIterator() {
foreach ($this->getKeys() as $key) {
yield $key => $this->select($key);
}
$this->select(null);
}
/**
* obtenir le résultat de l'analyse de la valeur du wrapper sélectionné
*
* cette fonction doit être appelée après {@link set()} ou {@link unset()} et
* après que le wrapper aie été sélectionné avec {@link select()}
*/
function getResult($key=false): Result {
return $this->context->result;
}
/** retourner true si la valeur existe */
function isPresent($key=false): bool {
return $this->getResult($key)->present;
}
/** retourner le type associé à la valeur */
function getType($key=false): IType {
return $this->context->type;
}
/** retourner true si la valeur est disponible */
function isAvailable($key=false): bool {
return $this->getResult($key)->available;
}
/** retourner true si la valeur est valide */
function isValid($key=false): bool {
return $this->getResult($key)->valid;
}
/** retourner true si la valeur est dans sa forme normalisée */
function isNormalized($key=false): bool {
return $this->getResult($key)->normalized;
}
function get($default=null, $key=false) {
$context = $this->context;
if (!$context->result->available) return $default;
return $context->input->get($context->valueKey);
}
function set($value, ?array $params=null, $key=false): self {
$context = $this->context;
$context->input->set($value, $context->valueKey);
$this->afterModify($params);
return $this;
}
function unset(?array $params=null, $key=false): self {
$context = $this->context;
$context->input->unset($context->valueKey);
$this->afterModify($params);
return $this;
}
protected function _format(WrapperContext $context, $format=null): string {
$value = $context->input->get($context->valueKey);
/** @var func $formatterFunc */
$formatterFunc = $context->schema->formatterFunc;
if ($formatterFunc !== null) {
# la fonction formatter n'a pas forcément accès au format de la définition
# le lui fournir ici
$format ??= $context->schema->format;
return $formatterFunc->invoke([$value, $format, $context, $this]);
} else {
# on assume que le type a été initialisé avec le format de la définition
# le cas échéant
return $context->type->format($value, $format);
}
}
/** formatter la valeur pour affichage */
function format($format=null, $key=false): string {
return $this->_format($this->context, $format);
}
#############################################################################
# key & properties
function offsetExists($offset): bool {
return in_array($offset, $this->getKeys());
}
function offsetGet($offset) {
return $this->get(null, $offset);
}
function offsetSet($offset, $value): void {
$this->set($value, null, $offset);
}
function offsetUnset($offset): void {
$this->unset(null, $offset);
}
}