<?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); } }