<pman>Intégration de la branche dev74
This commit is contained in:
		
						commit
						9d7dbd76d1
					
				| @ -8,6 +8,18 @@ use nulib\cl; | ||||
|  * de {@link IAccess} | ||||
|  */ | ||||
| abstract class AbstractAccess implements IAccess { | ||||
|   const ALLOW_EMPTY = true; | ||||
| 
 | ||||
|   function __construct(?array $params=null) { | ||||
|     $this->allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $allowEmpty; | ||||
| 
 | ||||
|   function isAllowEmpty(): bool { | ||||
|     return $this->allowEmpty; | ||||
|   } | ||||
| 
 | ||||
|   function inc(): int { | ||||
|     $value = (int)$this->get(); | ||||
|     $this->set(++$value); | ||||
| @ -33,4 +45,13 @@ abstract class AbstractAccess implements IAccess { | ||||
|     cl::set($array, $key, $value); | ||||
|     $this->set($array); | ||||
|   } | ||||
| 
 | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void { | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $params=null): void { | ||||
|   } | ||||
| 
 | ||||
|   function ensureOrder(array $keys, ?array $params=null): void { | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										182
									
								
								src/php/access/ChainAccess.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								src/php/access/ChainAccess.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,182 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| use nulib\StateException; | ||||
| use ReflectionClass; | ||||
| use ReflectionException; | ||||
| 
 | ||||
| class ChainAccess extends AbstractAccess { | ||||
|   const ACCESS_AUTO = 0, ACCESS_KEY = 1, ACCESS_PROPERTY = 2; | ||||
| 
 | ||||
|   private static function unexpected_access_type(): StateException { | ||||
|     return StateException::unexpected_state("access_type"); | ||||
|   } | ||||
|    | ||||
|   function __construct(IAccess $access, $key, ?array $params=null) { | ||||
|     parent::__construct(); | ||||
|     $this->access = $access; | ||||
|     $this->key = $key; | ||||
|     $this->accessType = $params["access_type"] ?? self::ACCESS_AUTO; | ||||
|   } | ||||
| 
 | ||||
|   protected IAccess $access; | ||||
| 
 | ||||
|   /** @var null|int|string|array */ | ||||
|   protected $key; | ||||
| 
 | ||||
|   protected int $accessType; | ||||
| 
 | ||||
|   protected function _accessType($access, $key): int { | ||||
|     $accessType = $this->accessType; | ||||
|     if ($accessType === self::ACCESS_AUTO) { | ||||
|       if (is_object($access) && is_string($key)) { | ||||
|         $accessType = self::ACCESS_PROPERTY; | ||||
|       } else { | ||||
|         $accessType = self::ACCESS_KEY; | ||||
|       } | ||||
|     } | ||||
|     return $accessType; | ||||
|   } | ||||
| 
 | ||||
|   protected function _has(): bool { | ||||
|     $src = $this->access->get(); | ||||
|     $key = $this->key; | ||||
|     $accessType = $this->_accessType($src, $key); | ||||
|     if ($accessType === self::ACCESS_KEY) { | ||||
|       return cl::phas($src, $key); | ||||
|     } elseif ($accessType === self::ACCESS_PROPERTY) { | ||||
|       $class = new ReflectionClass($src); | ||||
|       return $class->hasProperty($key) || property_exists($src, $key); | ||||
|     } else { | ||||
|       throw self::unexpected_access_type(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected function _get($default=null) { | ||||
|     $src = $this->access->get(); | ||||
|     $key = $this->key; | ||||
|     $accessType = $this->_accessType($src, $key); | ||||
|     if ($accessType === self::ACCESS_KEY) { | ||||
|       return cl::pget($src, $key); | ||||
|     } elseif ($accessType === self::ACCESS_PROPERTY) { | ||||
|       $class = new ReflectionClass($src); | ||||
|       try { | ||||
|         $property = $class->getProperty($key); | ||||
|         $property->setAccessible(true); | ||||
|       } catch (ReflectionException $e) { | ||||
|         $property = null; | ||||
|       } | ||||
|       if ($property !== null) { | ||||
|         return $property->getValue($src); | ||||
|       } elseif (property_exists($src, $key)) { | ||||
|         return $src->$key; | ||||
|       } else { | ||||
|         return $default; | ||||
|       } | ||||
|     } else { | ||||
|       throw self::unexpected_access_type(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected function _pset(object $dest, $name, $value): void { | ||||
|     $class = new ReflectionClass($dest); | ||||
|     try { | ||||
|       $property = $class->getProperty($name); | ||||
|       $property->setAccessible(true); | ||||
|     } catch (ReflectionException $e) { | ||||
|       $property = null; | ||||
|     } | ||||
|     if ($property !== null) { | ||||
|       $property->setValue($dest, $value); | ||||
|     } else { | ||||
|       $dest->$name = $value; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function isAllowEmpty(): bool { | ||||
|     return $this->access->isAllowEmpty(); | ||||
|   } | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     if (!$this->access->exists()) return false; | ||||
|     if ($this->key === null) return true; | ||||
|     return $this->_has(); | ||||
|   } | ||||
| 
 | ||||
|   function available(): bool { | ||||
|     if (!$this->access->available()) return false; | ||||
|     if ($this->key === null) return true; | ||||
|     if (!$this->_has()) return false; | ||||
|     return $this->isAllowEmpty() || $this->_get() !== ""; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     if ($this->key === null) { | ||||
|       return $this->access->get($default); | ||||
|     } | ||||
|     return $this->_get($default); | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     if ($this->key === null) { | ||||
|       $this->access->set($value); | ||||
|       return; | ||||
|     } | ||||
|     $dest = $this->access->get(); | ||||
|     $key = $this->key; | ||||
|     $accessType = $this->_accessType($dest, $key); | ||||
|     if ($accessType === self::ACCESS_KEY) { | ||||
|       cl::pset($dest, $key, $value); | ||||
|       $this->access->set($dest); | ||||
|     } elseif ($accessType === self::ACCESS_PROPERTY) { | ||||
|       $this->_pset($dest, $key, $value); | ||||
|     } else { | ||||
|       throw self::unexpected_access_type(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     if ($this->key === null) { | ||||
|       $this->access->del(); | ||||
|       return; | ||||
|     } | ||||
|     $dest = $this->access->get(); | ||||
|     $key = $this->key; | ||||
|     $accessType = $this->_accessType($dest, $key); | ||||
|     if ($accessType === self::ACCESS_KEY) { | ||||
|       cl::pdel($dest, $key); | ||||
|       $this->access->set($dest); | ||||
|     } elseif ($accessType === self::ACCESS_PROPERTY) { | ||||
|       $this->_pset($dest, $key, null); | ||||
|     } else { | ||||
|       throw self::unexpected_access_type(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key, ?array $params=null): IAccess { | ||||
|     if ($key === null) return $this; | ||||
|     $accessType = $params["access_type"] ?? $this->accessType; | ||||
|     if ($accessType === self::ACCESS_KEY && $accessType === $this->accessType) { | ||||
|       if ($this->key !== null) $key = cl::merge($this->key, $key); | ||||
|       return new ChainAccess($this->access, $key); | ||||
|     } else { | ||||
|       return new ChainAccess($this, $key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void { | ||||
|     #XXX fonction de $accessType?
 | ||||
|     #$this->access->ensureAssoc($keys, $params);
 | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $params=null): void { | ||||
|     #XXX fonction de $accessType?
 | ||||
|     #$this->access->ensureKeys($defaults, $params);
 | ||||
|   } | ||||
| 
 | ||||
|   function ensureOrder(array $keys, ?array $params=null): void { | ||||
|     #XXX fonction de $accessType?
 | ||||
|     #$this->access->ensureOrder($keys, $params);
 | ||||
|   } | ||||
| } | ||||
| @ -7,67 +7,99 @@ use nulib\cl; | ||||
|  * Class FormAccess: accès à une valeur de $_POST puis $_GET, dans cet ordre | ||||
|  */ | ||||
| class FormAccess extends AbstractAccess { | ||||
|   const ALLOW_EMPTY = false; | ||||
| 
 | ||||
|   function __construct($key, ?array $params=null) { | ||||
|     parent::__construct($params); | ||||
|     $this->key = $key; | ||||
|     $this->allowEmpty = $params["allow_empty"] ?? false; | ||||
|   } | ||||
| 
 | ||||
|   /** @var int|string */ | ||||
|   /** @var null|int|string|array */ | ||||
|   protected $key; | ||||
| 
 | ||||
|   protected bool $allowEmpty; | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|   protected function _exists(array $first, ?array $second=null): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     return array_key_exists($key, $_POST) || array_key_exists($key, $_GET); | ||||
|     if ($key === null) return true; | ||||
|     if (cl::phas($first, $key)) return true; | ||||
|     return $second !== null && cl::phas($second, $key); | ||||
|   } | ||||
| 
 | ||||
|   public function available(): bool { | ||||
|   function exists(): bool { | ||||
|     return $this->_exists($_POST, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   protected function _available(array $first, ?array $second=null): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     if (array_key_exists($key, $_POST)) { | ||||
|       return $this->allowEmpty || $_POST[$key] !== ""; | ||||
|     } elseif (array_key_exists($key, $_GET)) { | ||||
|       return $this->allowEmpty || $_GET[$key] !== ""; | ||||
|     if ($key === null) return true; | ||||
|     if (cl::phas($first, $key)) { | ||||
|       return $this->allowEmpty || cl::pget($first, $key) !== ""; | ||||
|     } elseif ($second !== null && cl::phas($second, $key)) { | ||||
|       return $this->allowEmpty || cl::pget($second, $key) !== ""; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|   public function available(): bool { | ||||
|     return $this->_available($_POST, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   protected function _get($default, array $first, ?array $second=null) { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return $default; | ||||
|     if (array_key_exists($key, $_POST)) { | ||||
|       $value = $_POST[$key]; | ||||
|       if ($value === "" && !$this->allowEmpty) return $default; | ||||
|       return $value; | ||||
|     } elseif (array_key_exists($key, $_GET)) { | ||||
|       $value = $_GET[$key]; | ||||
|       if ($value === "" && !$this->allowEmpty) return $default; | ||||
|       return $value; | ||||
|     } else { | ||||
|     if ($key === null) return cl::merge($first, $second); | ||||
|     if (cl::phas($first, $key)) { | ||||
|       $value = cl::pget($first, $key); | ||||
|       if ($value !== "" || $this->allowEmpty) return $value; | ||||
|     } elseif ($second !== null && cl::phas($second, $key)) { | ||||
|       $value = cl::pget($second, $key); | ||||
|       if ($value !== "" || $this->allowEmpty) return $value; | ||||
|     } | ||||
|     return $default; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     return $this->_get($default, $_POST, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   function _set($value, array &$first, ?array &$second=null): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) { | ||||
|       # interdire la modification de la destination
 | ||||
|       return; | ||||
|     } | ||||
|     if ($second !== null && !cl::phas($first, $key) && cl::phas($second, $key)) { | ||||
|       cl::pset($second, $key, $value); | ||||
|     } else { | ||||
|       cl::pset($first, $key, $value); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     $this->_set($value, $_POST, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   function _del(array &$first, ?array &$second=null): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     if (!array_key_exists($key, $_POST) && array_key_exists($key, $_GET)) { | ||||
|       cl::set($_GET, $key, $value); | ||||
|     if ($key === null) { | ||||
|       # interdire la modification de la destination
 | ||||
|       return; | ||||
|     } | ||||
|     if ($second !== null && !cl::phas($first, $key) && cl::phas($second, $key)) { | ||||
|       cl::pdel($second, $key); | ||||
|     } else { | ||||
|       cl::set($_POST, $key, $value); | ||||
|       cl::pdel($first, $key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     if (!array_key_exists($key, $_POST) && array_key_exists($key, $_GET)) { | ||||
|       cl::del($_GET, $key); | ||||
|     } else { | ||||
|       cl::del($_POST, $key); | ||||
|     } | ||||
|     $this->_del($_POST, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key): self { | ||||
|     if ($key === null) return $this; | ||||
|     if ($this->key !== null) $key = cl::merge($this->key, $key); | ||||
|     return new static($key, [ | ||||
|       "allow_empty" => $this->allowEmpty | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -8,42 +8,22 @@ use nulib\cl; | ||||
|  */ | ||||
| class GetAccess extends FormAccess { | ||||
|   function exists(): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     return array_key_exists($key, $_GET); | ||||
|     return $this->_exists($_GET); | ||||
|   } | ||||
| 
 | ||||
|   public function available(): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     if (array_key_exists($key, $_GET)) { | ||||
|       return $this->allowEmpty || $_GET[$key] !== ""; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|     return $this->_available($_GET); | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return $default; | ||||
|     if (array_key_exists($key, $_GET)) { | ||||
|       $value = $_GET[$key]; | ||||
|       if ($value === "" && !$this->allowEmpty) return $default; | ||||
|       return $value; | ||||
|     } else { | ||||
|       return $default; | ||||
|     } | ||||
|     return $this->_get($default, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     cl::set($_GET, $key, $value); | ||||
|     $this->_set($value, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     cl::del($_GET, $key); | ||||
|     $this->_del($_GET); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| 
 | ||||
| use ReflectionClass; | ||||
| 
 | ||||
| /** | ||||
|  * Interface IAccess: abstraction d'un accès complet à une valeur | ||||
|  */ | ||||
| @ -25,4 +27,26 @@ interface IAccess extends IGetter, ISetter, IDeleter { | ||||
|    * tableau si $key===null | ||||
|    */ | ||||
|   function append($value, $key=null): void; | ||||
| 
 | ||||
|   /** retourner une instance permettant d'accéder à $value[$key] */ | ||||
|   function addKey($key): IAccess; | ||||
| 
 | ||||
|   /** | ||||
|    * s'assurer que la destination est un tableau associatif en remplaçant les | ||||
|    * clés numériques par les clés correspondantes du tableau $keys | ||||
|    */ | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void; | ||||
| 
 | ||||
|   /** | ||||
|    * s'assurer que toutes les clés mentionnées dans le tableau $defaults | ||||
|    * existent. si elles n'existent pas, leur donner la valeur du tableau | ||||
|    * $defaults | ||||
|    */ | ||||
|   function ensureKeys(array $defaults, ?array $params=null): void; | ||||
| 
 | ||||
|   /** | ||||
|    * s'assure que les clés de la destination sont dans l'ordre mentionné dans le | ||||
|    * tableau $keys. toutes les clés supplémentaires sont placées à la fin | ||||
|    */ | ||||
|   function ensureOrder(array $keys, ?array $params=null): void; | ||||
| } | ||||
|  | ||||
| @ -11,6 +11,12 @@ interface IGetter { | ||||
|    */ | ||||
|   function exists(): bool; | ||||
| 
 | ||||
|   /** | ||||
|    * @return bool true si cet objet autorise les chaines vides. si c'est le cas, | ||||
|    * {@link exists()} et {@link available()} sont fonctionnellement identiques | ||||
|    */ | ||||
|   function isAllowEmpty(): bool; | ||||
| 
 | ||||
|   /** @return bool true si la valeur existe et est utilisable, false sinon */ | ||||
|   function available(): bool; | ||||
| 
 | ||||
|  | ||||
| @ -3,67 +3,186 @@ namespace nur\sery\wip\php\access; | ||||
| 
 | ||||
| use ArrayAccess; | ||||
| use nulib\cl; | ||||
| use nulib\ref\schema\ref_schema; | ||||
| 
 | ||||
| /** | ||||
|  * Class KeyAccess: accès à une valeur d'une clé dans un tableau | ||||
|  * Class KeyAccess: accès | ||||
|  * - soit à une valeur d'un chemin de clé dans un tableau (si $key !== null) | ||||
|  * - soit à une valeur scalaire (si $key === null) | ||||
|  */ | ||||
| class KeyAccess extends AbstractAccess { | ||||
|   function __construct(&$array, $key, ?array $params=null) { | ||||
|     $this->array =& $array; | ||||
|   const ALLOW_NULL = null; | ||||
|   const ALLOW_FALSE = null; | ||||
|   const PROTECT_DEST = false; | ||||
| 
 | ||||
|   function __construct(&$dest, $key=null, ?array $params=null) { | ||||
|     parent::__construct($params); | ||||
|     $this->protectDest = $params["protect_dest"] ?? static::PROTECT_DEST; | ||||
|     $this->dest =& $dest; | ||||
|     $this->key = $key; | ||||
|     $this->allowNull = $params["allow_null"] ?? true; | ||||
|     $this->allowFalse = $params["allow_false"] ?? false; | ||||
|     $this->allowEmpty = $params["allow_empty"] ?? true; | ||||
|     $this->allowNull = $params["allow_null"] ?? static::ALLOW_NULL; | ||||
|     $this->allowFalse = $params["allow_false"] ?? static::ALLOW_FALSE; | ||||
|   } | ||||
| 
 | ||||
|   /** @var array|ArrayAccess */ | ||||
|   protected $array; | ||||
|   protected bool $protectDest; | ||||
| 
 | ||||
|   function reset(&$array): self { | ||||
|     $this->array =& $array; | ||||
|   /** @var mixed|array|ArrayAccess */ | ||||
|   protected $dest; | ||||
| 
 | ||||
|   /** @var null|int|string|array */ | ||||
|   protected $key; | ||||
| 
 | ||||
|   function reset(&$dest, $key=null): self { | ||||
|     $this->dest =& $dest; | ||||
|     $this->key = $key; | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** @var int|string */ | ||||
|   protected $key; | ||||
|   function resetKey($key=null): self { | ||||
|     $this->key = $key; | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $allowNull; | ||||
|   protected ?bool $allowNull; | ||||
| 
 | ||||
|   protected bool $allowFalse; | ||||
|   protected function isAllowNull(): bool { | ||||
|     $allowNull = $this->allowNull; | ||||
|     if ($allowNull !== null) return $allowNull; | ||||
|     return $this->key !== null; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $allowEmpty; | ||||
|   protected ?bool $allowFalse; | ||||
| 
 | ||||
|   protected function isAllowFalse(): bool { | ||||
|     $allowFalse = $this->allowFalse; | ||||
|     if ($allowFalse !== null) return $allowFalse; | ||||
|     return $this->key === null; | ||||
|   } | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     return cl::has($this->array, $key); | ||||
|     if ($key === null) { | ||||
|       return $this->isAllowNull() || $this->dest !== null; | ||||
|     } else { | ||||
|       return cl::phas($this->dest, $key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function available(): bool { | ||||
|     if (!$this->exists()) return false; | ||||
|     $value = cl::get($this->array, $this->key); | ||||
|     if ($value === null) return $this->allowNull; | ||||
|     if ($value === false) return $this->allowFalse; | ||||
|     $key = $this->key; | ||||
|     if ($key === null) $value = $this->dest; | ||||
|     else $value = cl::pget($this->dest, $key); | ||||
|     if ($value === "") return $this->allowEmpty; | ||||
|     if ($value === null) return $this->isAllowNull(); | ||||
|     if ($value === false) return $this->isAllowFalse(); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     if ($this->key === null) return $default; | ||||
|     $value = cl::get($this->array, $this->key, $default); | ||||
|     if ($value === null && !$this->allowNull) return $default; | ||||
|     if ($value === false && !$this->allowFalse) return $default; | ||||
|     $key = $this->key; | ||||
|     if ($key === null) $value = $this->dest; | ||||
|     else $value = cl::pget($this->dest, $key, $default); | ||||
|     if ($value === "" && !$this->allowEmpty) return $default; | ||||
|     if ($value === null && !$this->isAllowNull()) return $default; | ||||
|     if ($value === false && !$this->isAllowFalse()) return $default; | ||||
|     return $value; | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     if ($this->key === null) return; | ||||
|     cl::set($this->array, $this->key, $value); | ||||
|     $key = $this->key; | ||||
|     if ($key === null) { | ||||
|       if (!$this->protectDest) $this->dest = $value; | ||||
|     } else { | ||||
|       cl::pset($this->dest, $key, $value); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     if ($this->key === null) return; | ||||
|     cl::del($this->array, $this->key); | ||||
|     $key = $this->key; | ||||
|     if ($key === null) { | ||||
|       if (!$this->protectDest) $this->dest = null; | ||||
|     } else { | ||||
|       cl::pdel($this->dest, $key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key): self { | ||||
|     if ($key === null) return $this; | ||||
|     if ($this->key !== null) $key = cl::merge($this->key, $key); | ||||
|     return new KeyAccess($this->dest, $key, [ | ||||
|       "allow_empty" => $this->allowEmpty, | ||||
|       "allow_null" => $this->allowNull, | ||||
|       "allow_false" => $this->allowFalse, | ||||
|       "protect_dest" => $this->protectDest, | ||||
|     ]); | ||||
|   } | ||||
| 
 | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void { | ||||
|     $dest =& $this->dest; | ||||
|     $prefix = $params["key_prefix"] ?? null; | ||||
|     $suffix = $params["key_suffix"] ?? null; | ||||
|     $index = 0; | ||||
|     foreach ($keys as $key) { | ||||
|       if ($prefix !== null || $suffix !== null) { | ||||
|         $destKey = "$prefix$key$suffix"; | ||||
|       } else { | ||||
|         # préserver les clés numériques
 | ||||
|         $destKey = $key; | ||||
|       } | ||||
|       if ($dest !== null && array_key_exists($destKey, $dest)) continue; | ||||
|       while (in_array($index, $keys, true)) { | ||||
|         $index++; | ||||
|       } | ||||
|       if ($dest !== null && array_key_exists($index, $dest)) { | ||||
|         $dest[$destKey] = $dest[$index]; | ||||
|         unset($dest[$index]); | ||||
|         $index++; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $params=null): void { | ||||
|     $dest =& $this->dest; | ||||
|     $keys = array_keys($defaults); | ||||
|     $prefix = $params["key_prefix"] ?? null; | ||||
|     $suffix = $params["key_suffix"] ?? null; | ||||
|     foreach ($keys as $key) { | ||||
|       $destKey = "$prefix$key$suffix"; | ||||
|       if ($dest === null || !array_key_exists($destKey, $dest)) { | ||||
|         $dest[$destKey] = $defaults[$key]; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function ensureOrder(array $keys, ?array $params=null): void { | ||||
|     $dest =& $this->dest; | ||||
|     if ($dest === null) return; | ||||
| 
 | ||||
|     $prefix = $params["key_prefix"] ?? null; | ||||
|     $suffix = $params["key_suffix"] ?? null; | ||||
|     if ($prefix !== null || $suffix !== null) { | ||||
|       foreach ($keys as &$key) { | ||||
|         $key = "$prefix$key$suffix"; | ||||
|       }; unset($key); | ||||
|     } | ||||
| 
 | ||||
|     $destKeys = array_keys($dest); | ||||
|     $keyCount = count($keys); | ||||
|     if (array_slice($destKeys, 0, $keyCount) === $keys) { | ||||
|       # si le tableau a déjà les bonnes clés dans le bon ordre, rien à faire
 | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     $ordered = []; | ||||
|     foreach ($keys as $key) { | ||||
|       if (array_key_exists($key, $dest)) { | ||||
|         $ordered[$key] = $dest[$key]; | ||||
|         unset($dest[$key]); | ||||
|       } | ||||
|     } | ||||
|     $preserveKeys = $params["preserve_keys"] ?? false; | ||||
|     if ($preserveKeys) $dest = cl::merge2($ordered, $dest); | ||||
|     else $dest = array_merge($ordered, $dest); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -8,42 +8,22 @@ use nulib\cl; | ||||
|  */ | ||||
| class PostAccess extends FormAccess { | ||||
|   function exists(): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     return array_key_exists($key, $_POST); | ||||
|     return $this->_exists($_POST); | ||||
|   } | ||||
| 
 | ||||
|   public function available(): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     if (array_key_exists($key, $_POST)) { | ||||
|       return $this->allowEmpty || $_POST[$key] !== ""; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|     return $this->_available($_POST); | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return $default; | ||||
|     if (array_key_exists($key, $_POST)) { | ||||
|       $value = $_POST[$key]; | ||||
|       if ($value === "" && !$this->allowEmpty) return $default; | ||||
|       return $value; | ||||
|     } else { | ||||
|       return $default; | ||||
|     } | ||||
|     return $this->_get($default, $_POST); | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     cl::set($_POST, $key, $value); | ||||
|     $this->_set($value, $_POST); | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     cl::del($_POST, $key); | ||||
|     $this->_del($_POST); | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										175
									
								
								src/php/access/PropertyAccess.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								src/php/access/PropertyAccess.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,175 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| 
 | ||||
| use nulib\StateException; | ||||
| use nulib\str; | ||||
| use ReflectionClass; | ||||
| use ReflectionException; | ||||
| use ReflectionNamedType; | ||||
| use ReflectionProperty; | ||||
| use stdClass; | ||||
| 
 | ||||
| class PropertyAccess extends AbstractAccess { | ||||
|   const PROTECT_DEST = true; | ||||
|   const MAP_NAMES = true; | ||||
|   const ALLOW_NULL = true; | ||||
|   const ALLOW_FALSE = false; | ||||
| 
 | ||||
|   function __construct(?object $dest, ?string $name=null, ?array $params=null) { | ||||
|     parent::__construct($params); | ||||
|     $this->protectDest = $params["protect_dest"] ?? static::PROTECT_DEST; | ||||
|     $this->mapNames = $params["map_names"] ?? static::MAP_NAMES; | ||||
|     $this->_setName($name); | ||||
|     $this->_setDest($dest); | ||||
|     $this->allowNull = $params["allow_null"] ?? static::ALLOW_NULL; | ||||
|     $this->allowFalse = $params["allow_false"] ?? static::ALLOW_FALSE; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $protectDest; | ||||
| 
 | ||||
|   protected ?object $dest; | ||||
|   protected bool $mapNames; | ||||
| 
 | ||||
|   protected ?string $name; | ||||
| 
 | ||||
|   protected ?ReflectionProperty $property; | ||||
| 
 | ||||
|   private function _getName(string $key): string { | ||||
|     return $this->mapNames? str::us2camel($key): $key; | ||||
|   } | ||||
|   private function _setName(?string $name): void { | ||||
|     if ($name !== null) $name = $this->_getName($name); | ||||
|     $this->name = $name; | ||||
|   } | ||||
| 
 | ||||
|   private function _getProperty(?string $name, ?ReflectionClass $class, ?object $object=null): ?ReflectionProperty { | ||||
|     $property = null; | ||||
|     if ($class === null && $object !== null) { | ||||
|       $class = new ReflectionClass($object); | ||||
|     } | ||||
|     if ($class !== null && $name !== null) { | ||||
|       try { | ||||
|         $property = $class->getProperty($name); | ||||
|         $property->setAccessible(true); | ||||
|       } catch (ReflectionException $e) { | ||||
|       } | ||||
|     } | ||||
|     return $property; | ||||
|   } | ||||
| 
 | ||||
|   private function _setDest(?object $dest): void { | ||||
|     $this->dest = $dest; | ||||
|     $this->property = $this->_getProperty($this->name, null, $dest); | ||||
|   } | ||||
| 
 | ||||
|   function reset(?object $dest, ?string $name=null): self { | ||||
|     $this->_setName($name); | ||||
|     $this->_setDest($dest); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function resetKey($name=null): self { | ||||
|     $this->_setName($name); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $allowNull; | ||||
| 
 | ||||
|   protected bool $allowFalse; | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     $name = $this->name; | ||||
|     if ($this->dest === null) return false; | ||||
|     return $name === null | ||||
|       || $this->property !== null | ||||
|       || property_exists($this->dest, $name); | ||||
|   } | ||||
| 
 | ||||
|   protected function _get($default=null) { | ||||
|     $name = $this->name; | ||||
|     $property = $this->property; | ||||
|     if ($this->dest === null) { | ||||
|       return $default; | ||||
|     } elseif ($name === null) { | ||||
|       return $this->dest; | ||||
|     } elseif ($property !== null) { | ||||
|       return $property->getValue($this->dest); | ||||
|     } elseif (property_exists($this->dest, $name)) { | ||||
|       return $this->dest->$name; | ||||
|     } else { | ||||
|       return $default; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function available(): bool { | ||||
|     if (!$this->exists()) return false; | ||||
|     $value = $this->_get(); | ||||
|     if ($value === "") return $this->allowEmpty; | ||||
|     if ($value === null) return $this->allowNull; | ||||
|     if ($value === false) return $this->allowFalse; | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     if (!$this->exists()) return $default; | ||||
|     $value = $this->_get(); | ||||
|     if ($value === "" && !$this->allowEmpty) return $default; | ||||
|     if ($value === null && !$this->allowNull) return $default; | ||||
|     if ($value === false && !$this->allowFalse) return $default; | ||||
|     return $value; | ||||
|   } | ||||
| 
 | ||||
|   protected function _set($value): void { | ||||
|     $name = $this->name; | ||||
|     $property = $this->property; | ||||
|     if ($this->dest === null) { | ||||
|       throw StateException::unexpected_state("dest is null"); | ||||
|     } elseif ($name === null) { | ||||
|       if (!$this->protectDest) $this->_setDest($value); | ||||
|     } elseif ($property !== null) { | ||||
|       $property->setValue($this->dest, $value); | ||||
|     } else { | ||||
|       $this->dest->$name = $value; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     $this->_set($value); | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     $this->_set(null); | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key): IAccess { | ||||
|     if ($key === null) return $this; | ||||
|     return new ChainAccess($this, $key); | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $params=null): void { | ||||
|     $dest = $this->dest; | ||||
|     if ($dest === null) { | ||||
|       # comme ne connait pas la classe de l'objet destination, on n'essaie pas
 | ||||
|       # de le créer
 | ||||
|       return; | ||||
|     } | ||||
|     $class = new ReflectionClass($dest); | ||||
|     $keys = array_keys($defaults); | ||||
|     $prefix = $params["key_prefix"] ?? null; | ||||
|     $suffix = $params["key_suffix"] ?? null; | ||||
|     foreach ($keys as $key) { | ||||
|       $name = $this->_getName("$prefix$key$suffix"); | ||||
|       $property = $this->_getProperty($name, $class); | ||||
|       if ($property !== null) { | ||||
|         $type = $property->getType(); | ||||
|         if ($type !== null && !$property->isInitialized($dest) && $type->allowsNull()) { | ||||
|           # initialiser avec null au lieu de $defaults[$key] pour respecter le
 | ||||
|           # type de la propriété
 | ||||
|           $property->setValue($dest, null); | ||||
|         } | ||||
|       } elseif (!property_exists($dest, $name)) { | ||||
|         $dest->$name = $defaults[$key]; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -16,6 +16,7 @@ namespace nur\sery\wip\php\access; | ||||
|  */ | ||||
| class ShadowAccess extends AbstractAccess { | ||||
|   function __construct(IAccess $reader, IAccess $writer) { | ||||
|     parent::__construct(); | ||||
|     $this->reader = $reader; | ||||
|     $this->writer = $writer; | ||||
|     $this->getter = $reader; | ||||
| @ -27,6 +28,10 @@ class ShadowAccess extends AbstractAccess { | ||||
| 
 | ||||
|   protected IGetter $getter; | ||||
| 
 | ||||
|   public function isAllowEmpty(): bool { | ||||
|     return $this->getter->isAllowEmpty(); | ||||
|   } | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     return $this->getter->exists(); | ||||
|   } | ||||
| @ -48,4 +53,20 @@ class ShadowAccess extends AbstractAccess { | ||||
|     $this->writer->del(); | ||||
|     $this->getter = $this->reader; | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key): IAccess { | ||||
|     return new ChainAccess($this, $key); | ||||
|   } | ||||
| 
 | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void { | ||||
|     $this->writer->ensureAssoc($keys, $params); | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $params=null): void { | ||||
|     $this->writer->ensureKeys($defaults, $params); | ||||
|   } | ||||
| 
 | ||||
|   function ensureOrder(array $keys, ?array $params=null): void { | ||||
|     $this->writer->ensureOrder($keys, $params); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,56 +0,0 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| 
 | ||||
| /** | ||||
|  * Class ValueAccess: accès à une valeur unitaire | ||||
|  */ | ||||
| class ValueAccess extends AbstractAccess { | ||||
|   function __construct(&$value, ?array $params=null) { | ||||
|     $this->value =& $value; | ||||
|     $this->allowNull = $params["allow_null"] ?? false; | ||||
|     $this->allowFalse = $params["allow_false"] ?? true; | ||||
|     $this->allowEmpty = $params["allow_empty"] ?? true; | ||||
|   } | ||||
| 
 | ||||
|   /** @var mixed */ | ||||
|   protected $value; | ||||
| 
 | ||||
|   function reset(&$value): self { | ||||
|     $this->value =& $value; | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $allowNull; | ||||
| 
 | ||||
|   protected bool $allowFalse; | ||||
| 
 | ||||
|   protected bool $allowEmpty; | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     return $this->allowNull || $this->value !== null; | ||||
|   } | ||||
| 
 | ||||
|   function available(): bool { | ||||
|     if (!$this->exists()) return false; | ||||
|     $value = $this->value; | ||||
|     if ($value === false) return $this->allowFalse; | ||||
|     if ($value === "") return $this->allowEmpty; | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     $value = $this->value; | ||||
|     if ($value === null && !$this->allowNull) return $default; | ||||
|     if ($value === false && !$this->allowFalse) return $default; | ||||
|     if ($value === "" && !$this->allowEmpty) return $default; | ||||
|     return $value; | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     $this->value = $value; | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     $this->value = null; | ||||
|   } | ||||
| } | ||||
| @ -2,9 +2,7 @@ | ||||
| namespace nur\sery\wip\schema; | ||||
| 
 | ||||
| use IteratorAggregate; | ||||
| use nur\sery\wip\schema\_assoc\AssocResult; | ||||
| use nur\sery\wip\schema\_list\ListResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use Throwable; | ||||
| 
 | ||||
| /** | ||||
|  * Class Result: résultat de l'analyse ou de la normalisation d'une valeur | ||||
| @ -17,6 +15,8 @@ use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
|  * @property bool $normalized si la valeur est valide, est-elle normalisée? | ||||
|  * @property string|null $messageKey clé de message si la valeur n'est pas valide | ||||
|  * @property string|null $message message si la valeur n'est pas valide | ||||
|  * @property Throwable|null $exception l'exception qui a fait échouer la | ||||
|  * validation le cas échéant | ||||
|  * @property string|null $origValue valeur originale avant extraction et analyse | ||||
|  * @property mixed|null $normalizedValue la valeur normalisée si elle est | ||||
|  *  disponible, null sinon. ce champ est utilisé comme optimisation si la valeur | ||||
| @ -26,7 +26,7 @@ abstract class Result implements IteratorAggregate { | ||||
|   const KEYS = [ | ||||
|     "resultAvailable", | ||||
|     "present", "available", "null", "valid", "normalized", | ||||
|     "messageKey", "message", | ||||
|     "messageKey", "message", "exception", | ||||
|     "origValue", "normalizedValue", | ||||
|   ]; | ||||
| 
 | ||||
| @ -34,10 +34,6 @@ abstract class Result implements IteratorAggregate { | ||||
|     $this->reset(); | ||||
|   } | ||||
| 
 | ||||
|   function isAssoc(?AssocResult &$result=null): bool { return false; } | ||||
|   function isList(?ListResult &$result=null): bool { return false; } | ||||
|   function isScalar(?ScalarResult &$result=null): bool { return false; } | ||||
| 
 | ||||
|   /** | ||||
|    * Obtenir la liste des clés valides pour les valeurs accessibles via cet | ||||
|    * objet | ||||
|  | ||||
| @ -17,6 +17,29 @@ use nur\sery\wip\schema\types\tcontent; | ||||
| use nur\sery\wip\schema\types\tpkey; | ||||
| use nur\sery\wip\schema\types\trawstring; | ||||
| 
 | ||||
| /** | ||||
|  * Class Schema | ||||
|  * | ||||
|  * @property-read array|IType $type | ||||
|  * @property-read mixed $default | ||||
|  * @property-read string|null $title | ||||
|  * @property-read bool $required | ||||
|  * @property-read bool $nullable | ||||
|  * @property-read string|array|null $desc | ||||
|  * @property-read callable|null $analyzerFunc | ||||
|  * @property-read callable|null $extractorFunc | ||||
|  * @property-read callable|null $parserFunc | ||||
|  * @property-read callable|null $normalizerFunc | ||||
|  * @property-read array|null $messages | ||||
|  * @property-read callable|null $formatterFunc | ||||
|  * @property-read mixed $format | ||||
|  * @property-read array $nature | ||||
|  * @property-read array|null $schema | ||||
|  * @property-read string|int|null $name | ||||
|  * @property-read string|array|null $pkey | ||||
|  * @property-read string|null $header | ||||
|  * @property-read bool|null $computed | ||||
|  */ | ||||
| abstract class Schema implements ArrayAccess { | ||||
|   /** | ||||
|    * créer le cas échéant une nouvelle instance de {@link Schema} à partir d'une | ||||
| @ -57,7 +80,7 @@ abstract class Schema implements ArrayAccess { | ||||
|       # schéma d'un scalaire quelconque), on ne l'autorise pas ici
 | ||||
|       throw SchemaException::invalid_schema("definition is required"); | ||||
|     } | ||||
|     return self::ns($schema, $definition)->getWrapper($value, $valueKey, $wrapper); | ||||
|     return self::ns($schema, $definition)->getWrapper($value, $valueKey, null, $wrapper); | ||||
|   } | ||||
| 
 | ||||
|   protected static function have_nature(array $definition, ?string &$nature=null): bool { | ||||
| @ -75,18 +98,18 @@ abstract class Schema implements ArrayAccess { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   protected static function _normalize(&$definition, $definitionKey=null): void { | ||||
|   protected static function _normalize_definition(&$definition, $definitionKey=null, ?array $natureMetaschema=null): void { | ||||
|     if (!is_array($definition)) $definition = [$definition]; | ||||
|     # s'assurer que toutes les clés existent avec leur valeur par défaut
 | ||||
|     $index = 0; | ||||
|     foreach (array_keys(ref_schema::SCALAR_METASCHEMA) as $key) { | ||||
|     foreach (array_keys(ref_schema::VALUE_METASCHEMA) as $key) { | ||||
|       if (!array_key_exists($key, $definition)) { | ||||
|         if (array_key_exists($index, $definition)) { | ||||
|           $definition[$key] = $definition[$index]; | ||||
|           unset($definition[$index]); | ||||
|           $index++; | ||||
|         } else { | ||||
|           $definition[$key] = ref_schema::SCALAR_METASCHEMA[$key][1]; | ||||
|           $definition[$key] = ref_schema::VALUE_METASCHEMA[$key][1]; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| @ -138,6 +161,12 @@ abstract class Schema implements ArrayAccess { | ||||
|     # nature
 | ||||
|     $nature = $definition[""]; | ||||
|     tarray::ensure_array($nature); | ||||
|     $natureMetaschema ??= ref_schema::NATURE_METASCHEMA; | ||||
|     foreach (array_keys($natureMetaschema) as $key) { | ||||
|       if (!array_key_exists($key, $nature)) { | ||||
|         $nature[$key] = $natureMetaschema[$key][1]; | ||||
|       } | ||||
|     } | ||||
|     $definition[""] = $nature; | ||||
|     # name, pkey, header
 | ||||
|     $name = $definition["name"]; | ||||
| @ -169,11 +198,11 @@ abstract class Schema implements ArrayAccess { | ||||
|     switch ($nature[0] ?? null) { | ||||
|     case "assoc": | ||||
|       foreach ($definition["schema"] as $key => &$keydef) { | ||||
|         self::_normalize($keydef, $key); | ||||
|         self::_normalize_definition($keydef, $key); | ||||
|       }; unset($keydef); | ||||
|       break; | ||||
|     case "list": | ||||
|       self::_normalize($definition["schema"]); | ||||
|       self::_normalize_definition($definition["schema"]); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| @ -246,6 +275,13 @@ abstract class Schema implements ArrayAccess { | ||||
|     return $this->_definition; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * retourner la liste des clés valides pour l'accès aux valeurs et résultats | ||||
|    */ | ||||
|   abstract function getKeys(): array; | ||||
| 
 | ||||
|   abstract function getSchema($key): Schema; | ||||
| 
 | ||||
|   /** retourner true si le schéma est de nature tableau associatif */ | ||||
|   function isAssoc(?AssocSchema &$schema=null): bool { return false; } | ||||
|   /** retourner true si le schéma est de nature liste */ | ||||
| @ -253,7 +289,9 @@ abstract class Schema implements ArrayAccess { | ||||
|   /** retourner true si le schéma est de nature scalaire */ | ||||
|   function isScalar(?ScalarSchema &$schema=null): bool { return false; } | ||||
| 
 | ||||
|   abstract function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): Wrapper; | ||||
|   abstract protected function newWrapper(): Wrapper; | ||||
| 
 | ||||
|   abstract function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): Wrapper; | ||||
| 
 | ||||
|   #############################################################################
 | ||||
|   # key & properties
 | ||||
| @ -272,7 +310,15 @@ abstract class Schema implements ArrayAccess { | ||||
|     throw AccessException::read_only(null, $offset); | ||||
|   } | ||||
| 
 | ||||
|   const _PROPERTY_PKEYS = []; | ||||
|   const _PROPERTY_PKEYS = [ | ||||
|     "analyzerFunc" => "analyzer_func", | ||||
|     "extractorFunc" => "extractor_func", | ||||
|     "parserFunc" => "parser_func", | ||||
|     "normalizerFunc" => "normalizer_func", | ||||
|     "formatterFunc" => "formatter_func", | ||||
|     "nature" => ["", 0], | ||||
|   ]; | ||||
| 
 | ||||
|   function __get($name) { | ||||
|     $pkey = cl::get(static::_PROPERTY_PKEYS, $name, $name); | ||||
|     return cl::pget($this->definition, $pkey); | ||||
|  | ||||
| @ -1,20 +1,32 @@ | ||||
| # nulib\schema | ||||
| 
 | ||||
| * instance de WrapperContext directement dans le schéma  | ||||
| * plus de {key} ni {orig} dans messages | ||||
|   * les messages standard ne sont utilisés que s'il n'y a pas de message dans | ||||
|     l'exception | ||||
|   * si instance de UserException, prendre le message "non technique" pour | ||||
|     résultat | ||||
| * ensureKeys() et orderKeys() se fait au niveau de access (ou input?) | ||||
|   * access/input ne pouvant pas connaître les valeurs appropriées, c'est le | ||||
|     schéma qui les génère. ensureKeys($values) | ||||
|   * méthode ensureAssoc() transforme les clés séquentielles en clés associatives | ||||
| * l'ordre est `ensureAssoc [--> ensureKeys] [--> orderKeys]` | ||||
| 
 | ||||
| * rajouter l'attribut "size" pour spécifier la taille maximale des valeurs | ||||
|   * cela pourrait servir pour générer automatiquement des tables SQL | ||||
|   * ou pour modéliser un schéma FSV | ||||
| 
 | ||||
| * valeurs composite/computed | ||||
|   * analyse / vérification de la valeur complète après calcul du résultat, si | ||||
|     tous les résultats sont bons | ||||
|   * calcul des valeurs composites/computed par une fonction avant/après l'analyse | ||||
|     globale si résultat ok | ||||
|   * fonction getter_func, setter_func, deleter_func pour les propriétés de type | ||||
|     computed | ||||
| * tdate et tdatetime. qu'en est-il des autres classes (delay, etc.) | ||||
|   * possibilité de spécifier le format de la date à analyser  | ||||
| * ScalarSchema::from_property() | ||||
| 
 | ||||
| * l'argument $format de AssocWrapper::format() est un tableau associatif | ||||
|   `[$key => $format]` | ||||
|   cela permet de spécifier des format spécifiques pour certains champs. | ||||
|   * cela signifie que la valeur de retour n'est pas string :-( | ||||
|     retourner string|array | ||||
| 
 | ||||
| * dans AssocSchema, support `[key_prefix]` qui permet de spécifier un préfixe | ||||
|   commun aux champs dans le tableau destination, e.g | ||||
|   ~~~php | ||||
| @ -58,6 +70,8 @@ | ||||
| 
 | ||||
|   la définition de ces "circonstances" est encore à faire: soit un paramètre | ||||
|   lors de la définition du schéma, soit un truc magique du genre "toutes les | ||||
|   valeurs séquentielles sont des clés du schéma" | ||||
|   valeurs séquentielles sont des clés du schéma", soit un mode automatique | ||||
|   activé par un paramètre où une valeur "val" devient "val"=>true si la clé | ||||
|   "val" existe dans le schéma | ||||
| 
 | ||||
| -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary | ||||
| @ -3,9 +3,12 @@ namespace nur\sery\wip\schema; | ||||
| 
 | ||||
| use ArrayAccess; | ||||
| use IteratorAggregate; | ||||
| use nulib\php\func; | ||||
| use nur\sery\wip\schema\_assoc\AssocWrapper; | ||||
| use nur\sery\wip\schema\_list\ListWrapper; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarWrapper; | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| 
 | ||||
| abstract class Wrapper implements ArrayAccess, IteratorAggregate { | ||||
| @ -13,8 +16,92 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { | ||||
|   function isList(?ListWrapper &$wrapper=null): bool { return false; } | ||||
|   function isScalar(?ScalarWrapper &$wrapper=null): bool { return false; } | ||||
| 
 | ||||
|   /** spécifier la valeur destination gérée par cet objet */ | ||||
|   abstract function reset(&$value, $valueKey=null, ?bool $verifix=null): self; | ||||
|   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; | ||||
|     $context->type = null; | ||||
|     $context->result->reset(); | ||||
|     $context->analyzed = false; | ||||
|     $context->normalized = false; | ||||
|   } | ||||
| 
 | ||||
|   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 | ||||
| @ -38,37 +125,80 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * obtenir le résultat de l'appel d'une des fonctions {@link set()} ou | ||||
|    * {@link unset()} | ||||
|    * 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()} | ||||
|    */ | ||||
|   abstract function getResult(): Result; | ||||
|   function getResult($key=false): Result { | ||||
|     return $this->context->result; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner true si la valeur existe */ | ||||
|   abstract function isPresent(): bool; | ||||
|   function isPresent($key=false): bool { | ||||
|     return $this->getResult($key)->present; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner le type associé à la valeur */ | ||||
|   abstract function getType(): IType; | ||||
|   function getType($key=false): IType { | ||||
|     return $this->context->type; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner true si la valeur est disponible */ | ||||
|   abstract function isAvailable(): bool; | ||||
|   function isAvailable($key=false): bool { | ||||
|     return $this->getResult($key)->available; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner true si la valeur est valide */ | ||||
|   abstract function isValid(): bool; | ||||
|   function isValid($key=false): bool { | ||||
|     return $this->getResult($key)->valid; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner true si la valeur est dans sa forme normalisée */ | ||||
|   abstract function isNormalized(): bool; | ||||
|   function isNormalized($key=false): bool { | ||||
|     return $this->getResult($key)->normalized; | ||||
|   } | ||||
| 
 | ||||
|   /** obtenir la valeur */ | ||||
|   abstract function get($default=null); | ||||
|   function get($default=null, $key=false) { | ||||
|     $context = $this->context; | ||||
|     if (!$context->result->available) return $default; | ||||
|     return $context->input->get($context->valueKey); | ||||
|   } | ||||
| 
 | ||||
|   /** remplacer la valeur */ | ||||
|   abstract function set($value): self; | ||||
|   function set($value, ?array $params=null, $key=false): self { | ||||
|     $context = $this->context; | ||||
|     $context->input->set($value, $context->valueKey); | ||||
|     $this->afterModify($params); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** supprimer la valeur */ | ||||
|   abstract function unset(): self; | ||||
|   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 */ | ||||
|   abstract function format($format=null): string; | ||||
|   function format($format=null, $key=false): string { | ||||
|     return $this->_format($this->context, $format); | ||||
|   } | ||||
| 
 | ||||
|   #############################################################################
 | ||||
|   # key & properties
 | ||||
| @ -78,14 +208,14 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { | ||||
|   } | ||||
| 
 | ||||
|   function offsetGet($offset) { | ||||
|     return $this->select($offset); | ||||
|     return $this->get(null, $offset); | ||||
|   } | ||||
| 
 | ||||
|   function offsetSet($offset, $value): void { | ||||
|     $this->select($offset)->set($value); | ||||
|     $this->set($value, null, $offset); | ||||
|   } | ||||
| 
 | ||||
|   function offsetUnset($offset): void { | ||||
|     $this->select($offset)->unset(); | ||||
|     $this->unset(null, $offset); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -5,26 +5,44 @@ use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| 
 | ||||
| class WrapperContext { | ||||
|   function __construct(Schema $schema, Wrapper $wrapper, Input $input, $valueKey, Result $result) { | ||||
|   const DEFAULT_ANALYZE = true; | ||||
|   const DEFAULT_NORMALIZE = true; | ||||
|   const DEFAULT_THROW = true; | ||||
| 
 | ||||
|   function __construct(Schema $schema, ?Input $input, $valueKey, ?array $params) { | ||||
|     $this->resetParams($params); | ||||
|     $this->schema = $schema; | ||||
|     $this->wrapper = $wrapper; | ||||
|     $this->input = $input; | ||||
|     $this->result = $result; | ||||
|     $this->type = null; | ||||
|     $this->origValue = null; | ||||
|     $this->value = null; | ||||
|     if ($input !== null) $this->input = $input; | ||||
|     $this->valueKey = $valueKey; | ||||
|   } | ||||
| 
 | ||||
|   public Schema $schema; | ||||
|   public Wrapper $wrapper; | ||||
|   public Input $input; | ||||
|   public Result $result; | ||||
|   public ?IType $type; | ||||
|   /** @var mixed */ | ||||
|   public $origValue; | ||||
|   /** @var mixed */ | ||||
|   public $value; | ||||
|   /** @var int|string|null */ | ||||
|   public $valueKey; | ||||
|   public ?array $params; | ||||
|   public bool $analyze, $analyzed = false; | ||||
|   public bool $normalize, $normalized = false; | ||||
|   public ?bool $throw; | ||||
| 
 | ||||
|   function resetParams(?array $params): void { | ||||
|     $this->params = $params; | ||||
|     $this->analyze = $params["analyze"] ?? self::DEFAULT_ANALYZE; | ||||
|     $this->normalize = $params["normalize"] ?? self::DEFAULT_NORMALIZE; | ||||
|     $this->throw = $params["throw"] ?? self::DEFAULT_THROW; | ||||
|   } | ||||
| 
 | ||||
|   /** schéma de la valeur */ | ||||
|   public Schema $schema; | ||||
|   /** source et destination de la valeur */ | ||||
|   public Input $input; | ||||
|   /** @var string|int|null clé de la valeur dans le tableau destination */ | ||||
|   public $valueKey; | ||||
|   /** @var mixed */ | ||||
|   public $origValue = null; | ||||
|   /** @var mixed */ | ||||
|   public $value = null; | ||||
| 
 | ||||
|   /** @var string|int|null clé sélectionnée */ | ||||
|   public $selectedKey = null; | ||||
|   /** type de la valeur de la clé sélectionnée après analyse */ | ||||
|   public ?IType $type = null; | ||||
|   /** résultat de l'analyse de la valeur de la clé sélectionnée */ | ||||
|   public ?Result $result = null; | ||||
| } | ||||
|  | ||||
| @ -1,53 +0,0 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_assoc; | ||||
| 
 | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\Result; | ||||
| 
 | ||||
| class AssocResult extends Result { | ||||
|   function __construct(Result $arrayResult, array &$keyResults) { | ||||
|     $this->arrayResult = $arrayResult; | ||||
|     $this->keyResults =& $keyResults; | ||||
|     $this->result =& $this->arrayResult; | ||||
|     parent::__construct(); | ||||
|   } | ||||
| 
 | ||||
|   function isAssoc(?AssocResult &$result=null): bool { $result = $this; return true;} | ||||
| 
 | ||||
|   protected Result $arrayResult; | ||||
| 
 | ||||
|   /** @var Result[] */ | ||||
|   protected array $keyResults; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return array_keys($this->keyResults); | ||||
|   } | ||||
| 
 | ||||
|   protected Result $result; | ||||
| 
 | ||||
|   function select($key): Result { | ||||
|     if ($key === null) { | ||||
|       $this->result =& $this->arrayResult; | ||||
|     } elseif (array_key_exists($key, $this->keyResults)) { | ||||
|       $this->result =& $this->keyResults[$key]; | ||||
|     } else { | ||||
|       throw ValueException::invalid_key($key); | ||||
|     } | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function reset(): void { | ||||
|     $this->arrayResult->reset(); | ||||
|     foreach ($this->keyResults as $result) { | ||||
|       $result->reset(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function __get(string $name) { | ||||
|     return $this->result[$name]; | ||||
|   } | ||||
| 
 | ||||
|   function __set(string $name, $value): void { | ||||
|     $this->result[$name] = $value; | ||||
|   } | ||||
| } | ||||
| @ -3,6 +3,7 @@ namespace nur\sery\wip\schema\_assoc; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| 
 | ||||
| @ -10,12 +11,9 @@ use nur\sery\wip\schema\Wrapper; | ||||
|  * Class AssocSchema | ||||
|  */ | ||||
| class AssocSchema extends Schema { | ||||
|   /** @var array meta-schema d'un schéma de nature tableau associatif */ | ||||
|   const METASCHEMA = ref_schema::ASSOC_METASCHEMA; | ||||
| 
 | ||||
|   /** | ||||
|    * indiquer si $definition est une définition de schéma de nature tableau | ||||
|    * associatif que {@link normalize()} pourrait normaliser | ||||
|    * associatif que {@link normalize_definition()} pourrait normaliser | ||||
|    */ | ||||
|   static function isa_definition($definition): bool { | ||||
|     if (!is_array($definition)) return false; | ||||
| @ -27,7 +25,7 @@ class AssocSchema extends Schema { | ||||
|     return !cl::have_num_keys($definition); | ||||
|   } | ||||
| 
 | ||||
|   static function normalize($definition, $definitionKey=null): array { | ||||
|   static function normalize_definition($definition, $definitionKey=null): array { | ||||
|     if (!is_array($definition)) $definition = [$definition]; | ||||
|     if (!self::have_nature($definition)) { | ||||
|       $definition = [ | ||||
| @ -36,7 +34,8 @@ class AssocSchema extends Schema { | ||||
|         "schema" => $definition, | ||||
|       ]; | ||||
|     } | ||||
|     self::_normalize($definition, $definitionKey); | ||||
|     $natureMetaschema = array_merge(ref_schema::NATURE_METASCHEMA, ref_schema::ASSOC_NATURE_METASCHEMA); | ||||
|     self::_normalize_definition($definition, $definitionKey, $natureMetaschema); | ||||
|     self::_ensure_nature($definition, "assoc", "array"); | ||||
|     return $definition; | ||||
|   } | ||||
| @ -44,12 +43,17 @@ class AssocSchema extends Schema { | ||||
|   function __construct($definition=null, $definitionKey=null, bool $normalize=true) { | ||||
|     if ($definition === null) $definition = static::SCHEMA; | ||||
|     if ($normalize) { | ||||
|       $definition = self::normalize($definition, $definitionKey); | ||||
|       $definition = self::normalize_definition($definition, $definitionKey); | ||||
|       $this->_definition = $definition; | ||||
|       self::_ensure_type($definition); | ||||
|       self::_ensure_schema_instances($definition); | ||||
|     } | ||||
|     $this->definition = $definition; | ||||
|     $keys = []; | ||||
|     foreach ($definition["schema"] as $key => $schema) { | ||||
|       if (!$schema["computed"]) $keys[] = $key; | ||||
|     } | ||||
|     $this->keys = $keys; | ||||
|   } | ||||
| 
 | ||||
|   function isAssoc(?AssocSchema &$schema=null): bool { | ||||
| @ -57,12 +61,29 @@ class AssocSchema extends Schema { | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   protected array $keys; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return $this->keys; | ||||
|   } | ||||
| 
 | ||||
|   function getSchema($key): Schema { | ||||
|     if ($key === null) return $this; | ||||
|     $schema = $this->definition["schema"][$key] ?? null; | ||||
|     if ($schema === null) throw ValueException::invalid_key($key); | ||||
|     return $schema; | ||||
|   } | ||||
| 
 | ||||
|   protected function newWrapper(): AssocWrapper { | ||||
|     return new AssocWrapper($this); | ||||
|   } | ||||
| 
 | ||||
|   function getWrapper(&$array=null, $arrayKey=null, ?Wrapper &$wrapper=null): AssocWrapper { | ||||
|   function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): AssocWrapper { | ||||
|     # si pas de valeur ni de wrapper, pas d'analyse et donc pas d'exception
 | ||||
|     # cf le code similaire dans AssocWrapper::__construct()
 | ||||
|     $dontAnalyze = $value === null && $wrapper === null; | ||||
|     if (!($wrapper instanceof AssocWrapper)) $wrapper = $this->newWrapper(); | ||||
|     return $wrapper->reset($array, $arrayKey); | ||||
|     if ($params !== null) $wrapper->resetParams($params); | ||||
|     return $wrapper->reset($value, $valueKey, $dontAnalyze? ["analyze" => false]: null); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_assoc; | ||||
| 
 | ||||
| use nulib\ref\schema\ref_analyze; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarWrapper; | ||||
| @ -8,133 +9,150 @@ use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| use nur\sery\wip\schema\WrapperContext; | ||||
| 
 | ||||
| class AssocWrapper extends Wrapper { | ||||
|   function __construct(AssocSchema $schema, &$array=null, $arrayKey=null, ?array $params=null) { | ||||
|     $verifix = $params["verifix"] ?? true; | ||||
|   function __construct(AssocSchema $schema, &$value=null, $valueKey=null, ?array $params=null) { | ||||
|     $keys = $schema->getKeys(); | ||||
|     $keyWrappers = []; | ||||
|     foreach ($keys as $key) { | ||||
|       $keyWrappers[$key] = $schema->getSchema($key)->getWrapper(); | ||||
|     } | ||||
|     $this->context = $context = new AssocWrapperContext($schema, null, null, $params); | ||||
|     $context->arrayWrapper = new ScalarWrapper($schema, $dummy, null, null, $context); | ||||
|     $context->keys = $keys; | ||||
|     $context->keyWrappers = $keyWrappers; | ||||
| 
 | ||||
|     # calculer manuellemet throw ici parce que WrapperContext le met à true par
 | ||||
|     # défaut. on veut pouvoir mettre temporairement throw à false si jamais il
 | ||||
|     # n'est pas spécifié par l'utilisateur
 | ||||
|     $throw = $params["throw"] ?? null; | ||||
|     if ($array !== null && $throw === null) { | ||||
|     # Si $value est null, ne pas lancer d'exception, parce qu'on considère que
 | ||||
|     # c'est une initialisation sans conséquences
 | ||||
|       $throw = true; | ||||
|     } | ||||
|     $this->schema = $schema; | ||||
|     $this->verifix = $verifix; | ||||
|     $this->throw = $throw ?? false; | ||||
|     $this->result = new AssocResult(); | ||||
|     $this->reset($array, $arrayKey); | ||||
|     $this->throw = $throw ?? true; | ||||
|     if ($throw === null && $value !== null) $throw = true; | ||||
|     $context->throw = $throw ?? false; | ||||
|     $this->reset($value, $valueKey); | ||||
|     $context->throw = $throw ?? true; | ||||
|   } | ||||
| 
 | ||||
|   function isAssoc(?AssocWrapper &$wrapper=null): bool { $wrapper = $this; return true; } | ||||
| 
 | ||||
|   protected bool $verifix; | ||||
|   /** @var AssocWrapperContext  */ | ||||
|   protected WrapperContext $context; | ||||
| 
 | ||||
|   protected bool $throw; | ||||
| 
 | ||||
|   /** schéma de ce tableau */ | ||||
|   protected AssocSchema $schema; | ||||
| 
 | ||||
|   /** source et destination de la valeur */ | ||||
|   protected Input $input; | ||||
| 
 | ||||
|   /** @var string|int|null clé du tableau dans le tableau destination */ | ||||
|   protected $arrayKey; | ||||
| 
 | ||||
|   protected IType $arrayType; | ||||
| 
 | ||||
|   protected ScalarResult $arrayResult; | ||||
| 
 | ||||
|   /** @var IType[] */ | ||||
|   protected array $keyTypes; | ||||
| 
 | ||||
|   /** @var Result[] */ | ||||
|   protected array $keyResults; | ||||
| 
 | ||||
|   protected AssocResult $result; | ||||
| 
 | ||||
|   protected ?array $keys; | ||||
| 
 | ||||
|   protected ?array $wrappers; | ||||
| 
 | ||||
|   protected function newInput(&$value): Input { | ||||
|     return new Input($value); | ||||
|   protected function resetContext($resetSelectedKey): void { | ||||
|     $context = $this->context; | ||||
|     $context->arrayWrapper->getResult()->reset(); | ||||
|     foreach ($context->keyWrappers as $wrapper) { | ||||
|       $wrapper->getResult()->reset(); | ||||
|     } | ||||
|     $context->analyzed = false; | ||||
|     $context->normalized = false; | ||||
|     if ($resetSelectedKey) $context->selectedKey = null; | ||||
|   } | ||||
| 
 | ||||
|   function reset(&$array, $arrayKey=null, ?bool $verifix=null): Wrapper { | ||||
|     if ($array instanceof Input) $input = $array; | ||||
|     else $input = $this->newInput($array); | ||||
|     $this->input = $input; | ||||
|     $this->arrayKey = $arrayKey; | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|   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; | ||||
|     foreach ($context->keyWrappers as $key => $keyWrapper) { | ||||
|       $keyInput = $input->addKey($valueKey); | ||||
|       $keyWrapper->reset($keyInput, $key, ["analyze" => false]); | ||||
|     } | ||||
|     $this->afterModify($params, true); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return $this->keys; | ||||
|     return $this->context->keys; | ||||
|   } | ||||
| 
 | ||||
|   function select($key=null): ScalarWrapper { | ||||
|     $wrapper = $this->wrappers[$key] ?? null; | ||||
|     if ($key !== null) return $wrapper; | ||||
|     throw ValueException::invalid_key($key); | ||||
|   protected function _getWrapper($key): Wrapper { | ||||
|     if ($key === null) return $this->context->arrayWrapper; | ||||
|     $wrapper = $context->keyWrappers[$key] ?? null; | ||||
|     if ($wrapper === null) throw ValueException::invalid_key($key); | ||||
|     return $wrapper; | ||||
|   } | ||||
| 
 | ||||
|   /** @param Result[] $results */ | ||||
|   function verifix(?bool $throw=null, ?array &$results=null): bool { | ||||
|   /** @param string|int|null $key */ | ||||
|   function select($key=null): Wrapper { | ||||
|     $wrapper = $this->_getWrapper($key); | ||||
|     $this->context->selectedKey = $key; | ||||
|     return $wrapper; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   function getResult(): AssocResult { | ||||
|     return $this->result; | ||||
|   /** | ||||
|    * @param AssocWrapperContext $context | ||||
|    * @param AssocWrapper $wrapper | ||||
|    */ | ||||
|   static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int { | ||||
|     if ($context->ensureArray) { | ||||
|       $valueKey = $context->valueKey; | ||||
|       $array = $context->input->get($valueKey); | ||||
|       if ($array === null) $context->input->set([], $valueKey); | ||||
|     } | ||||
|     $what = ScalarWrapper::_analyze($context, $wrapper, $params); | ||||
|     /** @var ScalarResult $result */ | ||||
|     $result = $context->result; | ||||
|     if (!$result->valid) return $what; | ||||
|     foreach ($context->keyWrappers as $keyWrapper) { | ||||
|       $keyWrapper->analyze($params); | ||||
|       if (!$keyWrapper->isValid()) { | ||||
|         $what = ref_analyze::INVALID; | ||||
|         $result->addInvalidMessage($keyWrapper); | ||||
|       } | ||||
|     } | ||||
|     return $what; | ||||
|   } | ||||
| 
 | ||||
|   function isPresent(): bool { | ||||
|     return $this->result->present; | ||||
|   /** | ||||
|    * @param AssocWrapperContext $context | ||||
|    * @param AssocWrapper $wrapper | ||||
|    */ | ||||
|   static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool { | ||||
|     $modified = ScalarWrapper::_normalize($context, $wrapper, $params); | ||||
|     foreach ($context->keyWrappers as $keyWrapper) { | ||||
|       if ($keyWrapper->normalize($params)) $modified = true; | ||||
|     } | ||||
|     return $modified; | ||||
|   } | ||||
| 
 | ||||
|   function getType(): IType { | ||||
|     return $this->arrayType; | ||||
|   function getResult($key=false): Result { | ||||
|     if ($key === false) $key = $this->context->selectedKey; | ||||
|     return $this->_getWrapper($key)->getResult(); | ||||
|   } | ||||
| 
 | ||||
|   function isAvailable(): bool { | ||||
|     return $this->result->available; | ||||
|   function getType($key=false): IType { | ||||
|     if ($key === false) $key = $this->context->selectedKey; | ||||
|     return $this->_getWrapper($key)->getType(); | ||||
|   } | ||||
| 
 | ||||
|   function isValid(): bool { | ||||
|     return $this->result->valid; | ||||
|   function get($default=null, $key=false) { | ||||
|     $context = $this->context; | ||||
|     if (!$context->arrayWrapper->isAvailable()) return $default; | ||||
|     if ($key === false) $key = $context->selectedKey; | ||||
|     return $this->_getWrapper($key)->get($default); | ||||
|   } | ||||
| 
 | ||||
|   function isNormalized(): bool { | ||||
|     return $this->result->normalized; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     if ($this->result->available) return $this->input->get($this->arrayKey); | ||||
|     else return $default; | ||||
|   } | ||||
| 
 | ||||
|   function set($value, ?bool $verifix=null): AssocWrapper { | ||||
|     $this->input->set($value, $this->arrayKey); | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|   function set($value, ?array $params=null, $key=false): Wrapper { | ||||
|     $context = $this->context; | ||||
|     if ($key === false) $key = $context->selectedKey; | ||||
|     $this->_getWrapper($key)->set($value); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function unset(?bool $verifix=null): AssocWrapper { | ||||
|     $this->input->unset($this->arrayKey); | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|   function unset(?array $params=null, $key=false): Wrapper { | ||||
|     $context = $this->context; | ||||
|     if ($key === false) $key = $context->selectedKey; | ||||
|     $this->_getWrapper($key)->unset(); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function format($format = null): string { | ||||
|     // TODO: Implement format() method.
 | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(): bool { | ||||
|   } | ||||
|   function orderKeys(): bool { | ||||
|   function format($format=null, $key=false): string { | ||||
|     $context = $this->context; | ||||
|     if ($key === false) $key = $context->selectedKey; | ||||
|     return $this->_getWrapper($key)->format($format); | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										33
									
								
								src/schema/_assoc/AssocWrapperContext.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/schema/_assoc/AssocWrapperContext.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_assoc; | ||||
| 
 | ||||
| use nur\sery\wip\schema\_scalar\ScalarWrapper; | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| use nur\sery\wip\schema\WrapperContext; | ||||
| 
 | ||||
| class AssocWrapperContext extends WrapperContext { | ||||
|   const DEFAULT_ENSURE_ARRAY = false; | ||||
|   const DEFAULT_ENSURE_KEYS = true; | ||||
|   const DEFAULT_ENSURE_ORDER = true; | ||||
| 
 | ||||
|   function __construct(Schema $schema, ?Input $input, $valueKey, ?array $params) { | ||||
|     parent::__construct($schema, $input, $valueKey, $params); | ||||
|     $this->ensureArray = $params["ensure_array"] ?? self::DEFAULT_ENSURE_ARRAY; | ||||
|     $this->ensureKeys = $params["ensure_keys"] ?? self::DEFAULT_ENSURE_KEYS; | ||||
|     $this->ensureOrder = $params["ensure_order"] ?? self::DEFAULT_ENSURE_ORDER; | ||||
|   } | ||||
| 
 | ||||
|   public bool $ensureArray; | ||||
|   public bool $ensureKeys; | ||||
|   public bool $ensureOrder; | ||||
| 
 | ||||
|   public ?ScalarWrapper $arrayWrapper = null; | ||||
| 
 | ||||
|   /** liste des clés valides */ | ||||
|   public array $keys; | ||||
| 
 | ||||
|   /** @var Wrapper[] */ | ||||
|   public array $keyWrappers; | ||||
| } | ||||
| @ -2,6 +2,7 @@ | ||||
| namespace nur\sery\wip\schema\_list; | ||||
| 
 | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| 
 | ||||
| @ -34,7 +35,7 @@ class ListSchema extends Schema { | ||||
|         "schema" => $definition[0], | ||||
|       ]; | ||||
|     } | ||||
|     self::_normalize($definition, $definitionKey); | ||||
|     self::_normalize_definition($definition, $definitionKey); | ||||
|     self::_ensure_nature($definition, "list", "array"); | ||||
|     return $definition; | ||||
|   } | ||||
| @ -55,12 +56,26 @@ class ListSchema extends Schema { | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   const KEYS = [null]; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return self::KEYS; | ||||
|   } | ||||
| 
 | ||||
|   public function getSchema($key): Schema { | ||||
|     if ($key !== null) throw ValueException::invalid_key($key); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   protected function newWrapper(): ListWrapper { | ||||
|     return new ListWrapper($this); | ||||
|   } | ||||
| 
 | ||||
|   function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ListWrapper { | ||||
|   function getWrapper(&$value=null, $valueKey=null, ?array $params = null, ?Wrapper &$wrapper=null): ListWrapper { | ||||
|     # si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception
 | ||||
|     # cf le code similaire dans ScalarWrapper::__construct()
 | ||||
|     $verifix = $value !== null || $wrapper !== null; | ||||
|     if (!($wrapper instanceof ListWrapper)) $wrapper = $this->newWrapper(); | ||||
|     return $wrapper->reset($value, $valueKey); | ||||
|     return $wrapper->reset($value, $valueKey, $verifix); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -6,6 +6,8 @@ use nulib\ref\schema\ref_analyze; | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| use Throwable; | ||||
| 
 | ||||
| /** | ||||
| @ -15,7 +17,7 @@ class ScalarResult extends Result { | ||||
|   function isScalar(?ScalarResult &$result=null): bool { $result = $this; return true; } | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return [null]; | ||||
|     return ScalarSchema::KEYS; | ||||
|   } | ||||
| 
 | ||||
|   function select($key): Result { | ||||
| @ -23,8 +25,7 @@ class ScalarResult extends Result { | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** @var array  */ | ||||
|   protected $result; | ||||
|   protected array $result; | ||||
|    | ||||
|   function reset(): void { | ||||
|     $this->result = array_merge( | ||||
| @ -46,26 +47,13 @@ class ScalarResult extends Result { | ||||
|     $this->result[$name] = $value; | ||||
|   } | ||||
| 
 | ||||
|   protected static function replace_key(string &$message, ?string $key): void { | ||||
|     if ($key) { | ||||
|       $message = str_replace("{key}", $key, $message); | ||||
|     } else { | ||||
|       $message = str_replace("{key}: ", "", $message); | ||||
|       $message = str_replace("cette valeur", "la valeur", $message); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected static function replace_orig(string &$message, $origValue): void { | ||||
|     $message = str_replace("{orig}", strval($origValue), $message); | ||||
|   } | ||||
| 
 | ||||
|   protected function getMessage(string $key, ScalarSchema $schema): string { | ||||
|   protected function getMessage(string $key,  Schema $schema): string { | ||||
|     $message = cl::get($schema->messages, $key); | ||||
|     if ($message !== null) return $message; | ||||
|     return cl::get(ref_schema::MESSAGES, $key); | ||||
|   } | ||||
| 
 | ||||
|   function setMissing(ScalarSchema $schema): int { | ||||
|   function setMissing( Schema $schema): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = false; | ||||
|     $this->available = false; | ||||
| @ -75,15 +63,13 @@ class ScalarResult extends Result { | ||||
|       $this->normalized = true; | ||||
|       return ref_analyze::NORMALIZED; | ||||
|     } else { | ||||
|       $messageKey = $this->messageKey = "missing"; | ||||
|       $message = $this->getMessage($messageKey, $schema); | ||||
|       self::replace_key($message, $schema->name); | ||||
|       $this->message = $message; | ||||
|       $this->messageKey = $messageKey = "missing"; | ||||
|       $this->message = $this->getMessage($messageKey, $schema); | ||||
|       return ref_analyze::MISSING; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function setUnavailable(ScalarSchema $schema): int { | ||||
|   function setUnavailable( Schema $schema): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = false; | ||||
| @ -93,15 +79,13 @@ class ScalarResult extends Result { | ||||
|       $this->normalized = true; | ||||
|       return ref_analyze::NORMALIZED; | ||||
|     } else { | ||||
|       $messageKey = $this->messageKey = "unavailable"; | ||||
|       $message = $this->getMessage($messageKey, $schema); | ||||
|       self::replace_key($message, $schema->name); | ||||
|       $this->message = $message; | ||||
|       $this->messageKey = $messageKey = "unavailable"; | ||||
|       $this->message = $this->getMessage($messageKey, $schema); | ||||
|       return ref_analyze::UNAVAILABLE; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function setNull(ScalarSchema $schema): int { | ||||
|   function setNull( Schema $schema): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
| @ -111,33 +95,53 @@ class ScalarResult extends Result { | ||||
|       $this->normalized = true; | ||||
|       return ref_analyze::NORMALIZED; | ||||
|     } else { | ||||
|       $messageKey = $this->messageKey = "null"; | ||||
|       $message = $this->getMessage($messageKey, $schema); | ||||
|       self::replace_key($message, $schema->name); | ||||
|       $this->message = $message; | ||||
|       $this->messageKey = $messageKey = "null"; | ||||
|       $this->message = $this->getMessage($messageKey, $schema); | ||||
|       return ref_analyze::NULL; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function setInvalid($value, ScalarSchema $schema, ?Throwable $t=null): int { | ||||
|   function setInvalid($value,  Schema $schema, ?Throwable $exception=null): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
|     $this->null = false; | ||||
|     $this->valid = false; | ||||
|     $this->origValue = $value; | ||||
|     $messageKey = $this->messageKey = "invalid"; | ||||
|     $this->messageKey = $messageKey = "invalid"; | ||||
|     $message = $this->getMessage($messageKey, $schema); | ||||
|     self::replace_key($message, $schema->name); | ||||
|     self::replace_orig($message, $schema->orig); | ||||
|     if ($t !== null) { | ||||
|       $tmessage = ValueException::get_message($t); | ||||
|       if ($tmessage) $message .= ": $tmessage"; | ||||
|     if ($exception !== null) { | ||||
|       $tmessage = ValueException::get_message($exception); | ||||
|       if ($tmessage) $message = $tmessage; | ||||
|     } | ||||
|     $this->message = $message; | ||||
|     $this->exception = $exception; | ||||
|     return ref_analyze::INVALID; | ||||
|   } | ||||
| 
 | ||||
|   function addInvalidMessage(Wrapper $wrapper): void { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
|     $this->null = false; | ||||
|     $this->valid = false; | ||||
|     $this->messageKey = "invalid"; | ||||
|     $result = $wrapper->getResult(); | ||||
|     $resultException = $result->exception; | ||||
|     $resultMessage = $result->message; | ||||
|     if ($resultException !== null) { | ||||
|       $tmessage = ValueException::get_message($resultException); | ||||
|       if ($tmessage) { | ||||
|         if ($resultMessage !== null) $resultMessage .= ": "; | ||||
|         $resultMessage .= $tmessage; | ||||
|       } | ||||
|     } | ||||
|     $message = $this->message; | ||||
|     if ($message) $message .= "\n"; | ||||
|     $message .= $resultMessage; | ||||
|     $this->message = $message; | ||||
|   } | ||||
| 
 | ||||
|   function setValid($normalizedValue=null): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
| @ -159,6 +163,10 @@ class ScalarResult extends Result { | ||||
|   } | ||||
| 
 | ||||
|   function throw(bool $throw): void { | ||||
|     if ($throw) throw new ValueException($this->message); | ||||
|     if ($throw) { | ||||
|       $exception = $this->exception; | ||||
|       if ($exception !== null) throw $exception; | ||||
|       else throw new ValueException($this->message); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,41 +1,19 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_scalar; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| 
 | ||||
| /** | ||||
|  * Class ScalarSchema | ||||
|  *  | ||||
|  * @property-read array|IType $type | ||||
|  * @property-read mixed $default | ||||
|  * @property-read string|null $title | ||||
|  * @property-read bool $required | ||||
|  * @property-read bool $nullable | ||||
|  * @property-read string|array|null $desc | ||||
|  * @property-read callable|null $analyzerFunc | ||||
|  * @property-read callable|null $extractorFunc | ||||
|  * @property-read callable|null $parserFunc | ||||
|  * @property-read callable|null $normalizerFunc | ||||
|  * @property-read array|null $messages | ||||
|  * @property-read callable|null $formatterFunc | ||||
|  * @property-read mixed $format | ||||
|  * @property-read array $nature | ||||
|  * @property-read array|null $schema | ||||
|  * @property-read string|int|null $name | ||||
|  * @property-read string|array|null $pkey | ||||
|  * @property-read string|null $header | ||||
|  * @property-read bool|null $composite | ||||
|  */ | ||||
| class ScalarSchema extends Schema { | ||||
|   /** @var array meta-schema d'un schéma de nature scalaire */ | ||||
|   const METASCHEMA = ref_schema::SCALAR_METASCHEMA; | ||||
| 
 | ||||
|   /** | ||||
|    * indiquer si $definition est une définition de schéma scalaire que | ||||
|    * {@link normalize()} pourrait normaliser | ||||
|    * {@link normalize_definition()} pourrait normaliser | ||||
|    */ | ||||
|   static function isa_definition($definition): bool { | ||||
|     # chaine ou null
 | ||||
| @ -64,8 +42,9 @@ class ScalarSchema extends Schema { | ||||
|     return $haveIndex0 && $count > 1; | ||||
|   } | ||||
| 
 | ||||
|   static function normalize($definition, $definitionKey=null): array { | ||||
|     self::_normalize($definition, $definitionKey); | ||||
|   static function normalize_definition($definition, $definitionKey=null): array { | ||||
|     $natureMetaschema = array_merge(ref_schema::NATURE_METASCHEMA, ref_schema::SCALAR_NATURE_METASCHEMA); | ||||
|     self::_normalize_definition($definition, $definitionKey, $natureMetaschema); | ||||
|     self::_ensure_nature($definition, "scalar"); | ||||
|     return $definition; | ||||
|   } | ||||
| @ -73,7 +52,7 @@ class ScalarSchema extends Schema { | ||||
|   function __construct($definition=null, $definitionKey=null, bool $normalize=true) { | ||||
|     if ($definition === null) $definition = static::SCHEMA; | ||||
|     if ($normalize) { | ||||
|       $definition = self::normalize($definition, $definitionKey); | ||||
|       $definition = self::normalize_definition($definition, $definitionKey); | ||||
|       $this->_definition = $definition; | ||||
|       self::_ensure_type($definition); | ||||
|       self::_ensure_schema_instances($definition); | ||||
| @ -86,27 +65,27 @@ class ScalarSchema extends Schema { | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   const KEYS = [null]; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return self::KEYS; | ||||
|   } | ||||
| 
 | ||||
|   function getSchema($key): Schema { | ||||
|     if ($key !== null) throw ValueException::invalid_key($key); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   protected function newWrapper(): ScalarWrapper { | ||||
|     return new ScalarWrapper($this); | ||||
|   } | ||||
| 
 | ||||
|   function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ScalarWrapper { | ||||
|   function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): ScalarWrapper { | ||||
|     # si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception
 | ||||
|     # cf le code similaire dans ScalarWrapper::__construct()
 | ||||
|     $verifix = $value !== null || $wrapper !== null; | ||||
|     $dontAnalyze = $value === null && $wrapper === null; | ||||
|     if (!($wrapper instanceof ScalarWrapper)) $wrapper = $this->newWrapper(); | ||||
|     return $wrapper->reset($value, $valueKey, $verifix); | ||||
|     if ($params !== null) $wrapper->resetParams($params); | ||||
|     return $wrapper->reset($value, $valueKey, $dontAnalyze? ["analyze" => false]: null); | ||||
|   } | ||||
| 
 | ||||
|   #############################################################################
 | ||||
|   # key & properties
 | ||||
| 
 | ||||
|   const _PROPERTY_PKEYS = [ | ||||
|     "analyzerFunc" => "analyzer_func", | ||||
|     "extractorFunc" => "extractor_func", | ||||
|     "parserFunc" => "parser_func", | ||||
|     "normalizerFunc" => "normalizer_func", | ||||
|     "formatterFunc" => "formatter_func", | ||||
|     "nature" => ["", 0], | ||||
|   ]; | ||||
| } | ||||
|  | ||||
| @ -4,67 +4,46 @@ namespace nur\sery\wip\schema\_scalar; | ||||
| use nulib\php\func; | ||||
| use nulib\ref\schema\ref_analyze; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\WrapperContext; | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\_assoc\AssocWrapper; | ||||
| use nur\sery\wip\schema\_assoc\AssocWrapperContext; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nur\sery\wip\schema\types; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| use nur\sery\wip\schema\WrapperContext; | ||||
| 
 | ||||
| /** | ||||
|  * Class ScalarWrapper | ||||
|  * | ||||
|  * @method ScalarWrapper reset(&$value, $valueKey=null, ?array $params=null) | ||||
|  * @method ScalarResult getResult($key=false) | ||||
|  * @method self set($value, ?array $params=null, $key=false) | ||||
|  * @method self unset(?array $params=null, $key=false) | ||||
|  */ | ||||
| class ScalarWrapper extends Wrapper { | ||||
|   function __construct(ScalarSchema $schema, &$value=null, $valueKey=null, ?array $params=null) { | ||||
|     $verifix = $params["verifix"] ?? true; | ||||
|   function __construct(Schema $schema, &$value=null, $valueKey=null, ?array $params=null, ?WrapperContext $context=null) { | ||||
|     if ($context === null) $context = new WrapperContext($schema, null, null, $params); | ||||
|     $context->result = new ScalarResult(); | ||||
|     $this->context = $context; | ||||
| 
 | ||||
|     # calculer manuellemet throw ici parce que WrapperContext le met à true par
 | ||||
|     # défaut. on veut pouvoir mettre temporairement throw à false si jamais il
 | ||||
|     # n'est pas spécifié par l'utilisateur
 | ||||
|     $throw = $params["throw"] ?? null; | ||||
|     if ($value !== null && $throw === null) { | ||||
|     # Si $value est null, ne pas lancer d'exception, parce qu'on considère que
 | ||||
|     # c'est une initialisation sans conséquences
 | ||||
|       $throw = true; | ||||
|     } | ||||
|     $this->verifix = $verifix; | ||||
|     $this->throw = $throw ?? false; | ||||
|     $this->schema = $schema; | ||||
|     $this->result = new ScalarResult(); | ||||
|     if ($throw === null && $value !== null) $throw = true; | ||||
|     $context->throw = $throw ?? false; | ||||
|     $this->reset($value, $valueKey); | ||||
|     $this->throw = $throw ?? true; | ||||
|     $context->throw = $throw ?? true; | ||||
|   } | ||||
| 
 | ||||
|   function isScalar(?ScalarWrapper &$wrapper=null): bool { $wrapper = $this; return true; } | ||||
| 
 | ||||
|   protected bool $verifix; | ||||
| 
 | ||||
|   protected bool $throw; | ||||
| 
 | ||||
|   /** schéma de cette valeur */ | ||||
|   protected ScalarSchema $schema; | ||||
| 
 | ||||
|   /** source et destination de la valeur */ | ||||
|   protected Input $input; | ||||
| 
 | ||||
|   /** @var string|int|null clé de la valeur dans le tableau destination */ | ||||
|   protected $valueKey; | ||||
| 
 | ||||
|   /** type de la valeur après analyse */ | ||||
|   protected ?IType $type; | ||||
| 
 | ||||
|   /** résultat de l'analyse de la valeur */ | ||||
|   protected ScalarResult $result; | ||||
| 
 | ||||
|   protected function newInput(&$value): Input { | ||||
|     return new Input($value); | ||||
|   } | ||||
| 
 | ||||
|   function reset(&$value, $valueKey=null, ?bool $verifix=null): Wrapper { | ||||
|     if ($value instanceof Input) $input = $value; | ||||
|     else $input = $this->newInput($value); | ||||
|     $this->input = $input; | ||||
|     $this->valueKey = $valueKey; | ||||
|     $this->type = null; | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|     return $this; | ||||
|   } | ||||
|   protected WrapperContext $context; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return [null]; | ||||
|     return ScalarSchema::KEYS; | ||||
|   } | ||||
| 
 | ||||
|   /** @param string|int|null $key */ | ||||
| @ -74,7 +53,7 @@ class ScalarWrapper extends Wrapper { | ||||
|   } | ||||
| 
 | ||||
|   /** analyser la valeur et résoudre son type */ | ||||
|   protected function analyze0(WrapperContext $context): int { | ||||
|   protected static function _analyze0(WrapperContext $context): int { | ||||
|     /** @var ScalarSchema $schema */ | ||||
|     $schema = $context->schema; | ||||
|     $input = $context->input; | ||||
| @ -112,7 +91,7 @@ class ScalarWrapper extends Wrapper { | ||||
|           $args = $name; | ||||
|           $name = $key; | ||||
|         } | ||||
|         $type = types::get($schema->nullable, $name, $args, $this->schema->getDefinition()); | ||||
|         $type = types::get($schema->nullable, $name, $args, $schema->getDefinition()); | ||||
|         if ($firstType === null) $firstType = $type; | ||||
|         $types[] = $type; | ||||
|         if ($type->isAvailable($input, $valueKey)) { | ||||
| @ -140,7 +119,7 @@ class ScalarWrapper extends Wrapper { | ||||
|         $type = $firstType; | ||||
|       } | ||||
|     } | ||||
|     $context->type = $this->type = $type; | ||||
|     $context->type = $type; | ||||
| 
 | ||||
|     if (!$type->isAvailable($input, $valueKey)) { | ||||
|       if ($default !== null) { | ||||
| @ -165,25 +144,28 @@ class ScalarWrapper extends Wrapper { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected function analyze(): int { | ||||
|     $schema = $this->schema; | ||||
|     $input = $this->input; | ||||
|     $valueKey = $this->valueKey; | ||||
|     $result = $this->result; | ||||
|     $result->reset(); | ||||
|     $context = new WrapperContext($schema, $this, $input, $valueKey, $result); | ||||
|   /** | ||||
|    * @param ScalarWrapper $wrapper | ||||
|    */ | ||||
|   static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int { | ||||
|     /** @var ScalarSchema $schema */ | ||||
|     $schema = $context->schema; | ||||
|     $input = $context->input; | ||||
|     $valueKey = $context->valueKey; | ||||
|     /** @var ScalarResult $result */ | ||||
|     $result = $context->result; | ||||
| 
 | ||||
|     /** @var func $analyzerFunc */ | ||||
|     $analyzerFunc = $schema->analyzerFunc; | ||||
|     if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context]); | ||||
|     else $what = $this->analyze0($context); | ||||
|     if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context, $wrapper]); | ||||
|     else $what = self::_analyze0($context); | ||||
|     if ($what !== ref_analyze::STRING) return $what; | ||||
| 
 | ||||
|     $value = $context->value; | ||||
|     try { | ||||
|       /** @var func $extractorFunc */ | ||||
|       $extractorFunc = $schema->extractorFunc; | ||||
|       if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context]); | ||||
|       if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context, $wrapper]); | ||||
|       else $extracted = $context->type->extract($value); | ||||
|       $context->value = $extracted; | ||||
|     } catch (ValueException $e) { | ||||
| @ -194,7 +176,7 @@ class ScalarWrapper extends Wrapper { | ||||
|     try { | ||||
|       /** @var func $parserFunc */ | ||||
|       $parserFunc = $schema->parserFunc; | ||||
|       if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context]); | ||||
|       if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context, $wrapper]); | ||||
|       else $parsed = $context->type->parse($extracted); | ||||
|       $context->value = $parsed; | ||||
|     } catch (ValueException $e) { | ||||
| @ -211,107 +193,53 @@ class ScalarWrapper extends Wrapper { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function verifix(?bool $throw=null): bool { | ||||
|     $result = $this->result; | ||||
|     $valueKey = $this->valueKey; | ||||
|     $verifix = false; | ||||
|   /** | ||||
|    * @param ScalarWrapper $wrapper | ||||
|    */ | ||||
|   static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool { | ||||
|     /** @var ScalarSchema $schema */ | ||||
|     $schema = $context->schema; | ||||
|     $input = $context->input; | ||||
|     $valueKey = $context->valueKey; | ||||
|     /** @var ScalarResult $result */ | ||||
|     $result = $context->result; | ||||
| 
 | ||||
|     $normalize = false; | ||||
|     $modified = false; | ||||
|     if ($result->resultAvailable) { | ||||
|       if ($result->null) { | ||||
|         # forcer la valeur null, parce que la valeur actuelle est peut-être une
 | ||||
|         # valeur assimilée à null
 | ||||
|         $this->input->set(null, $valueKey); | ||||
|         $input->set(null, $valueKey); | ||||
|       } elseif ($result->valid && !$result->normalized) { | ||||
|         $normalizedValue = $result->normalizedValue; | ||||
|         if ($normalizedValue !== null) { | ||||
|           # la valeur normalisée est disponible
 | ||||
|           $this->input->set($normalizedValue); | ||||
|           $input->set($normalizedValue, $valueKey); | ||||
|           $result->normalizedValue = null; | ||||
|           $modified = true; | ||||
|         } else { | ||||
|           # normaliser la valeur
 | ||||
|           $verifix = true; | ||||
|           $normalize = true; | ||||
|         } | ||||
|       } | ||||
|     } else { | ||||
|       $verifix = true; | ||||
|       $normalize = true; | ||||
|     } | ||||
| 
 | ||||
|     if ($verifix) { | ||||
|       $value = $this->input->get($valueKey); | ||||
|       $schema = $this->schema; | ||||
|     if ($normalize) { | ||||
|       $value = $input->get($valueKey); | ||||
|       /** @var func $normalizerFunc */ | ||||
|       $normalizerFunc = $schema->normalizerFunc; | ||||
|       if ($normalizerFunc !== null) { | ||||
|         $context = new WrapperContext($schema, $this, $this->input, $valueKey, $result); | ||||
|         $orig = $value; | ||||
|         $value = $normalizerFunc->invoke([$orig, $context]); | ||||
|         $value = $normalizerFunc->invoke([$orig, $context, $wrapper]); | ||||
|         $modified = $value !== $orig; | ||||
|       } else { | ||||
|         $modified = $this->type->verifix($value, $result, $this->schema); | ||||
|         $modified = $context->type->normalize($value, $result, $schema); | ||||
|       } | ||||
|       if ($result->valid) $this->input->set($value, $valueKey); | ||||
|       if ($result->valid) $input->set($value, $valueKey); | ||||
|     } | ||||
|     if (!$result->valid) $result->throw($throw ?? $this->throw); | ||||
|     return $modified; | ||||
|   } | ||||
| 
 | ||||
|   function getResult(): ScalarResult { | ||||
|     return $this->result; | ||||
|   } | ||||
| 
 | ||||
|   function isPresent(): bool { | ||||
|     return $this->result->present; | ||||
|   } | ||||
| 
 | ||||
|   function getType(): IType { | ||||
|     return $this->type; | ||||
|   } | ||||
| 
 | ||||
|   function isAvailable(): bool { | ||||
|     return $this->result->available; | ||||
|   } | ||||
| 
 | ||||
|   function isValid(): bool { | ||||
|     return $this->result->valid; | ||||
|   } | ||||
| 
 | ||||
|   function isNormalized(): bool { | ||||
|     return $this->result->normalized; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     if ($this->result->available) return $this->input->get($this->valueKey); | ||||
|     else return $default; | ||||
|   } | ||||
| 
 | ||||
|   function set($value, ?bool $verifix=null): ScalarWrapper { | ||||
|     $this->input->set($value, $this->valueKey); | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function unset(?bool $verifix=null): ScalarWrapper { | ||||
|     $this->input->unset($this->valueKey); | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function format($format=null): string { | ||||
|     $value = $this->input->get($this->valueKey); | ||||
|     /** @var func $formatterFunc */ | ||||
|     $formatterFunc = $this->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 ??= $this->schema->format; | ||||
|       return $formatterFunc->invoke([$value, $format]); | ||||
|     } else { | ||||
|       # on assume que le type a été initialisé avec le format de la définition
 | ||||
|       # le cas échéant
 | ||||
|       return $this->type->format($value, $format); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -5,7 +5,6 @@ namespace nur\sery\wip\schema\input; | ||||
| # construire des querystring et paramètres de formulaires
 | ||||
| use nur\sery\wip\php\access\FormAccess; | ||||
| use nur\sery\wip\php\access\IAccess; | ||||
| use nur\sery\wip\php\access\KeyAccess; | ||||
| use nur\sery\wip\php\access\ShadowAccess; | ||||
| 
 | ||||
| /** | ||||
| @ -18,15 +17,14 @@ use nur\sery\wip\php\access\ShadowAccess; | ||||
| class FormInput extends Input { | ||||
|   const ALLOW_EMPTY = false; | ||||
| 
 | ||||
|   protected function formAccess($key): IAccess { | ||||
|     return new FormAccess($key, [ | ||||
|       "allow_empty" => $this->allowEmpty, | ||||
|     ]); | ||||
|   function __construct(&$dest=null, ?array $params=null) { | ||||
|     parent::__construct($dest, $params); | ||||
|     $this->access = new ShadowAccess($this->formAccess($this->access), $this->access); | ||||
|   } | ||||
| 
 | ||||
|   protected function access($key): IAccess { | ||||
|     return $this->keyAccess[$key] ??= new ShadowAccess($this->formAccess($key), new KeyAccess($this->value, $key, [ | ||||
|       "allow_empty" => $this->allowEmpty, | ||||
|     ])); | ||||
|   protected function formAccess(IAccess $access): IAccess { | ||||
|     return new FormAccess(null, [ | ||||
|       "allow_empty" => $access->isAllowEmpty(), | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -11,9 +11,9 @@ use nur\sery\wip\php\access\IAccess; | ||||
|  * une référence | ||||
|  */ | ||||
| class GetInput extends FormInput { | ||||
|   protected function formAccess($key): IAccess { | ||||
|     return new GetAccess($key, [ | ||||
|       "allow_empty" => $this->allowEmpty, | ||||
|   protected function formAccess(IAccess $access): IAccess { | ||||
|     return new GetAccess(null, [ | ||||
|       "allow_empty" => $access->isAllowEmpty(), | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,9 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\input; | ||||
| 
 | ||||
| use nulib\StateException; | ||||
| use nur\sery\wip\php\access\IAccess; | ||||
| use nur\sery\wip\php\access\KeyAccess; | ||||
| use nur\sery\wip\php\access\ValueAccess; | ||||
| use nur\sery\wip\php\access\PropertyAccess; | ||||
| 
 | ||||
| /** | ||||
|  * Class Input: accès à une valeur | ||||
| @ -13,54 +14,62 @@ use nur\sery\wip\php\access\ValueAccess; | ||||
| class Input { | ||||
|   const ALLOW_EMPTY = true; | ||||
| 
 | ||||
|   function __construct(&$value=null, ?array $params=null) { | ||||
|     $this->value =& $value; | ||||
|     $this->allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY; | ||||
|   const ACCESS_AUTO = 0, ACCESS_KEY = 1, ACCESS_PROPERTY = 2; | ||||
| 
 | ||||
|   private static function unexpected_access_type(): StateException { | ||||
|     return StateException::unexpected_state("access_type"); | ||||
|   } | ||||
| 
 | ||||
|   /** @var mixed */ | ||||
|   protected $value; | ||||
|   function __construct(&$dest=null, ?array $params=null) { | ||||
|     $accessType = $params["access_type"] ?? self::ACCESS_AUTO; | ||||
|     if ($accessType === self::ACCESS_AUTO) { | ||||
|       $accessType = is_object($dest)? self::ACCESS_PROPERTY: self::ACCESS_KEY; | ||||
|     } | ||||
| 
 | ||||
|   /** | ||||
|    * @var bool comment considérer une chaine vide: "" si allowEmpty, null sinon | ||||
|    */ | ||||
|   protected $allowEmpty; | ||||
| 
 | ||||
|   protected ?ValueAccess $valueAccess = null; | ||||
|   protected ?array $keyAccess = null; | ||||
| 
 | ||||
|   protected function access($key): IAccess { | ||||
|     if ($key === null) { | ||||
|       return $this->valueAccess ??= new ValueAccess($this->value, [ | ||||
|     $allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY; | ||||
|     if ($accessType == self::ACCESS_PROPERTY) { | ||||
|       $this->access = new PropertyAccess($dest, null, [ | ||||
|         "allow_empty" => $allowEmpty, | ||||
|         "allow_null" => true, | ||||
|       ]); | ||||
|     } elseif ($accessType == self::ACCESS_KEY) { | ||||
|       $this->access = new KeyAccess($dest, null, [ | ||||
|         "allow_empty" => $allowEmpty, | ||||
|         "allow_null" => true, | ||||
|         "allow_empty" => $this->allowEmpty, | ||||
|       ]); | ||||
|     } else { | ||||
|       return $this->keyAccess[$key] ??= new KeyAccess($this->value, $key, [ | ||||
|         "allow_empty" => $this->allowEmpty, | ||||
|       ]); | ||||
|       throw self::unexpected_access_type(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected IAccess $access; | ||||
| 
 | ||||
|   /** tester si la valeur existe sans tenir compte de $allowEmpty */ | ||||
|   function isPresent($key=null): bool { | ||||
|     return $this->access($key)->exists(); | ||||
|     return $this->access->resetKey($key)->exists(); | ||||
|   } | ||||
| 
 | ||||
|   /** tester si la valeur est disponible en tenant compte de $allowEmpty */ | ||||
|   function isAvailable($key=null): bool { | ||||
|     return $this->access($key)->available(); | ||||
|     return $this->access->resetKey($key)->available(); | ||||
|   } | ||||
| 
 | ||||
|   function get($key=null) { | ||||
|     return $this->access($key)->get(); | ||||
|     return $this->access->resetKey($key)->get(); | ||||
|   } | ||||
| 
 | ||||
|   function set($value, $key=null): void { | ||||
|     $this->access($key)->set($value); | ||||
|     $this->access->resetKey($key)->set($value); | ||||
|   } | ||||
| 
 | ||||
|   function unset($key=null): void { | ||||
|     $this->access($key)->del(); | ||||
|     $this->access->resetKey($key)->del(); | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key): Input { | ||||
|     if ($key === null) return $this; | ||||
|     $input = clone $this; | ||||
|     $input->access = $this->access->addKey($key); | ||||
|     return $input; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -11,9 +11,9 @@ use nur\sery\wip\php\access\PostAccess; | ||||
|  * une référence | ||||
|  */ | ||||
| class PostInput extends FormInput { | ||||
|   protected function formAccess($key): IAccess { | ||||
|     return new PostAccess($key, [ | ||||
|       "allow_empty" => $this->allowEmpty, | ||||
|   protected function formAccess(IAccess $access): IAccess { | ||||
|     return new PostAccess(null, [ | ||||
|       "allow_empty" => $access->isAllowEmpty(), | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -23,7 +23,7 @@ interface IType { | ||||
| 
 | ||||
|   /** | ||||
|    * @return string la classe des objets gérés par ce format: le type attendu | ||||
|    * par {@link format()} et le type retourné par {@link verifix()} | ||||
|    * par {@link format()} et le type retourné par {@link normalize()} | ||||
|    * | ||||
|    * Les valeurs "mixed", "bool", "float", "int", "string" et "array" peuvent | ||||
|    * aussi être retournées, bien qu'elles ne soient pas à proprement parler des | ||||
| @ -98,15 +98,18 @@ interface IType { | ||||
|   function parse(string $value); | ||||
| 
 | ||||
|   /** | ||||
|    * analyser, corriger éventuellement et normaliser la valeur | ||||
|    * | ||||
|    * NB: si $value est un string. elle doit avoir déjà été traitée au préalable | ||||
|    * normaliser la valeur. elle *doit* déjà être valide. | ||||
|    * Si $value est un string. elle *doit* avoir déjà été traitée au préalable | ||||
|    * par extract() et parse() | ||||
|    * | ||||
|    * si la valeur était déjà normalisée, ou si une erreur s'est produite, | ||||
|    * retourner false. | ||||
|    * - si $result indique que la valeur est déjà normalisée, cette méthode ne | ||||
|    * fait rien | ||||
|    * - si la valeur était déjà normalisée, mettre à jour $result pour indiquer | ||||
|    * que la valeur est normalisée et retourner false | ||||
|    * - sinon, retourner true pour indiquer qu'il a fallut normaliser la valeur. | ||||
|    * $result n'est pas modifié | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool; | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool; | ||||
| 
 | ||||
|   /** | ||||
|    * formatter la valeur pour affichage. si $value n'est pas null, elle est | ||||
|  | ||||
| @ -37,7 +37,7 @@ class tarray extends _tstring { | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_array($value); | ||||
|     return is_scalar($value) || is_array($value); | ||||
|     return $normalized || is_scalar($value); | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
| @ -49,23 +49,14 @@ class tarray extends _tstring { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_array($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_array($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_string($value)) { | ||||
|       try { | ||||
|         $value = $this->parse($value); | ||||
|         $result->setValid(); | ||||
|         return true; | ||||
|       } catch (ValueException $e) { | ||||
|       } | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = cl::with($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } | ||||
|     $result->setInvalid($value, $schema); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -99,23 +99,14 @@ class tbool extends _tformatable { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_bool($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_bool($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_string($value)) { | ||||
|       try { | ||||
|         $value = $this->parse($value); | ||||
|         $result->setValid(); | ||||
|         return true; | ||||
|       } catch (ValueException $e) { | ||||
|       } | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = boolval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } | ||||
|     $result->setInvalid($value, $schema); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -27,7 +27,7 @@ class tcallable extends _tsimple { | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_callable($value); | ||||
|     $normalized = $value instanceof func; | ||||
|     return func::check($value); | ||||
|   } | ||||
| 
 | ||||
| @ -43,23 +43,14 @@ class tcallable extends _tsimple { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($value instanceof func) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif ($value instanceof func) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_callable($value)) { | ||||
|       $value = func::with($value); | ||||
|       $result->setNormalized(); | ||||
|       return true; | ||||
|     } elseif (is_string($value)) { | ||||
|       try { | ||||
|         $value = $this->parse($value); | ||||
|         $result->setValid(); | ||||
|         return true; | ||||
|       } catch (ValueException $e) { | ||||
|     } | ||||
|     } | ||||
|     $result->setInvalid($value, $schema); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -25,7 +25,7 @@ abstract class tcontent extends _tunion { | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_string($value) || is_array($value); | ||||
|     return is_scalar($value) || is_array($value); | ||||
|     return $normalized || is_scalar($value); | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
| @ -36,18 +36,15 @@ abstract class tcontent extends _tunion { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_string($value) || is_array($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_string($value) || is_array($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = strval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } else { | ||||
|       $result->setInvalid($value, $schema); | ||||
|       return false; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function format($value, $format=null): string { | ||||
|  | ||||
| @ -43,23 +43,14 @@ class tfloat extends _tformatable { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_float($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_float($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_string($value)) { | ||||
|       try { | ||||
|         $value = $this->parse($value); | ||||
|         $result->setValid(); | ||||
|         return true; | ||||
|       } catch (ValueException $e) { | ||||
|       } | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = floatval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } | ||||
|     $result->setInvalid($value, $schema); | ||||
|     return false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -29,8 +29,8 @@ class tgeneric extends _tsimple { | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = true; | ||||
|     return $value instanceof $this->class; | ||||
|     $normalized = $value instanceof $this->class; | ||||
|     return $normalized; | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
| @ -41,8 +41,8 @@ class tgeneric extends _tsimple { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     $result->setNormalized(); | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if (!$result->normalized) $result->setNormalized(); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -45,23 +45,14 @@ class tint extends _tformatable { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_int($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_int($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_string($value)) { | ||||
|       try { | ||||
|         $value = $this->parse($value); | ||||
|         $result->setValid(); | ||||
|         return true; | ||||
|       } catch (ValueException $e) { | ||||
|       } | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = intval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } | ||||
|     $result->setInvalid($value, $schema); | ||||
|     return false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -25,7 +25,7 @@ class tkey extends _tunion { | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_string($value) || is_int($value); | ||||
|     return is_scalar($value); | ||||
|     return $normalized || is_scalar($value); | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
| @ -36,18 +36,15 @@ class tkey extends _tunion { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_string($value) || is_int($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_string($value) || is_int($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = strval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } else { | ||||
|       $result->setInvalid($value, $schema); | ||||
|       return false; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function format($value, $format=null): string { | ||||
|  | ||||
| @ -35,8 +35,8 @@ class tmixed extends _tsimple { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     $result->setNormalized(); | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if (!$result->normalized) $result->setNormalized(); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -30,7 +30,7 @@ class tpkey extends _tunion { | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_string($value) || is_int($value) || is_array($value); | ||||
|     return is_scalar($value) || is_array($value); | ||||
|     return $normalized || is_scalar($value); | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
| @ -41,18 +41,15 @@ class tpkey extends _tunion { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_string($value) || is_int($value) || is_array($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_string($value) || is_int($value) || is_array($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = strval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } else { | ||||
|       $result->setInvalid($value, $schema); | ||||
|       return false; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function format($value, $format=null): string { | ||||
|  | ||||
| @ -44,18 +44,15 @@ class trawstring extends _tstring { | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_string($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_string($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = strval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } else { | ||||
|       $result->setInvalid($value, $schema); | ||||
|       return false; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function format($value, $format=null): string { | ||||
|  | ||||
| @ -5,7 +5,69 @@ use nulib\tests\TestCase; | ||||
| use stdClass; | ||||
| 
 | ||||
| class KeyAccessTest extends TestCase { | ||||
|   function testAccess() { | ||||
|   function testValueAccess() { | ||||
|     $default = new stdClass(); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = null; | ||||
|     $a = new KeyAccess($i); | ||||
|     self::assertFalse($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = false; | ||||
|     $a = new KeyAccess($i); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(false, $a->get($default)); | ||||
| 
 | ||||
|     $i = ""; | ||||
|     $a = new KeyAccess($i); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame("", $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = null; | ||||
|     $a = new KeyAccess($i, null, ["allow_null" => false]); | ||||
|     self::assertFalse($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = null; | ||||
|     $a = new KeyAccess($i, null, ["allow_null" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(null, $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = false; | ||||
|     $a = new KeyAccess($i, null, ["allow_false" => false]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = false; | ||||
|     $a = new KeyAccess($i, null, ["allow_false" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(false, $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = ""; | ||||
|     $a = new KeyAccess($i, null, ["allow_empty" => false]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = ""; | ||||
|     $a = new KeyAccess($i, null, ["allow_empty" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame("", $a->get($default)); | ||||
|   } | ||||
|    | ||||
|   function testArrayAccess() { | ||||
|     $default = new stdClass(); | ||||
|     $array = ["null" => null, "false" => false, "empty" => ""]; | ||||
| 
 | ||||
| @ -63,4 +125,91 @@ class KeyAccessTest extends TestCase { | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame("", $a->get($default)); | ||||
|   } | ||||
| 
 | ||||
|   private function _ensureAssoc(?array $orig, ?array $expected, array $keys, ?array $params=null) { | ||||
|     $v = $orig; $a = new KeyAccess($v); | ||||
|     $a->ensureAssoc($keys, $params); | ||||
|     self::assertSame($expected, $v); | ||||
|   } | ||||
|   function testEnsureAssoc() { | ||||
|     $keys = ["a", "b", "c"]; | ||||
| 
 | ||||
|     $this->_ensureAssoc(null, null, $keys); | ||||
|     $this->_ensureAssoc([], [], $keys); | ||||
|     $this->_ensureAssoc([1], ["a" => 1], $keys); | ||||
|     $this->_ensureAssoc([1, 2, 3], ["a" => 1, "b" => 2, "c" => 3], $keys); | ||||
|     $this->_ensureAssoc([1, 2, 3, 4], [3 => 4, "a" => 1, "b" => 2, "c" => 3], $keys); | ||||
|     $this->_ensureAssoc(["c" => 3, 1], ["c" => 3, "a" => 1], $keys); | ||||
|     $this->_ensureAssoc(["c" => 3, "b" => 2, 1], ["c" => 3, "b" => 2, "a" => 1], $keys); | ||||
|     $this->_ensureAssoc(["c" => 3, "b" => 2, "a" => 1], ["c" => 3, "b" => 2, "a" => 1], $keys); | ||||
|     $this->_ensureAssoc(["a" => 1, 2], ["a" => 1, "b" => 2], $keys); | ||||
|     $this->_ensureAssoc([2, "a" => 1], ["a" => 1, "b" => 2], $keys); | ||||
| 
 | ||||
|     $keys = [0, "a", "b"]; | ||||
|     $this->_ensureAssoc([1], [1], $keys); | ||||
|     $this->_ensureAssoc([1, 2], [1, "a" => 2], $keys); | ||||
|   } | ||||
| 
 | ||||
|   private function _ensureKeys(?array $orig, ?array $expected, array $defaults, ?array $params=null) { | ||||
|     $v = $orig; $a = new KeyAccess($v); | ||||
|     $a->ensureKeys($defaults, $params); | ||||
|     self::assertSame($expected, $v); | ||||
|   } | ||||
|   function testEnsureKeys() { | ||||
|     $defaults = ["a" => false, "b" => false, "c" => false]; | ||||
| 
 | ||||
|     $this->_ensureKeys(null, ["a" => false, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys([], ["a" => false, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys(["a" => 1], ["a" => 1, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys(["a" => 1, "b" => 2, "c" => 3], ["a" => 1, "b" => 2, "c" => 3], $defaults); | ||||
|     $this->_ensureKeys(["x"], ["x", "a" => false, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys(["x", "a" => 1], ["x", "a" => 1, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys(["a" => 1, "x"], ["a" => 1, "x", "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys(["a" => 1, "b" => 2, "c" => 3, "x"], ["a" => 1, "b" => 2, "c" => 3, "x"], $defaults); | ||||
|   } | ||||
| 
 | ||||
|   private function _ensureOrder(?array $orig, ?array $expected, array $keys, ?array $params=null) { | ||||
|     $v = $orig; $a = new KeyAccess($v); | ||||
|     $a->ensureOrder($keys, $params); | ||||
|     self::assertSame($expected, $v); | ||||
|   } | ||||
|   function testEnsureOrder() { | ||||
|     $keys = ["a", "b", "c"]; | ||||
| 
 | ||||
|     $this->_ensureOrder(null, null, $keys); | ||||
|     $this->_ensureOrder([], [], $keys); | ||||
|     $this->_ensureOrder([1], [1], $keys); | ||||
|     $this->_ensureOrder(["b" => 2, "a" => 1], ["a" => 1, "b" => 2], $keys); | ||||
|     $this->_ensureOrder(["c" => 3, "a" => 1], ["a" => 1, "c" => 3], $keys); | ||||
|   } | ||||
| 
 | ||||
|   private function _ensureAssocKeysOrder(?array $orig, ?array $expected, array $defaults, ?array $params=null) { | ||||
|     $v = $orig; $a = new KeyAccess($v); | ||||
|     $keys = array_keys($defaults); | ||||
|     $a->ensureAssoc($keys, $params); | ||||
|     $a->ensureKeys($defaults, $params); | ||||
|     $a->ensureOrder($keys, $params); | ||||
|     self::assertSame($expected, $v); | ||||
|   } | ||||
|   function testEnsureAssocKeysOrder() { | ||||
|     $defaults = ["a" => false, "b" => false, "c" => false]; | ||||
| 
 | ||||
|     $this->_ensureAssocKeysOrder(null, ["a" => false, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([], ["a" => false, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([1], ["a" => 1, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([1, 2, 3], ["a" => 1, "b" => 2, "c" => 3], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([1, 2, 3, 4], ["a" => 1, "b" => 2, "c" => 3, 4], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([1, 2, 3, 4], ["a" => 1, "b" => 2, "c" => 3, 3 => 4], $defaults, [ | ||||
|       "preserve_keys" => true, | ||||
|     ]); | ||||
|     $this->_ensureAssocKeysOrder(["c" => 3, 1], ["a" => 1, "b" => false, "c" => 3], $defaults); | ||||
|     $this->_ensureAssocKeysOrder(["c" => 3, "b" => 2, 1], ["a" => 1, "b" => 2, "c" => 3], $defaults); | ||||
|     $this->_ensureAssocKeysOrder(["c" => 3, "b" => 2, "a" => 1], ["a" => 1, "b" => 2, "c" => 3], $defaults); | ||||
|     $this->_ensureAssocKeysOrder(["a" => 1, 2], ["a" => 1, "b" => 2, "c" => false], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([2, "a" => 1], ["a" => 1, "b" => 2, "c" => false], $defaults); | ||||
| 
 | ||||
|     $this->_ensureAssocKeysOrder([1], ["x_a" => 1, "x_b" => false, "x_c" => false], $defaults, [ | ||||
|       "key_prefix" => "x_", | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,69 +0,0 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| 
 | ||||
| use nulib\tests\TestCase; | ||||
| use stdClass; | ||||
| 
 | ||||
| class ValueAccessTest extends TestCase { | ||||
|   function testAccess() { | ||||
|     $default = new stdClass(); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = null; | ||||
|     $a = new ValueAccess($i); | ||||
|     self::assertFalse($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = false; | ||||
|     $a = new ValueAccess($i); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(false, $a->get($default)); | ||||
| 
 | ||||
|     $i = ""; | ||||
|     $a = new ValueAccess($i); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame("", $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = null; | ||||
|     $a = new ValueAccess($i, ["allow_null" => false]); | ||||
|     self::assertFalse($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = null; | ||||
|     $a = new ValueAccess($i, ["allow_null" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(null, $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = false; | ||||
|     $a = new ValueAccess($i, ["allow_false" => false]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = false; | ||||
|     $a = new ValueAccess($i, ["allow_false" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(false, $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = ""; | ||||
|     $a = new ValueAccess($i, ["allow_empty" => false]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = ""; | ||||
|     $a = new ValueAccess($i, ["allow_empty" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame("", $a->get($default)); | ||||
|   } | ||||
| } | ||||
| @ -7,7 +7,14 @@ use nur\sery\wip\schema\_scalar\ScalarSchemaTest; | ||||
| 
 | ||||
| class AssocSchemaTest extends TestCase { | ||||
|   const NULL_SCHEMA = [ | ||||
|     "" => ["assoc"], | ||||
|     "" => [ | ||||
|       "assoc", | ||||
|       "compute_func" => null, | ||||
|       "validate_func" => null, | ||||
|       "ensure_array" => false, | ||||
|       "ensure_keys" => true, | ||||
|       "ensure_order" => true, | ||||
|     ], | ||||
|     "schema" => null, | ||||
|     "type" => [null], | ||||
|     "default" => null, | ||||
| @ -44,7 +51,7 @@ class AssocSchemaTest extends TestCase { | ||||
|         "type" => ["string"], "nullable" => false, | ||||
|         "name" => "a", "pkey" => "a", "header" => "a", | ||||
|       ], | ||||
|     ]), AssocSchema::normalize(["a" => "string"])); | ||||
|     ]), AssocSchema::normalize_definition(["a" => "string"])); | ||||
| 
 | ||||
|     self::assertSame(self::schema([ | ||||
|       "type" => ["array"], "nullable" => true, | ||||
| @ -61,7 +68,7 @@ class AssocSchemaTest extends TestCase { | ||||
|         "type" => ["bool"], "nullable" => false, | ||||
|         "name" => "c", "pkey" => "c", "header" => "c", | ||||
|       ], | ||||
|     ]), AssocSchema::normalize([ | ||||
|     ]), AssocSchema::normalize_definition([ | ||||
|       "a" => "string", | ||||
|       "b" => "int", | ||||
|       "c" => "bool", | ||||
| @ -90,6 +97,72 @@ class AssocSchemaTest extends TestCase { | ||||
|         "name" => "c", "pkey" => "c", "header" => "c", | ||||
|       ], | ||||
|     ]), $schema->getDefinition()); | ||||
|     yaml::dump($schema->getDefinition()); | ||||
|     //yaml::dump($schema->getDefinition());
 | ||||
|   } | ||||
| 
 | ||||
|   function testWrapper() { | ||||
|     $schema = new AssocSchema([ | ||||
|       "a" => "?string", | ||||
|       "b" => "?int", | ||||
|       "c" => "?bool", | ||||
|     ]); | ||||
|     $array = ["a" => "  string  ", "b" => "  42 ", "c" => false]; | ||||
|     $schema->getWrapper($array); | ||||
|     self::assertSame([ | ||||
|       "a" => "string", | ||||
|       "b" => 42, | ||||
|       "c" => false, | ||||
|     ], $array); | ||||
| 
 | ||||
|     $schema = new AssocSchema([ | ||||
|       "a" => "string", | ||||
|       "b" => "int", | ||||
|       "c" => "bool", | ||||
|     ]); | ||||
| 
 | ||||
|     $array = ["a" => "  string  "]; | ||||
|     $schema->getWrapper($array); | ||||
|     self::assertSame([ | ||||
|       "a" => "string", | ||||
|       "b" => false, | ||||
|       "c" => null, | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["c" => false, "a" => "  string  "]; | ||||
|     $schema->getWrapper($array); | ||||
|     self::assertSame([ | ||||
|       "a" => "string", | ||||
|       "b" => false, | ||||
|       "c" => false, | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["a" => "  string  "]; | ||||
|     $schema->getWrapper($array, null, ["ensure_order" => false]); | ||||
|     self::assertSame([ | ||||
|       "a" => "string", | ||||
|       "b" => false, | ||||
|       "c" => null, | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["c" => false, "a" => "  string  "]; | ||||
|     $schema->getWrapper($array, null, ["ensure_order" => false]); | ||||
|     self::assertSame([ | ||||
|       "c" => false, | ||||
|       "a" => "string", | ||||
|       "b" => false, | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["a" => "  string  "]; | ||||
|     $schema->getWrapper($array, null, ["ensure_keys" => false]); | ||||
|     self::assertSame([ | ||||
|       "a" => "string", | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["c" => false, "a" => "  string  "]; | ||||
|     $schema->getWrapper($array, null, ["ensure_keys" => false]); | ||||
|     self::assertSame([ | ||||
|       "a" => "string", | ||||
|       "c" => false, | ||||
|     ], $array); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -19,7 +19,11 @@ class ScalarSchemaTest extends TestCase { | ||||
|     "messages" => null, | ||||
|     "formatter_func" => null, | ||||
|     "format" => null, | ||||
|     "" => ["scalar"], | ||||
|     "" => [ | ||||
|       "scalar", | ||||
|       "compute_func" => null, | ||||
|       "validate_func" => null, | ||||
|     ], | ||||
|     "schema" => null, | ||||
|     "name" => null, | ||||
|     "pkey" => null, | ||||
| @ -32,33 +36,33 @@ class ScalarSchemaTest extends TestCase { | ||||
|   } | ||||
| 
 | ||||
|   function testNormalize() { | ||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize(null)); | ||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([])); | ||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([null])); | ||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition(null)); | ||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition([])); | ||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition([null])); | ||||
|     self::assertException(SchemaException::class, function () { | ||||
|       ScalarSchema::normalize([[]]); | ||||
|       ScalarSchema::normalize_definition([[]]); | ||||
|     }); | ||||
|     self::assertException(SchemaException::class, function () { | ||||
|       ScalarSchema::normalize([[null]]); | ||||
|       ScalarSchema::normalize_definition([[null]]); | ||||
|     }); | ||||
| 
 | ||||
|     $string = self::schema(["type" => ["string"], "nullable" => false]); | ||||
|     self::assertSame($string, ScalarSchema::normalize("string")); | ||||
|     self::assertSame($string, ScalarSchema::normalize(["string"])); | ||||
|     self::assertSame($string, ScalarSchema::normalize_definition("string")); | ||||
|     self::assertSame($string, ScalarSchema::normalize_definition(["string"])); | ||||
| 
 | ||||
|     $nstring = self::schema(["type" => ["string"]]); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize(["?string"])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize(["?string|null"])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize(["string|null"])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize([["?string", "null"]])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize([["string", "null"]])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize([["string", null]])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition(["?string"])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition(["?string|null"])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition(["string|null"])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition([["?string", "null"]])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition([["string", "null"]])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition([["string", null]])); | ||||
| 
 | ||||
|     $key = self::schema(["type" => ["string", "int"], "nullable" => false]); | ||||
|     self::assertSame($key, ScalarSchema::normalize("string|int")); | ||||
|     self::assertSame($key, ScalarSchema::normalize_definition("string|int")); | ||||
| 
 | ||||
|     $nkey = self::schema(["type" => ["string", "int"], "nullable" => true]); | ||||
|     self::assertSame($nkey, ScalarSchema::normalize("?string|int")); | ||||
|     self::assertSame($nkey, ScalarSchema::normalize("string|?int")); | ||||
|     self::assertSame($nkey, ScalarSchema::normalize_definition("?string|int")); | ||||
|     self::assertSame($nkey, ScalarSchema::normalize_definition("string|?int")); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -15,19 +15,21 @@ class ScalarWrapperTest extends TestCase { | ||||
|     self::assertSame($normalized, $wrapper->isNormalized(), "normalized"); | ||||
|   } | ||||
| 
 | ||||
|   function checkVerifix(ScalarSchema $schema, $orig, bool $verifix, $value, bool $present, bool $available, bool $valid, bool $normalized, ?array $inputParams=null): void { | ||||
|   function checkVerifix(ScalarSchema $schema, $orig, bool $normalize, $value, bool $present, bool $available, bool $valid, bool $normalized, ?array $inputParams=null): void { | ||||
|     $wrapper = $schema->getWrapper(); | ||||
|     $wrapper->resetParams(["normalize" => $normalize]); | ||||
|     if ($inputParams !== null) $input = new Input($orig, $inputParams); | ||||
|     else $input = $orig; | ||||
|     $wrapper->reset($input, null, $verifix); | ||||
|     $wrapper->reset($input); | ||||
|     $this->checkValue($wrapper, $value, $present, $available, $valid, $normalized); | ||||
|   } | ||||
| 
 | ||||
|   function checkException(ScalarSchema $schema, $orig, bool $verifix, string $exceptionClass, ?array $inputParams=null) { | ||||
|   function checkException(ScalarSchema $schema, $orig, bool $normalize, string $exceptionClass, ?array $inputParams=null) { | ||||
|     $wrapper = $schema->getWrapper(); | ||||
|     if ($inputParams !== null) $orig = new Input($orig, $inputParams); | ||||
|     self::assertException($exceptionClass, function() use ($wrapper, &$orig, $verifix) { | ||||
|       $wrapper->reset($orig, null, $verifix); | ||||
|     self::assertException($exceptionClass, function() use ($wrapper, &$orig, $normalize) { | ||||
|       $wrapper->resetParams(["normalize" => $normalize]); | ||||
|       $wrapper->reset($orig); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user