240 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nulib\schema;
 | |
| 
 | |
| use ArrayAccess;
 | |
| use IteratorAggregate;
 | |
| use nulib\php\func;
 | |
| use nulib\schema\input\Input;
 | |
| use nulib\schema\types\IType;
 | |
| use nulib\ValueException;
 | |
| 
 | |
| 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(bool $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;
 | |
|   }
 | |
| 
 | |
|   protected function afterModify(?array $params, bool $resetSelectedKey=false): void {
 | |
|     $context = $this->context;
 | |
|     $this->resetContext($resetSelectedKey);
 | |
|     if ($params["analyze"] ?? $context->analyze) {
 | |
|       $this->analyze($params);
 | |
|     }
 | |
|     if ($context->analyzed && ($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;
 | |
| 
 | |
|   protected function getConsolidatedResult(): Result {
 | |
|     return $this->context->result;
 | |
|   }
 | |
| 
 | |
|   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;
 | |
|     }
 | |
| 
 | |
|     $result = $this->getConsolidatedResult();
 | |
|     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
 | |
|    *
 | |
|    * $key peut valoir:
 | |
|    * - false pour la clé courante (ne pas changer la sélection)
 | |
|    * - null ou "" le wrapper de la valeur principale
 | |
|    * - ou toute autre valeur présente dans {@link getKeys()} pour les valeurs
 | |
|    * accessible via cet objet
 | |
|    *
 | |
|    * @param string|int|null $key
 | |
|    * @return Wrapper $this
 | |
|    */
 | |
|   abstract function select($key=null): Wrapper;
 | |
| 
 | |
|   function getIterator() {
 | |
|     foreach ($this->getKeys() as $key) {
 | |
|       yield $key => $this->select($key);
 | |
|     }
 | |
|     $this->select();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * 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()}
 | |
|    *
 | |
|    * $key peut valoir:
 | |
|    * - false pour la clé sélectionnée avec {@link select()}
 | |
|    * - null pour le résultat consolidé
 | |
|    * - "" pour le résultat de l'analyse de la valeur principale
 | |
|    * - ou toute autre valeur présente dans {@link getKeys()} pour le résultat
 | |
|    * de l'analyse des valeurs correspondantes
 | |
|    */
 | |
|   function getResult($key=false): Result {
 | |
|     if ($key === false || $key === "") return $this->context->result;
 | |
|     if ($key === null) return $this->getConsolidatedResult();
 | |
|     throw ValueException::invalid_key($key);
 | |
|   }
 | |
| 
 | |
|   /** 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 nulle */
 | |
|   function isNull($key=false): bool {
 | |
|     return $this->getResult($key)->null;
 | |
|   }
 | |
| 
 | |
|   /** 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);
 | |
|   }
 | |
| }
 |