<pman>Intégration de la branche dev74
This commit is contained in:
		
						commit
						9d7dbd76d1
					
				| @ -8,6 +8,18 @@ use nulib\cl; | |||||||
|  * de {@link IAccess} |  * de {@link IAccess} | ||||||
|  */ |  */ | ||||||
| abstract class AbstractAccess implements 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 { |   function inc(): int { | ||||||
|     $value = (int)$this->get(); |     $value = (int)$this->get(); | ||||||
|     $this->set(++$value); |     $this->set(++$value); | ||||||
| @ -33,4 +45,13 @@ abstract class AbstractAccess implements IAccess { | |||||||
|     cl::set($array, $key, $value); |     cl::set($array, $key, $value); | ||||||
|     $this->set($array); |     $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: accès à une valeur de $_POST puis $_GET, dans cet ordre | ||||||
|  */ |  */ | ||||||
| class FormAccess extends AbstractAccess { | class FormAccess extends AbstractAccess { | ||||||
|  |   const ALLOW_EMPTY = false; | ||||||
|  | 
 | ||||||
|   function __construct($key, ?array $params=null) { |   function __construct($key, ?array $params=null) { | ||||||
|  |     parent::__construct($params); | ||||||
|     $this->key = $key; |     $this->key = $key; | ||||||
|     $this->allowEmpty = $params["allow_empty"] ?? false; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** @var int|string */ |   /** @var null|int|string|array */ | ||||||
|   protected $key; |   protected $key; | ||||||
| 
 | 
 | ||||||
|   protected bool $allowEmpty; |   protected function _exists(array $first, ?array $second=null): bool { | ||||||
| 
 |  | ||||||
|   function exists(): bool { |  | ||||||
|     $key = $this->key; |     $key = $this->key; | ||||||
|     if ($key === null) return false; |     if ($key === null) return true; | ||||||
|     return array_key_exists($key, $_POST) || array_key_exists($key, $_GET); |     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; |     $key = $this->key; | ||||||
|     if ($key === null) return false; |     if ($key === null) return true; | ||||||
|     if (array_key_exists($key, $_POST)) { |     if (cl::phas($first, $key)) { | ||||||
|       return $this->allowEmpty || $_POST[$key] !== ""; |       return $this->allowEmpty || cl::pget($first, $key) !== ""; | ||||||
|     } elseif (array_key_exists($key, $_GET)) { |     } elseif ($second !== null && cl::phas($second, $key)) { | ||||||
|       return $this->allowEmpty || $_GET[$key] !== ""; |       return $this->allowEmpty || cl::pget($second, $key) !== ""; | ||||||
|     } else { |     } else { | ||||||
|       return false; |       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; |     $key = $this->key; | ||||||
|     if ($key === null) return $default; |     if ($key === null) return cl::merge($first, $second); | ||||||
|     if (array_key_exists($key, $_POST)) { |     if (cl::phas($first, $key)) { | ||||||
|       $value = $_POST[$key]; |       $value = cl::pget($first, $key); | ||||||
|       if ($value === "" && !$this->allowEmpty) return $default; |       if ($value !== "" || $this->allowEmpty) return $value; | ||||||
|       return $value; |     } elseif ($second !== null && cl::phas($second, $key)) { | ||||||
|     } elseif (array_key_exists($key, $_GET)) { |       $value = cl::pget($second, $key); | ||||||
|       $value = $_GET[$key]; |       if ($value !== "" || $this->allowEmpty) return $value; | ||||||
|       if ($value === "" && !$this->allowEmpty) return $default; |     } | ||||||
|       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 { |     } else { | ||||||
|       return $default; |       cl::pset($first, $key, $value); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function set($value): void { |   function set($value): void { | ||||||
|  |     $this->_set($value, $_POST, $_GET); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function _del(array &$first, ?array &$second=null): void { | ||||||
|     $key = $this->key; |     $key = $this->key; | ||||||
|     if ($key === null) return; |     if ($key === null) { | ||||||
|     if (!array_key_exists($key, $_POST) && array_key_exists($key, $_GET)) { |       # interdire la modification de la destination
 | ||||||
|       cl::set($_GET, $key, $value); |       return; | ||||||
|  |     } | ||||||
|  |     if ($second !== null && !cl::phas($first, $key) && cl::phas($second, $key)) { | ||||||
|  |       cl::pdel($second, $key); | ||||||
|     } else { |     } else { | ||||||
|       cl::set($_POST, $key, $value); |       cl::pdel($first, $key); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function del(): void { |   function del(): void { | ||||||
|     $key = $this->key; |     $this->_del($_POST, $_GET); | ||||||
|     if ($key === null) return; |   } | ||||||
|     if (!array_key_exists($key, $_POST) && array_key_exists($key, $_GET)) { | 
 | ||||||
|       cl::del($_GET, $key); |   function addKey($key): self { | ||||||
|     } else { |     if ($key === null) return $this; | ||||||
|       cl::del($_POST, $key); |     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 { | class GetAccess extends FormAccess { | ||||||
|   function exists(): bool { |   function exists(): bool { | ||||||
|     $key = $this->key; |     return $this->_exists($_GET); | ||||||
|     if ($key === null) return false; |  | ||||||
|     return array_key_exists($key, $_GET); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public function available(): bool { |   public function available(): bool { | ||||||
|     $key = $this->key; |     return $this->_available($_GET); | ||||||
|     if ($key === null) return false; |  | ||||||
|     if (array_key_exists($key, $_GET)) { |  | ||||||
|       return $this->allowEmpty || $_GET[$key] !== ""; |  | ||||||
|     } else { |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function get($default=null) { |   function get($default=null) { | ||||||
|     $key = $this->key; |     return $this->_get($default, $_GET); | ||||||
|     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; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function set($value): void { |   function set($value): void { | ||||||
|     $key = $this->key; |     $this->_set($value, $_GET); | ||||||
|     if ($key === null) return; |  | ||||||
|     cl::set($_GET, $key, $value); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function del(): void { |   function del(): void { | ||||||
|     $key = $this->key; |     $this->_del($_GET); | ||||||
|     if ($key === null) return; |  | ||||||
|     cl::del($_GET, $key); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| <?php | <?php | ||||||
| namespace nur\sery\wip\php\access; | namespace nur\sery\wip\php\access; | ||||||
| 
 | 
 | ||||||
|  | use ReflectionClass; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Interface IAccess: abstraction d'un accès complet à une valeur |  * Interface IAccess: abstraction d'un accès complet à une valeur | ||||||
|  */ |  */ | ||||||
| @ -25,4 +27,26 @@ interface IAccess extends IGetter, ISetter, IDeleter { | |||||||
|    * tableau si $key===null |    * tableau si $key===null | ||||||
|    */ |    */ | ||||||
|   function append($value, $key=null): void; |   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; |   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 */ |   /** @return bool true si la valeur existe et est utilisable, false sinon */ | ||||||
|   function available(): bool; |   function available(): bool; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,67 +3,186 @@ namespace nur\sery\wip\php\access; | |||||||
| 
 | 
 | ||||||
| use ArrayAccess; | use ArrayAccess; | ||||||
| use nulib\cl; | 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 { | class KeyAccess extends AbstractAccess { | ||||||
|   function __construct(&$array, $key, ?array $params=null) { |   const ALLOW_NULL = null; | ||||||
|     $this->array =& $array; |   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->key = $key; | ||||||
|     $this->allowNull = $params["allow_null"] ?? true; |     $this->allowNull = $params["allow_null"] ?? static::ALLOW_NULL; | ||||||
|     $this->allowFalse = $params["allow_false"] ?? false; |     $this->allowFalse = $params["allow_false"] ?? static::ALLOW_FALSE; | ||||||
|     $this->allowEmpty = $params["allow_empty"] ?? true; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** @var array|ArrayAccess */ |   protected bool $protectDest; | ||||||
|   protected $array; |  | ||||||
| 
 | 
 | ||||||
|   function reset(&$array): self { |   /** @var mixed|array|ArrayAccess */ | ||||||
|     $this->array =& $array; |   protected $dest; | ||||||
|  | 
 | ||||||
|  |   /** @var null|int|string|array */ | ||||||
|  |   protected $key; | ||||||
|  | 
 | ||||||
|  |   function reset(&$dest, $key=null): self { | ||||||
|  |     $this->dest =& $dest; | ||||||
|  |     $this->key = $key; | ||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** @var int|string */ |   function resetKey($key=null): self { | ||||||
|   protected $key; |     $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 { |   function exists(): bool { | ||||||
|     $key = $this->key; |     $key = $this->key; | ||||||
|     if ($key === null) return false; |     if ($key === null) { | ||||||
|     return cl::has($this->array, $key); |       return $this->isAllowNull() || $this->dest !== null; | ||||||
|  |     } else { | ||||||
|  |       return cl::phas($this->dest, $key); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function available(): bool { |   function available(): bool { | ||||||
|     if (!$this->exists()) return false; |     if (!$this->exists()) return false; | ||||||
|     $value = cl::get($this->array, $this->key); |     $key = $this->key; | ||||||
|     if ($value === null) return $this->allowNull; |     if ($key === null) $value = $this->dest; | ||||||
|     if ($value === false) return $this->allowFalse; |     else $value = cl::pget($this->dest, $key); | ||||||
|     if ($value === "") return $this->allowEmpty; |     if ($value === "") return $this->allowEmpty; | ||||||
|  |     if ($value === null) return $this->isAllowNull(); | ||||||
|  |     if ($value === false) return $this->isAllowFalse(); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function get($default=null) { |   function get($default=null) { | ||||||
|     if ($this->key === null) return $default; |     $key = $this->key; | ||||||
|     $value = cl::get($this->array, $this->key, $default); |     if ($key === null) $value = $this->dest; | ||||||
|     if ($value === null && !$this->allowNull) return $default; |     else $value = cl::pget($this->dest, $key, $default); | ||||||
|     if ($value === false && !$this->allowFalse) return $default; |  | ||||||
|     if ($value === "" && !$this->allowEmpty) return $default; |     if ($value === "" && !$this->allowEmpty) return $default; | ||||||
|  |     if ($value === null && !$this->isAllowNull()) return $default; | ||||||
|  |     if ($value === false && !$this->isAllowFalse()) return $default; | ||||||
|     return $value; |     return $value; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function set($value): void { |   function set($value): void { | ||||||
|     if ($this->key === null) return; |     $key = $this->key; | ||||||
|     cl::set($this->array, $this->key, $value); |     if ($key === null) { | ||||||
|  |       if (!$this->protectDest) $this->dest = $value; | ||||||
|  |     } else { | ||||||
|  |       cl::pset($this->dest, $key, $value); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function del(): void { |   function del(): void { | ||||||
|     if ($this->key === null) return; |     $key = $this->key; | ||||||
|     cl::del($this->array, $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 { | class PostAccess extends FormAccess { | ||||||
|   function exists(): bool { |   function exists(): bool { | ||||||
|     $key = $this->key; |     return $this->_exists($_POST); | ||||||
|     if ($key === null) return false; |  | ||||||
|     return array_key_exists($key, $_POST); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public function available(): bool { |   public function available(): bool { | ||||||
|     $key = $this->key; |     return $this->_available($_POST); | ||||||
|     if ($key === null) return false; |  | ||||||
|     if (array_key_exists($key, $_POST)) { |  | ||||||
|       return $this->allowEmpty || $_POST[$key] !== ""; |  | ||||||
|     } else { |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function get($default=null) { |   function get($default=null) { | ||||||
|     $key = $this->key; |     return $this->_get($default, $_POST); | ||||||
|     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; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function set($value): void { |   function set($value): void { | ||||||
|     $key = $this->key; |     $this->_set($value, $_POST); | ||||||
|     if ($key === null) return; |  | ||||||
|     cl::set($_POST, $key, $value); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function del(): void { |   function del(): void { | ||||||
|     $key = $this->key; |     $this->_del($_POST); | ||||||
|     if ($key === null) return; |  | ||||||
|     cl::del($_POST, $key); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										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 { | class ShadowAccess extends AbstractAccess { | ||||||
|   function __construct(IAccess $reader, IAccess $writer) { |   function __construct(IAccess $reader, IAccess $writer) { | ||||||
|  |     parent::__construct(); | ||||||
|     $this->reader = $reader; |     $this->reader = $reader; | ||||||
|     $this->writer = $writer; |     $this->writer = $writer; | ||||||
|     $this->getter = $reader; |     $this->getter = $reader; | ||||||
| @ -27,6 +28,10 @@ class ShadowAccess extends AbstractAccess { | |||||||
| 
 | 
 | ||||||
|   protected IGetter $getter; |   protected IGetter $getter; | ||||||
| 
 | 
 | ||||||
|  |   public function isAllowEmpty(): bool { | ||||||
|  |     return $this->getter->isAllowEmpty(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   function exists(): bool { |   function exists(): bool { | ||||||
|     return $this->getter->exists(); |     return $this->getter->exists(); | ||||||
|   } |   } | ||||||
| @ -48,4 +53,20 @@ class ShadowAccess extends AbstractAccess { | |||||||
|     $this->writer->del(); |     $this->writer->del(); | ||||||
|     $this->getter = $this->reader; |     $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; | namespace nur\sery\wip\schema; | ||||||
| 
 | 
 | ||||||
| use IteratorAggregate; | use IteratorAggregate; | ||||||
| use nur\sery\wip\schema\_assoc\AssocResult; | use Throwable; | ||||||
| use nur\sery\wip\schema\_list\ListResult; |  | ||||||
| use nur\sery\wip\schema\_scalar\ScalarResult; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Class Result: résultat de l'analyse ou de la normalisation d'une valeur |  * 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 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 $messageKey clé de message si la valeur n'est pas valide | ||||||
|  * @property string|null $message 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 string|null $origValue valeur originale avant extraction et analyse | ||||||
|  * @property mixed|null $normalizedValue la valeur normalisée si elle est |  * @property mixed|null $normalizedValue la valeur normalisée si elle est | ||||||
|  *  disponible, null sinon. ce champ est utilisé comme optimisation si la valeur |  *  disponible, null sinon. ce champ est utilisé comme optimisation si la valeur | ||||||
| @ -26,7 +26,7 @@ abstract class Result implements IteratorAggregate { | |||||||
|   const KEYS = [ |   const KEYS = [ | ||||||
|     "resultAvailable", |     "resultAvailable", | ||||||
|     "present", "available", "null", "valid", "normalized", |     "present", "available", "null", "valid", "normalized", | ||||||
|     "messageKey", "message", |     "messageKey", "message", "exception", | ||||||
|     "origValue", "normalizedValue", |     "origValue", "normalizedValue", | ||||||
|   ]; |   ]; | ||||||
| 
 | 
 | ||||||
| @ -34,10 +34,6 @@ abstract class Result implements IteratorAggregate { | |||||||
|     $this->reset(); |     $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 |    * Obtenir la liste des clés valides pour les valeurs accessibles via cet | ||||||
|    * objet |    * 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\tpkey; | ||||||
| use nur\sery\wip\schema\types\trawstring; | 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 { | abstract class Schema implements ArrayAccess { | ||||||
|   /** |   /** | ||||||
|    * créer le cas échéant une nouvelle instance de {@link Schema} à partir d'une |    * 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
 |       # schéma d'un scalaire quelconque), on ne l'autorise pas ici
 | ||||||
|       throw SchemaException::invalid_schema("definition is required"); |       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 { |   protected static function have_nature(array $definition, ?string &$nature=null): bool { | ||||||
| @ -75,18 +98,18 @@ abstract class Schema implements ArrayAccess { | |||||||
|     return false; |     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]; |     if (!is_array($definition)) $definition = [$definition]; | ||||||
|     # s'assurer que toutes les clés existent avec leur valeur par défaut
 |     # s'assurer que toutes les clés existent avec leur valeur par défaut
 | ||||||
|     $index = 0; |     $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($key, $definition)) { | ||||||
|         if (array_key_exists($index, $definition)) { |         if (array_key_exists($index, $definition)) { | ||||||
|           $definition[$key] = $definition[$index]; |           $definition[$key] = $definition[$index]; | ||||||
|           unset($definition[$index]); |           unset($definition[$index]); | ||||||
|           $index++; |           $index++; | ||||||
|         } else { |         } 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
 | ||||||
|     $nature = $definition[""]; |     $nature = $definition[""]; | ||||||
|     tarray::ensure_array($nature); |     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; |     $definition[""] = $nature; | ||||||
|     # name, pkey, header
 |     # name, pkey, header
 | ||||||
|     $name = $definition["name"]; |     $name = $definition["name"]; | ||||||
| @ -169,11 +198,11 @@ abstract class Schema implements ArrayAccess { | |||||||
|     switch ($nature[0] ?? null) { |     switch ($nature[0] ?? null) { | ||||||
|     case "assoc": |     case "assoc": | ||||||
|       foreach ($definition["schema"] as $key => &$keydef) { |       foreach ($definition["schema"] as $key => &$keydef) { | ||||||
|         self::_normalize($keydef, $key); |         self::_normalize_definition($keydef, $key); | ||||||
|       }; unset($keydef); |       }; unset($keydef); | ||||||
|       break; |       break; | ||||||
|     case "list": |     case "list": | ||||||
|       self::_normalize($definition["schema"]); |       self::_normalize_definition($definition["schema"]); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -246,6 +275,13 @@ abstract class Schema implements ArrayAccess { | |||||||
|     return $this->_definition; |     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 */ |   /** retourner true si le schéma est de nature tableau associatif */ | ||||||
|   function isAssoc(?AssocSchema &$schema=null): bool { return false; } |   function isAssoc(?AssocSchema &$schema=null): bool { return false; } | ||||||
|   /** retourner true si le schéma est de nature liste */ |   /** 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 */ |   /** retourner true si le schéma est de nature scalaire */ | ||||||
|   function isScalar(?ScalarSchema &$schema=null): bool { return false; } |   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
 |   # key & properties
 | ||||||
| @ -272,7 +310,15 @@ abstract class Schema implements ArrayAccess { | |||||||
|     throw AccessException::read_only(null, $offset); |     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) { |   function __get($name) { | ||||||
|     $pkey = cl::get(static::_PROPERTY_PKEYS, $name, $name); |     $pkey = cl::get(static::_PROPERTY_PKEYS, $name, $name); | ||||||
|     return cl::pget($this->definition, $pkey); |     return cl::pget($this->definition, $pkey); | ||||||
|  | |||||||
| @ -1,20 +1,32 @@ | |||||||
| # nulib\schema | # nulib\schema | ||||||
| 
 | 
 | ||||||
| * instance de WrapperContext directement dans le schéma  | * ensureKeys() et orderKeys() se fait au niveau de access (ou input?) | ||||||
| * plus de {key} ni {orig} dans messages |   * access/input ne pouvant pas connaître les valeurs appropriées, c'est le | ||||||
|   * les messages standard ne sont utilisés que s'il n'y a pas de message dans |     schéma qui les génère. ensureKeys($values) | ||||||
|     l'exception |   * méthode ensureAssoc() transforme les clés séquentielles en clés associatives | ||||||
|   * si instance de UserException, prendre le message "non technique" pour | * l'ordre est `ensureAssoc [--> ensureKeys] [--> orderKeys]` | ||||||
|     résultat | 
 | ||||||
|  | * 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 | * valeurs composite/computed | ||||||
|   * analyse / vérification de la valeur complète après calcul du résultat, si |   * analyse / vérification de la valeur complète après calcul du résultat, si | ||||||
|     tous les résultats sont bons |     tous les résultats sont bons | ||||||
|   * calcul des valeurs composites/computed par une fonction avant/après l'analyse |   * calcul des valeurs composites/computed par une fonction avant/après l'analyse | ||||||
|     globale si résultat ok |     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.) | * tdate et tdatetime. qu'en est-il des autres classes (delay, etc.) | ||||||
|   * possibilité de spécifier le format de la date à analyser  |   * possibilité de spécifier le format de la date à analyser  | ||||||
| * ScalarSchema::from_property() | * 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 | * dans AssocSchema, support `[key_prefix]` qui permet de spécifier un préfixe | ||||||
|   commun aux champs dans le tableau destination, e.g |   commun aux champs dans le tableau destination, e.g | ||||||
|   ~~~php |   ~~~php | ||||||
| @ -58,6 +70,8 @@ | |||||||
| 
 | 
 | ||||||
|   la définition de ces "circonstances" est encore à faire: soit un paramètre |   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 |   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 | -*- 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 ArrayAccess; | ||||||
| use IteratorAggregate; | use IteratorAggregate; | ||||||
|  | use nulib\php\func; | ||||||
| use nur\sery\wip\schema\_assoc\AssocWrapper; | use nur\sery\wip\schema\_assoc\AssocWrapper; | ||||||
| use nur\sery\wip\schema\_list\ListWrapper; | 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\_scalar\ScalarWrapper; | ||||||
|  | use nur\sery\wip\schema\input\Input; | ||||||
| use nur\sery\wip\schema\types\IType; | use nur\sery\wip\schema\types\IType; | ||||||
| 
 | 
 | ||||||
| abstract class Wrapper implements ArrayAccess, IteratorAggregate { | 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 isList(?ListWrapper &$wrapper=null): bool { return false; } | ||||||
|   function isScalar(?ScalarWrapper &$wrapper=null): bool { return false; } |   function isScalar(?ScalarWrapper &$wrapper=null): bool { return false; } | ||||||
| 
 | 
 | ||||||
|   /** spécifier la valeur destination gérée par cet objet */ |   protected WrapperContext $context; | ||||||
|   abstract function reset(&$value, $valueKey=null, ?bool $verifix=null): self; | 
 | ||||||
|  |   /** 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 |    * 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 |    * obtenir le résultat de l'analyse de la valeur du wrapper sélectionné | ||||||
|    * {@link unset()} |    * | ||||||
|  |    * 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 */ |   /** 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 */ |   /** 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 */ |   /** 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 */ |   /** 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 */ |   /** 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 */ |   function get($default=null, $key=false) { | ||||||
|   abstract function get($default=null); |     $context = $this->context; | ||||||
|  |     if (!$context->result->available) return $default; | ||||||
|  |     return $context->input->get($context->valueKey); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   /** remplacer la valeur */ |   function set($value, ?array $params=null, $key=false): self { | ||||||
|   abstract function set($value): self; |     $context = $this->context; | ||||||
|  |     $context->input->set($value, $context->valueKey); | ||||||
|  |     $this->afterModify($params); | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   /** supprimer la valeur */ |   function unset(?array $params=null, $key=false): self { | ||||||
|   abstract function unset(): 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 */ |   /** 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
 |   # key & properties
 | ||||||
| @ -78,14 +208,14 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function offsetGet($offset) { |   function offsetGet($offset) { | ||||||
|     return $this->select($offset); |     return $this->get(null, $offset); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function offsetSet($offset, $value): void { |   function offsetSet($offset, $value): void { | ||||||
|     $this->select($offset)->set($value); |     $this->set($value, null, $offset); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function offsetUnset($offset): void { |   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; | use nur\sery\wip\schema\types\IType; | ||||||
| 
 | 
 | ||||||
| class WrapperContext { | 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->schema = $schema; | ||||||
|     $this->wrapper = $wrapper; |     if ($input !== null) $this->input = $input; | ||||||
|     $this->input = $input; |  | ||||||
|     $this->result = $result; |  | ||||||
|     $this->type = null; |  | ||||||
|     $this->origValue = null; |  | ||||||
|     $this->value = null; |  | ||||||
|     $this->valueKey = $valueKey; |     $this->valueKey = $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; |   public Schema $schema; | ||||||
|   public Wrapper $wrapper; |   /** source et destination de la valeur */ | ||||||
|   public Input $input; |   public Input $input; | ||||||
|   public Result $result; |   /** @var string|int|null clé de la valeur dans le tableau destination */ | ||||||
|   public ?IType $type; |  | ||||||
|   /** @var mixed */ |  | ||||||
|   public $origValue; |  | ||||||
|   /** @var mixed */ |  | ||||||
|   public $value; |  | ||||||
|   /** @var int|string|null */ |  | ||||||
|   public $valueKey; |   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\cl; | ||||||
| use nulib\ref\schema\ref_schema; | use nulib\ref\schema\ref_schema; | ||||||
|  | use nulib\ValueException; | ||||||
| use nur\sery\wip\schema\Schema; | use nur\sery\wip\schema\Schema; | ||||||
| use nur\sery\wip\schema\Wrapper; | use nur\sery\wip\schema\Wrapper; | ||||||
| 
 | 
 | ||||||
| @ -10,12 +11,9 @@ use nur\sery\wip\schema\Wrapper; | |||||||
|  * Class AssocSchema |  * Class AssocSchema | ||||||
|  */ |  */ | ||||||
| class AssocSchema extends Schema { | 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 |    * 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 { |   static function isa_definition($definition): bool { | ||||||
|     if (!is_array($definition)) return false; |     if (!is_array($definition)) return false; | ||||||
| @ -27,7 +25,7 @@ class AssocSchema extends Schema { | |||||||
|     return !cl::have_num_keys($definition); |     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 (!is_array($definition)) $definition = [$definition]; | ||||||
|     if (!self::have_nature($definition)) { |     if (!self::have_nature($definition)) { | ||||||
|       $definition = [ |       $definition = [ | ||||||
| @ -36,7 +34,8 @@ class AssocSchema extends Schema { | |||||||
|         "schema" => $definition, |         "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"); |     self::_ensure_nature($definition, "assoc", "array"); | ||||||
|     return $definition; |     return $definition; | ||||||
|   } |   } | ||||||
| @ -44,12 +43,17 @@ class AssocSchema extends Schema { | |||||||
|   function __construct($definition=null, $definitionKey=null, bool $normalize=true) { |   function __construct($definition=null, $definitionKey=null, bool $normalize=true) { | ||||||
|     if ($definition === null) $definition = static::SCHEMA; |     if ($definition === null) $definition = static::SCHEMA; | ||||||
|     if ($normalize) { |     if ($normalize) { | ||||||
|       $definition = self::normalize($definition, $definitionKey); |       $definition = self::normalize_definition($definition, $definitionKey); | ||||||
|       $this->_definition = $definition; |       $this->_definition = $definition; | ||||||
|       self::_ensure_type($definition); |       self::_ensure_type($definition); | ||||||
|       self::_ensure_schema_instances($definition); |       self::_ensure_schema_instances($definition); | ||||||
|     } |     } | ||||||
|     $this->definition = $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 { |   function isAssoc(?AssocSchema &$schema=null): bool { | ||||||
| @ -57,12 +61,29 @@ class AssocSchema extends Schema { | |||||||
|     return true; |     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 { |   protected function newWrapper(): AssocWrapper { | ||||||
|     return new AssocWrapper($this); |     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(); |     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 | <?php | ||||||
| namespace nur\sery\wip\schema\_assoc; | namespace nur\sery\wip\schema\_assoc; | ||||||
| 
 | 
 | ||||||
|  | use nulib\ref\schema\ref_analyze; | ||||||
| use nulib\ValueException; | use nulib\ValueException; | ||||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | use nur\sery\wip\schema\_scalar\ScalarResult; | ||||||
| use nur\sery\wip\schema\_scalar\ScalarWrapper; | 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\Result; | ||||||
| use nur\sery\wip\schema\types\IType; | use nur\sery\wip\schema\types\IType; | ||||||
| use nur\sery\wip\schema\Wrapper; | use nur\sery\wip\schema\Wrapper; | ||||||
|  | use nur\sery\wip\schema\WrapperContext; | ||||||
| 
 | 
 | ||||||
| class AssocWrapper extends Wrapper { | class AssocWrapper extends Wrapper { | ||||||
|   function __construct(AssocSchema $schema, &$array=null, $arrayKey=null, ?array $params=null) { |   function __construct(AssocSchema $schema, &$value=null, $valueKey=null, ?array $params=null) { | ||||||
|     $verifix = $params["verifix"] ?? true; |     $keys = $schema->getKeys(); | ||||||
|     $throw = $params["throw"] ?? null; |     $keyWrappers = []; | ||||||
|     if ($array !== null && $throw === null) { |     foreach ($keys as $key) { | ||||||
|       # Si $value est null, ne pas lancer d'exception, parce qu'on considère que
 |       $keyWrappers[$key] = $schema->getSchema($key)->getWrapper(); | ||||||
|       # c'est une initialisation sans conséquences
 |  | ||||||
|       $throw = true; |  | ||||||
|     } |     } | ||||||
|     $this->schema = $schema; |     $this->context = $context = new AssocWrapperContext($schema, null, null, $params); | ||||||
|     $this->verifix = $verifix; |     $context->arrayWrapper = new ScalarWrapper($schema, $dummy, null, null, $context); | ||||||
|     $this->throw = $throw ?? false; |     $context->keys = $keys; | ||||||
|     $this->result = new AssocResult(); |     $context->keyWrappers = $keyWrappers; | ||||||
|     $this->reset($array, $arrayKey); | 
 | ||||||
|     $this->throw = $throw ?? true; |     # 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; | ||||||
|  |     # Si $value est null, ne pas lancer d'exception, parce qu'on considère que
 | ||||||
|  |     # c'est une initialisation sans conséquences
 | ||||||
|  |     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; } |   function isAssoc(?AssocWrapper &$wrapper=null): bool { $wrapper = $this; return true; } | ||||||
| 
 | 
 | ||||||
|   protected bool $verifix; |   /** @var AssocWrapperContext  */ | ||||||
|  |   protected WrapperContext $context; | ||||||
| 
 | 
 | ||||||
|   protected bool $throw; |   protected function resetContext($resetSelectedKey): void { | ||||||
| 
 |     $context = $this->context; | ||||||
|   /** schéma de ce tableau */ |     $context->arrayWrapper->getResult()->reset(); | ||||||
|   protected AssocSchema $schema; |     foreach ($context->keyWrappers as $wrapper) { | ||||||
| 
 |       $wrapper->getResult()->reset(); | ||||||
|   /** source et destination de la valeur */ |     } | ||||||
|   protected Input $input; |     $context->analyzed = false; | ||||||
| 
 |     $context->normalized = false; | ||||||
|   /** @var string|int|null clé du tableau dans le tableau destination */ |     if ($resetSelectedKey) $context->selectedKey = null; | ||||||
|   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); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function reset(&$array, $arrayKey=null, ?bool $verifix=null): Wrapper { |   function reset(&$value, $valueKey=null, ?array $params=null): Wrapper { | ||||||
|     if ($array instanceof Input) $input = $array; |     $context = $this->context; | ||||||
|     else $input = $this->newInput($array); |     if ($value instanceof Input) $input = $value; | ||||||
|     $this->input = $input; |     else $input = $this->newInput($value); | ||||||
|     $this->arrayKey = $arrayKey; |     $context->input = $input; | ||||||
|     $this->analyze(); |     $context->valueKey = $valueKey; | ||||||
|     if ($verifix ?? $this->verifix) $this->verifix(); |     foreach ($context->keyWrappers as $key => $keyWrapper) { | ||||||
|  |       $keyInput = $input->addKey($valueKey); | ||||||
|  |       $keyWrapper->reset($keyInput, $key, ["analyze" => false]); | ||||||
|  |     } | ||||||
|  |     $this->afterModify($params, true); | ||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function getKeys(): array { |   function getKeys(): array { | ||||||
|     return $this->keys; |     return $this->context->keys; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function select($key=null): ScalarWrapper { |   protected function _getWrapper($key): Wrapper { | ||||||
|     $wrapper = $this->wrappers[$key] ?? null; |     if ($key === null) return $this->context->arrayWrapper; | ||||||
|     if ($key !== null) return $wrapper; |     $wrapper = $context->keyWrappers[$key] ?? null; | ||||||
|     throw ValueException::invalid_key($key); |     if ($wrapper === null) throw ValueException::invalid_key($key); | ||||||
|  |     return $wrapper; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** @param Result[] $results */ |   /** @param string|int|null $key */ | ||||||
|   function verifix(?bool $throw=null, ?array &$results=null): bool { |   function select($key=null): Wrapper { | ||||||
|  |     $wrapper = $this->_getWrapper($key); | ||||||
|  |     $this->context->selectedKey = $key; | ||||||
|  |     return $wrapper; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| 
 |   /** | ||||||
|   function getResult(): AssocResult { |    * @param AssocWrapperContext $context | ||||||
|     return $this->result; |    * @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 { |   function getResult($key=false): Result { | ||||||
|     return $this->arrayType; |     if ($key === false) $key = $this->context->selectedKey; | ||||||
|  |     return $this->_getWrapper($key)->getResult(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function isAvailable(): bool { |   function getType($key=false): IType { | ||||||
|     return $this->result->available; |     if ($key === false) $key = $this->context->selectedKey; | ||||||
|  |     return $this->_getWrapper($key)->getType(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function isValid(): bool { |   function get($default=null, $key=false) { | ||||||
|     return $this->result->valid; |     $context = $this->context; | ||||||
|  |     if (!$context->arrayWrapper->isAvailable()) return $default; | ||||||
|  |     if ($key === false) $key = $context->selectedKey; | ||||||
|  |     return $this->_getWrapper($key)->get($default); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function isNormalized(): bool { |   function set($value, ?array $params=null, $key=false): Wrapper { | ||||||
|     return $this->result->normalized; |     $context = $this->context; | ||||||
|   } |     if ($key === false) $key = $context->selectedKey; | ||||||
| 
 |     $this->_getWrapper($key)->set($value); | ||||||
|   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(); |  | ||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function unset(?bool $verifix=null): AssocWrapper { |   function unset(?array $params=null, $key=false): Wrapper { | ||||||
|     $this->input->unset($this->arrayKey); |     $context = $this->context; | ||||||
|     $this->analyze(); |     if ($key === false) $key = $context->selectedKey; | ||||||
|     if ($verifix ?? $this->verifix) $this->verifix(); |     $this->_getWrapper($key)->unset(); | ||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function format($format = null): string { |   function format($format=null, $key=false): string { | ||||||
|     // TODO: Implement format() method.
 |     $context = $this->context; | ||||||
|   } |     if ($key === false) $key = $context->selectedKey; | ||||||
| 
 |     return $this->_getWrapper($key)->format($format); | ||||||
|   function ensureKeys(): bool { |  | ||||||
|   } |  | ||||||
|   function orderKeys(): bool { |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										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; | namespace nur\sery\wip\schema\_list; | ||||||
| 
 | 
 | ||||||
| use nulib\ref\schema\ref_schema; | use nulib\ref\schema\ref_schema; | ||||||
|  | use nulib\ValueException; | ||||||
| use nur\sery\wip\schema\Schema; | use nur\sery\wip\schema\Schema; | ||||||
| use nur\sery\wip\schema\Wrapper; | use nur\sery\wip\schema\Wrapper; | ||||||
| 
 | 
 | ||||||
| @ -34,7 +35,7 @@ class ListSchema extends Schema { | |||||||
|         "schema" => $definition[0], |         "schema" => $definition[0], | ||||||
|       ]; |       ]; | ||||||
|     } |     } | ||||||
|     self::_normalize($definition, $definitionKey); |     self::_normalize_definition($definition, $definitionKey); | ||||||
|     self::_ensure_nature($definition, "list", "array"); |     self::_ensure_nature($definition, "list", "array"); | ||||||
|     return $definition; |     return $definition; | ||||||
|   } |   } | ||||||
| @ -55,12 +56,26 @@ class ListSchema extends Schema { | |||||||
|     return true; |     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 { |   protected function newWrapper(): ListWrapper { | ||||||
|     return new ListWrapper($this); |     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(); |     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\ref\schema\ref_schema; | ||||||
| use nulib\ValueException; | use nulib\ValueException; | ||||||
| use nur\sery\wip\schema\Result; | use nur\sery\wip\schema\Result; | ||||||
|  | use nur\sery\wip\schema\Schema; | ||||||
|  | use nur\sery\wip\schema\Wrapper; | ||||||
| use Throwable; | use Throwable; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -15,7 +17,7 @@ class ScalarResult extends Result { | |||||||
|   function isScalar(?ScalarResult &$result=null): bool { $result = $this; return true; } |   function isScalar(?ScalarResult &$result=null): bool { $result = $this; return true; } | ||||||
| 
 | 
 | ||||||
|   function getKeys(): array { |   function getKeys(): array { | ||||||
|     return [null]; |     return ScalarSchema::KEYS; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function select($key): Result { |   function select($key): Result { | ||||||
| @ -23,8 +25,7 @@ class ScalarResult extends Result { | |||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** @var array  */ |   protected array $result; | ||||||
|   protected $result; |  | ||||||
|    |    | ||||||
|   function reset(): void { |   function reset(): void { | ||||||
|     $this->result = array_merge( |     $this->result = array_merge( | ||||||
| @ -46,26 +47,13 @@ class ScalarResult extends Result { | |||||||
|     $this->result[$name] = $value; |     $this->result[$name] = $value; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   protected static function replace_key(string &$message, ?string $key): void { |   protected function getMessage(string $key,  Schema $schema): string { | ||||||
|     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 { |  | ||||||
|     $message = cl::get($schema->messages, $key); |     $message = cl::get($schema->messages, $key); | ||||||
|     if ($message !== null) return $message; |     if ($message !== null) return $message; | ||||||
|     return cl::get(ref_schema::MESSAGES, $key); |     return cl::get(ref_schema::MESSAGES, $key); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function setMissing(ScalarSchema $schema): int { |   function setMissing( Schema $schema): int { | ||||||
|     $this->resultAvailable = true; |     $this->resultAvailable = true; | ||||||
|     $this->present = false; |     $this->present = false; | ||||||
|     $this->available = false; |     $this->available = false; | ||||||
| @ -75,15 +63,13 @@ class ScalarResult extends Result { | |||||||
|       $this->normalized = true; |       $this->normalized = true; | ||||||
|       return ref_analyze::NORMALIZED; |       return ref_analyze::NORMALIZED; | ||||||
|     } else { |     } else { | ||||||
|       $messageKey = $this->messageKey = "missing"; |       $this->messageKey = $messageKey = "missing"; | ||||||
|       $message = $this->getMessage($messageKey, $schema); |       $this->message = $this->getMessage($messageKey, $schema); | ||||||
|       self::replace_key($message, $schema->name); |  | ||||||
|       $this->message = $message; |  | ||||||
|       return ref_analyze::MISSING; |       return ref_analyze::MISSING; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function setUnavailable(ScalarSchema $schema): int { |   function setUnavailable( Schema $schema): int { | ||||||
|     $this->resultAvailable = true; |     $this->resultAvailable = true; | ||||||
|     $this->present = true; |     $this->present = true; | ||||||
|     $this->available = false; |     $this->available = false; | ||||||
| @ -93,15 +79,13 @@ class ScalarResult extends Result { | |||||||
|       $this->normalized = true; |       $this->normalized = true; | ||||||
|       return ref_analyze::NORMALIZED; |       return ref_analyze::NORMALIZED; | ||||||
|     } else { |     } else { | ||||||
|       $messageKey = $this->messageKey = "unavailable"; |       $this->messageKey = $messageKey = "unavailable"; | ||||||
|       $message = $this->getMessage($messageKey, $schema); |       $this->message = $this->getMessage($messageKey, $schema); | ||||||
|       self::replace_key($message, $schema->name); |  | ||||||
|       $this->message = $message; |  | ||||||
|       return ref_analyze::UNAVAILABLE; |       return ref_analyze::UNAVAILABLE; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function setNull(ScalarSchema $schema): int { |   function setNull( Schema $schema): int { | ||||||
|     $this->resultAvailable = true; |     $this->resultAvailable = true; | ||||||
|     $this->present = true; |     $this->present = true; | ||||||
|     $this->available = true; |     $this->available = true; | ||||||
| @ -111,33 +95,53 @@ class ScalarResult extends Result { | |||||||
|       $this->normalized = true; |       $this->normalized = true; | ||||||
|       return ref_analyze::NORMALIZED; |       return ref_analyze::NORMALIZED; | ||||||
|     } else { |     } else { | ||||||
|       $messageKey = $this->messageKey = "null"; |       $this->messageKey = $messageKey = "null"; | ||||||
|       $message = $this->getMessage($messageKey, $schema); |       $this->message = $this->getMessage($messageKey, $schema); | ||||||
|       self::replace_key($message, $schema->name); |  | ||||||
|       $this->message = $message; |  | ||||||
|       return ref_analyze::NULL; |       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->resultAvailable = true; | ||||||
|     $this->present = true; |     $this->present = true; | ||||||
|     $this->available = true; |     $this->available = true; | ||||||
|     $this->null = false; |     $this->null = false; | ||||||
|     $this->valid = false; |     $this->valid = false; | ||||||
|     $this->origValue = $value; |     $this->origValue = $value; | ||||||
|     $messageKey = $this->messageKey = "invalid"; |     $this->messageKey = $messageKey = "invalid"; | ||||||
|     $message = $this->getMessage($messageKey, $schema); |     $message = $this->getMessage($messageKey, $schema); | ||||||
|     self::replace_key($message, $schema->name); |     if ($exception !== null) { | ||||||
|     self::replace_orig($message, $schema->orig); |       $tmessage = ValueException::get_message($exception); | ||||||
|     if ($t !== null) { |       if ($tmessage) $message = $tmessage; | ||||||
|       $tmessage = ValueException::get_message($t); |  | ||||||
|       if ($tmessage) $message .= ": $tmessage"; |  | ||||||
|     } |     } | ||||||
|     $this->message = $message; |     $this->message = $message; | ||||||
|  |     $this->exception = $exception; | ||||||
|     return ref_analyze::INVALID; |     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 { |   function setValid($normalizedValue=null): int { | ||||||
|     $this->resultAvailable = true; |     $this->resultAvailable = true; | ||||||
|     $this->present = true; |     $this->present = true; | ||||||
| @ -159,6 +163,10 @@ class ScalarResult extends Result { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function throw(bool $throw): void { |   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 | <?php | ||||||
| namespace nur\sery\wip\schema\_scalar; | namespace nur\sery\wip\schema\_scalar; | ||||||
| 
 | 
 | ||||||
|  | use nulib\cl; | ||||||
| use nulib\ref\schema\ref_schema; | use nulib\ref\schema\ref_schema; | ||||||
|  | use nulib\ValueException; | ||||||
| use nur\sery\wip\schema\Schema; | use nur\sery\wip\schema\Schema; | ||||||
| use nur\sery\wip\schema\types\IType; |  | ||||||
| use nur\sery\wip\schema\Wrapper; | use nur\sery\wip\schema\Wrapper; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Class ScalarSchema |  * 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 { | 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 |    * 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 { |   static function isa_definition($definition): bool { | ||||||
|     # chaine ou null
 |     # chaine ou null
 | ||||||
| @ -64,8 +42,9 @@ class ScalarSchema extends Schema { | |||||||
|     return $haveIndex0 && $count > 1; |     return $haveIndex0 && $count > 1; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static function normalize($definition, $definitionKey=null): array { |   static function normalize_definition($definition, $definitionKey=null): array { | ||||||
|     self::_normalize($definition, $definitionKey); |     $natureMetaschema = array_merge(ref_schema::NATURE_METASCHEMA, ref_schema::SCALAR_NATURE_METASCHEMA); | ||||||
|  |     self::_normalize_definition($definition, $definitionKey, $natureMetaschema); | ||||||
|     self::_ensure_nature($definition, "scalar"); |     self::_ensure_nature($definition, "scalar"); | ||||||
|     return $definition; |     return $definition; | ||||||
|   } |   } | ||||||
| @ -73,7 +52,7 @@ class ScalarSchema extends Schema { | |||||||
|   function __construct($definition=null, $definitionKey=null, bool $normalize=true) { |   function __construct($definition=null, $definitionKey=null, bool $normalize=true) { | ||||||
|     if ($definition === null) $definition = static::SCHEMA; |     if ($definition === null) $definition = static::SCHEMA; | ||||||
|     if ($normalize) { |     if ($normalize) { | ||||||
|       $definition = self::normalize($definition, $definitionKey); |       $definition = self::normalize_definition($definition, $definitionKey); | ||||||
|       $this->_definition = $definition; |       $this->_definition = $definition; | ||||||
|       self::_ensure_type($definition); |       self::_ensure_type($definition); | ||||||
|       self::_ensure_schema_instances($definition); |       self::_ensure_schema_instances($definition); | ||||||
| @ -86,27 +65,27 @@ class ScalarSchema extends Schema { | |||||||
|     return true; |     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 { |   protected function newWrapper(): ScalarWrapper { | ||||||
|     return new ScalarWrapper($this); |     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
 |     # si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception
 | ||||||
|     # cf le code similaire dans ScalarWrapper::__construct()
 |     # cf le code similaire dans ScalarWrapper::__construct()
 | ||||||
|     $verifix = $value !== null || $wrapper !== null; |     $dontAnalyze = $value === null && $wrapper === null; | ||||||
|     if (!($wrapper instanceof ScalarWrapper)) $wrapper = $this->newWrapper(); |     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\php\func; | ||||||
| use nulib\ref\schema\ref_analyze; | use nulib\ref\schema\ref_analyze; | ||||||
| use nulib\ValueException; | use nulib\ValueException; | ||||||
| use nur\sery\wip\schema\WrapperContext; | use nur\sery\wip\schema\_assoc\AssocWrapper; | ||||||
| use nur\sery\wip\schema\input\Input; | 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; | ||||||
| use nur\sery\wip\schema\types\IType; | use nur\sery\wip\schema\types\IType; | ||||||
| use nur\sery\wip\schema\Wrapper; | 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 { | class ScalarWrapper extends Wrapper { | ||||||
|   function __construct(ScalarSchema $schema, &$value=null, $valueKey=null, ?array $params=null) { |   function __construct(Schema $schema, &$value=null, $valueKey=null, ?array $params=null, ?WrapperContext $context=null) { | ||||||
|     $verifix = $params["verifix"] ?? true; |     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; |     $throw = $params["throw"] ?? null; | ||||||
|     if ($value !== null && $throw === null) { |     # Si $value est null, ne pas lancer d'exception, parce qu'on considère que
 | ||||||
|       # Si $value est null, ne pas lancer d'exception, parce qu'on considère que
 |     # c'est une initialisation sans conséquences
 | ||||||
|       # c'est une initialisation sans conséquences
 |     if ($throw === null && $value !== null) $throw = true; | ||||||
|       $throw = true; |     $context->throw = $throw ?? false; | ||||||
|     } |  | ||||||
|     $this->verifix = $verifix; |  | ||||||
|     $this->throw = $throw ?? false; |  | ||||||
|     $this->schema = $schema; |  | ||||||
|     $this->result = new ScalarResult(); |  | ||||||
|     $this->reset($value, $valueKey); |     $this->reset($value, $valueKey); | ||||||
|     $this->throw = $throw ?? true; |     $context->throw = $throw ?? true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function isScalar(?ScalarWrapper &$wrapper=null): bool { $wrapper = $this; return true; } |   function isScalar(?ScalarWrapper &$wrapper=null): bool { $wrapper = $this; return true; } | ||||||
| 
 | 
 | ||||||
|   protected bool $verifix; |   protected WrapperContext $context; | ||||||
| 
 |  | ||||||
|   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; |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   function getKeys(): array { |   function getKeys(): array { | ||||||
|     return [null]; |     return ScalarSchema::KEYS; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** @param string|int|null $key */ |   /** @param string|int|null $key */ | ||||||
| @ -74,7 +53,7 @@ class ScalarWrapper extends Wrapper { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** analyser la valeur et résoudre son type */ |   /** analyser la valeur et résoudre son type */ | ||||||
|   protected function analyze0(WrapperContext $context): int { |   protected static function _analyze0(WrapperContext $context): int { | ||||||
|     /** @var ScalarSchema $schema */ |     /** @var ScalarSchema $schema */ | ||||||
|     $schema = $context->schema; |     $schema = $context->schema; | ||||||
|     $input = $context->input; |     $input = $context->input; | ||||||
| @ -112,7 +91,7 @@ class ScalarWrapper extends Wrapper { | |||||||
|           $args = $name; |           $args = $name; | ||||||
|           $name = $key; |           $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; |         if ($firstType === null) $firstType = $type; | ||||||
|         $types[] = $type; |         $types[] = $type; | ||||||
|         if ($type->isAvailable($input, $valueKey)) { |         if ($type->isAvailable($input, $valueKey)) { | ||||||
| @ -140,7 +119,7 @@ class ScalarWrapper extends Wrapper { | |||||||
|         $type = $firstType; |         $type = $firstType; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     $context->type = $this->type = $type; |     $context->type = $type; | ||||||
| 
 | 
 | ||||||
|     if (!$type->isAvailable($input, $valueKey)) { |     if (!$type->isAvailable($input, $valueKey)) { | ||||||
|       if ($default !== null) { |       if ($default !== null) { | ||||||
| @ -165,25 +144,28 @@ class ScalarWrapper extends Wrapper { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   protected function analyze(): int { |   /** | ||||||
|     $schema = $this->schema; |    * @param ScalarWrapper $wrapper | ||||||
|     $input = $this->input; |    */ | ||||||
|     $valueKey = $this->valueKey; |   static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int { | ||||||
|     $result = $this->result; |     /** @var ScalarSchema $schema */ | ||||||
|     $result->reset(); |     $schema = $context->schema; | ||||||
|     $context = new WrapperContext($schema, $this, $input, $valueKey, $result); |     $input = $context->input; | ||||||
|  |     $valueKey = $context->valueKey; | ||||||
|  |     /** @var ScalarResult $result */ | ||||||
|  |     $result = $context->result; | ||||||
| 
 | 
 | ||||||
|     /** @var func $analyzerFunc */ |     /** @var func $analyzerFunc */ | ||||||
|     $analyzerFunc = $schema->analyzerFunc; |     $analyzerFunc = $schema->analyzerFunc; | ||||||
|     if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context]); |     if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context, $wrapper]); | ||||||
|     else $what = $this->analyze0($context); |     else $what = self::_analyze0($context); | ||||||
|     if ($what !== ref_analyze::STRING) return $what; |     if ($what !== ref_analyze::STRING) return $what; | ||||||
| 
 | 
 | ||||||
|     $value = $context->value; |     $value = $context->value; | ||||||
|     try { |     try { | ||||||
|       /** @var func $extractorFunc */ |       /** @var func $extractorFunc */ | ||||||
|       $extractorFunc = $schema->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); |       else $extracted = $context->type->extract($value); | ||||||
|       $context->value = $extracted; |       $context->value = $extracted; | ||||||
|     } catch (ValueException $e) { |     } catch (ValueException $e) { | ||||||
| @ -194,7 +176,7 @@ class ScalarWrapper extends Wrapper { | |||||||
|     try { |     try { | ||||||
|       /** @var func $parserFunc */ |       /** @var func $parserFunc */ | ||||||
|       $parserFunc = $schema->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); |       else $parsed = $context->type->parse($extracted); | ||||||
|       $context->value = $parsed; |       $context->value = $parsed; | ||||||
|     } catch (ValueException $e) { |     } catch (ValueException $e) { | ||||||
| @ -211,107 +193,53 @@ class ScalarWrapper extends Wrapper { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function verifix(?bool $throw=null): bool { |   /** | ||||||
|     $result = $this->result; |    * @param ScalarWrapper $wrapper | ||||||
|     $valueKey = $this->valueKey; |    */ | ||||||
|     $verifix = false; |   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; |     $modified = false; | ||||||
|     if ($result->resultAvailable) { |     if ($result->resultAvailable) { | ||||||
|       if ($result->null) { |       if ($result->null) { | ||||||
|         # forcer la valeur null, parce que la valeur actuelle est peut-être une
 |         # forcer la valeur null, parce que la valeur actuelle est peut-être une
 | ||||||
|         # valeur assimilée à null
 |         # valeur assimilée à null
 | ||||||
|         $this->input->set(null, $valueKey); |         $input->set(null, $valueKey); | ||||||
|       } elseif ($result->valid && !$result->normalized) { |       } elseif ($result->valid && !$result->normalized) { | ||||||
|         $normalizedValue = $result->normalizedValue; |         $normalizedValue = $result->normalizedValue; | ||||||
|         if ($normalizedValue !== null) { |         if ($normalizedValue !== null) { | ||||||
|           # la valeur normalisée est disponible
 |           # la valeur normalisée est disponible
 | ||||||
|           $this->input->set($normalizedValue); |           $input->set($normalizedValue, $valueKey); | ||||||
|           $result->normalizedValue = null; |           $result->normalizedValue = null; | ||||||
|           $modified = true; |           $modified = true; | ||||||
|         } else { |         } else { | ||||||
|           # normaliser la valeur
 |           # normaliser la valeur
 | ||||||
|           $verifix = true; |           $normalize = true; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       $verifix = true; |       $normalize = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ($verifix) { |     if ($normalize) { | ||||||
|       $value = $this->input->get($valueKey); |       $value = $input->get($valueKey); | ||||||
|       $schema = $this->schema; |  | ||||||
|       /** @var func $normalizerFunc */ |       /** @var func $normalizerFunc */ | ||||||
|       $normalizerFunc = $schema->normalizerFunc; |       $normalizerFunc = $schema->normalizerFunc; | ||||||
|       if ($normalizerFunc !== null) { |       if ($normalizerFunc !== null) { | ||||||
|         $context = new WrapperContext($schema, $this, $this->input, $valueKey, $result); |  | ||||||
|         $orig = $value; |         $orig = $value; | ||||||
|         $value = $normalizerFunc->invoke([$orig, $context]); |         $value = $normalizerFunc->invoke([$orig, $context, $wrapper]); | ||||||
|         $modified = $value !== $orig; |         $modified = $value !== $orig; | ||||||
|       } else { |       } 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; |     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
 | # construire des querystring et paramètres de formulaires
 | ||||||
| use nur\sery\wip\php\access\FormAccess; | use nur\sery\wip\php\access\FormAccess; | ||||||
| use nur\sery\wip\php\access\IAccess; | use nur\sery\wip\php\access\IAccess; | ||||||
| use nur\sery\wip\php\access\KeyAccess; |  | ||||||
| use nur\sery\wip\php\access\ShadowAccess; | use nur\sery\wip\php\access\ShadowAccess; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -18,15 +17,14 @@ use nur\sery\wip\php\access\ShadowAccess; | |||||||
| class FormInput extends Input { | class FormInput extends Input { | ||||||
|   const ALLOW_EMPTY = false; |   const ALLOW_EMPTY = false; | ||||||
| 
 | 
 | ||||||
|   protected function formAccess($key): IAccess { |   function __construct(&$dest=null, ?array $params=null) { | ||||||
|     return new FormAccess($key, [ |     parent::__construct($dest, $params); | ||||||
|       "allow_empty" => $this->allowEmpty, |     $this->access = new ShadowAccess($this->formAccess($this->access), $this->access); | ||||||
|     ]); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   protected function access($key): IAccess { |   protected function formAccess(IAccess $access): IAccess { | ||||||
|     return $this->keyAccess[$key] ??= new ShadowAccess($this->formAccess($key), new KeyAccess($this->value, $key, [ |     return new FormAccess(null, [ | ||||||
|       "allow_empty" => $this->allowEmpty, |       "allow_empty" => $access->isAllowEmpty(), | ||||||
|     ])); |     ]); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -11,9 +11,9 @@ use nur\sery\wip\php\access\IAccess; | |||||||
|  * une référence |  * une référence | ||||||
|  */ |  */ | ||||||
| class GetInput extends FormInput { | class GetInput extends FormInput { | ||||||
|   protected function formAccess($key): IAccess { |   protected function formAccess(IAccess $access): IAccess { | ||||||
|     return new GetAccess($key, [ |     return new GetAccess(null, [ | ||||||
|       "allow_empty" => $this->allowEmpty, |       "allow_empty" => $access->isAllowEmpty(), | ||||||
|     ]); |     ]); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,10 @@ | |||||||
| <?php | <?php | ||||||
| namespace nur\sery\wip\schema\input; | namespace nur\sery\wip\schema\input; | ||||||
| 
 | 
 | ||||||
|  | use nulib\StateException; | ||||||
| use nur\sery\wip\php\access\IAccess; | use nur\sery\wip\php\access\IAccess; | ||||||
| use nur\sery\wip\php\access\KeyAccess; | 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 |  * Class Input: accès à une valeur | ||||||
| @ -13,54 +14,62 @@ use nur\sery\wip\php\access\ValueAccess; | |||||||
| class Input { | class Input { | ||||||
|   const ALLOW_EMPTY = true; |   const ALLOW_EMPTY = true; | ||||||
| 
 | 
 | ||||||
|   function __construct(&$value=null, ?array $params=null) { |   const ACCESS_AUTO = 0, ACCESS_KEY = 1, ACCESS_PROPERTY = 2; | ||||||
|     $this->value =& $value; | 
 | ||||||
|     $this->allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY; |   private static function unexpected_access_type(): StateException { | ||||||
|  |     return StateException::unexpected_state("access_type"); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** @var mixed */ |   function __construct(&$dest=null, ?array $params=null) { | ||||||
|   protected $value; |     $accessType = $params["access_type"] ?? self::ACCESS_AUTO; | ||||||
|  |     if ($accessType === self::ACCESS_AUTO) { | ||||||
|  |       $accessType = is_object($dest)? self::ACCESS_PROPERTY: self::ACCESS_KEY; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|   /** |     $allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY; | ||||||
|    * @var bool comment considérer une chaine vide: "" si allowEmpty, null sinon |     if ($accessType == self::ACCESS_PROPERTY) { | ||||||
|    */ |       $this->access = new PropertyAccess($dest, null, [ | ||||||
|   protected $allowEmpty; |         "allow_empty" => $allowEmpty, | ||||||
| 
 |         "allow_null" => true, | ||||||
|   protected ?ValueAccess $valueAccess = null; |       ]); | ||||||
|   protected ?array $keyAccess = null; |     } elseif ($accessType == self::ACCESS_KEY) { | ||||||
| 
 |       $this->access = new KeyAccess($dest, null, [ | ||||||
|   protected function access($key): IAccess { |         "allow_empty" => $allowEmpty, | ||||||
|     if ($key === null) { |  | ||||||
|       return $this->valueAccess ??= new ValueAccess($this->value, [ |  | ||||||
|         "allow_null" => true, |         "allow_null" => true, | ||||||
|         "allow_empty" => $this->allowEmpty, |  | ||||||
|       ]); |       ]); | ||||||
|     } else { |     } else { | ||||||
|       return $this->keyAccess[$key] ??= new KeyAccess($this->value, $key, [ |       throw self::unexpected_access_type(); | ||||||
|         "allow_empty" => $this->allowEmpty, |  | ||||||
|       ]); |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   protected IAccess $access; | ||||||
|  | 
 | ||||||
|   /** tester si la valeur existe sans tenir compte de $allowEmpty */ |   /** tester si la valeur existe sans tenir compte de $allowEmpty */ | ||||||
|   function isPresent($key=null): bool { |   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 */ |   /** tester si la valeur est disponible en tenant compte de $allowEmpty */ | ||||||
|   function isAvailable($key=null): bool { |   function isAvailable($key=null): bool { | ||||||
|     return $this->access($key)->available(); |     return $this->access->resetKey($key)->available(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function get($key=null) { |   function get($key=null) { | ||||||
|     return $this->access($key)->get(); |     return $this->access->resetKey($key)->get(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function set($value, $key=null): void { |   function set($value, $key=null): void { | ||||||
|     $this->access($key)->set($value); |     $this->access->resetKey($key)->set($value); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function unset($key=null): void { |   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 |  * une référence | ||||||
|  */ |  */ | ||||||
| class PostInput extends FormInput { | class PostInput extends FormInput { | ||||||
|   protected function formAccess($key): IAccess { |   protected function formAccess(IAccess $access): IAccess { | ||||||
|     return new PostAccess($key, [ |     return new PostAccess(null, [ | ||||||
|       "allow_empty" => $this->allowEmpty, |       "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 |    * @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 |    * Les valeurs "mixed", "bool", "float", "int", "string" et "array" peuvent | ||||||
|    * aussi être retournées, bien qu'elles ne soient pas à proprement parler des |    * aussi être retournées, bien qu'elles ne soient pas à proprement parler des | ||||||
| @ -98,15 +98,18 @@ interface IType { | |||||||
|   function parse(string $value); |   function parse(string $value); | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * analyser, corriger éventuellement et normaliser la valeur |    * 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 | ||||||
|    * NB: si $value est un string. elle doit avoir déjà été traitée au préalable |  | ||||||
|    * par extract() et parse() |    * par extract() et parse() | ||||||
|    * |    * | ||||||
|    * si la valeur était déjà normalisée, ou si une erreur s'est produite, |    * - si $result indique que la valeur est déjà normalisée, cette méthode ne | ||||||
|    * retourner false. |    * 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 |    * 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 { |   function isValid($value, ?bool &$normalized=null): bool { | ||||||
|     $normalized = is_array($value); |     $normalized = is_array($value); | ||||||
|     return is_scalar($value) || is_array($value); |     return $normalized || is_scalar($value); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function parse(string $value) { |   function parse(string $value) { | ||||||
| @ -49,23 +49,14 @@ class tarray extends _tstring { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     if (is_array($value)) { |     if ($result->normalized) { | ||||||
|  |     } elseif (is_array($value)) { | ||||||
|       $result->setNormalized(); |       $result->setNormalized(); | ||||||
|       return false; |  | ||||||
|     } elseif (is_string($value)) { |  | ||||||
|       try { |  | ||||||
|         $value = $this->parse($value); |  | ||||||
|         $result->setValid(); |  | ||||||
|         return true; |  | ||||||
|       } catch (ValueException $e) { |  | ||||||
|       } |  | ||||||
|     } elseif (is_scalar($value)) { |     } elseif (is_scalar($value)) { | ||||||
|       $value = cl::with($value); |       $value = cl::with($value); | ||||||
|       $result->setValid(); |  | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|     $result->setInvalid($value, $schema); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -99,23 +99,14 @@ class tbool extends _tformatable { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     if (is_bool($value)) { |     if ($result->normalized) { | ||||||
|  |     } elseif (is_bool($value)) { | ||||||
|       $result->setNormalized(); |       $result->setNormalized(); | ||||||
|       return false; |  | ||||||
|     } elseif (is_string($value)) { |  | ||||||
|       try { |  | ||||||
|         $value = $this->parse($value); |  | ||||||
|         $result->setValid(); |  | ||||||
|         return true; |  | ||||||
|       } catch (ValueException $e) { |  | ||||||
|       } |  | ||||||
|     } elseif (is_scalar($value)) { |     } elseif (is_scalar($value)) { | ||||||
|       $value = boolval($value); |       $value = boolval($value); | ||||||
|       $result->setValid(); |  | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|     $result->setInvalid($value, $schema); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -27,7 +27,7 @@ class tcallable extends _tsimple { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function isValid($value, ?bool &$normalized=null): bool { |   function isValid($value, ?bool &$normalized=null): bool { | ||||||
|     $normalized = is_callable($value); |     $normalized = $value instanceof func; | ||||||
|     return func::check($value); |     return func::check($value); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -43,23 +43,14 @@ class tcallable extends _tsimple { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     if ($value instanceof func) { |     if ($result->normalized) { | ||||||
|  |     } elseif ($value instanceof func) { | ||||||
|       $result->setNormalized(); |       $result->setNormalized(); | ||||||
|       return false; |  | ||||||
|     } elseif (is_callable($value)) { |     } elseif (is_callable($value)) { | ||||||
|       $value = func::with($value); |       $value = func::with($value); | ||||||
|       $result->setNormalized(); |  | ||||||
|       return true; |       return true; | ||||||
|     } elseif (is_string($value)) { |  | ||||||
|       try { |  | ||||||
|         $value = $this->parse($value); |  | ||||||
|         $result->setValid(); |  | ||||||
|         return true; |  | ||||||
|       } catch (ValueException $e) { |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|     $result->setInvalid($value, $schema); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ abstract class tcontent extends _tunion { | |||||||
| 
 | 
 | ||||||
|   function isValid($value, ?bool &$normalized=null): bool { |   function isValid($value, ?bool &$normalized=null): bool { | ||||||
|     $normalized = is_string($value) || is_array($value); |     $normalized = is_string($value) || is_array($value); | ||||||
|     return is_scalar($value) || is_array($value); |     return $normalized || is_scalar($value); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function parse(string $value) { |   function parse(string $value) { | ||||||
| @ -36,18 +36,15 @@ abstract class tcontent extends _tunion { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     if (is_string($value) || is_array($value)) { |     if ($result->normalized) { | ||||||
|  |     } elseif (is_string($value) || is_array($value)) { | ||||||
|       $result->setNormalized(); |       $result->setNormalized(); | ||||||
|       return false; |  | ||||||
|     } elseif (is_scalar($value)) { |     } elseif (is_scalar($value)) { | ||||||
|       $value = strval($value); |       $value = strval($value); | ||||||
|       $result->setValid(); |  | ||||||
|       return true; |       return true; | ||||||
|     } else { |  | ||||||
|       $result->setInvalid($value, $schema); |  | ||||||
|       return false; |  | ||||||
|     } |     } | ||||||
|  |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function format($value, $format=null): string { |   function format($value, $format=null): string { | ||||||
|  | |||||||
| @ -43,23 +43,14 @@ class tfloat extends _tformatable { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     if (is_float($value)) { |     if ($result->normalized) { | ||||||
|  |     } elseif (is_float($value)) { | ||||||
|       $result->setNormalized(); |       $result->setNormalized(); | ||||||
|       return false; |  | ||||||
|     } elseif (is_string($value)) { |  | ||||||
|       try { |  | ||||||
|         $value = $this->parse($value); |  | ||||||
|         $result->setValid(); |  | ||||||
|         return true; |  | ||||||
|       } catch (ValueException $e) { |  | ||||||
|       } |  | ||||||
|     } elseif (is_scalar($value)) { |     } elseif (is_scalar($value)) { | ||||||
|       $value = floatval($value); |       $value = floatval($value); | ||||||
|       $result->setValid(); |  | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|     $result->setInvalid($value, $schema); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -29,8 +29,8 @@ class tgeneric extends _tsimple { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function isValid($value, ?bool &$normalized=null): bool { |   function isValid($value, ?bool &$normalized=null): bool { | ||||||
|     $normalized = true; |     $normalized = $value instanceof $this->class; | ||||||
|     return $value instanceof $this->class; |     return $normalized; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function parse(string $value) { |   function parse(string $value) { | ||||||
| @ -41,8 +41,8 @@ class tgeneric extends _tsimple { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     $result->setNormalized(); |     if (!$result->normalized) $result->setNormalized(); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -45,23 +45,14 @@ class tint extends _tformatable { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     if (is_int($value)) { |     if ($result->normalized) { | ||||||
|  |     } elseif (is_int($value)) { | ||||||
|       $result->setNormalized(); |       $result->setNormalized(); | ||||||
|       return false; |  | ||||||
|     } elseif (is_string($value)) { |  | ||||||
|       try { |  | ||||||
|         $value = $this->parse($value); |  | ||||||
|         $result->setValid(); |  | ||||||
|         return true; |  | ||||||
|       } catch (ValueException $e) { |  | ||||||
|       } |  | ||||||
|     } elseif (is_scalar($value)) { |     } elseif (is_scalar($value)) { | ||||||
|       $value = intval($value); |       $value = intval($value); | ||||||
|       $result->setValid(); |  | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|     $result->setInvalid($value, $schema); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ class tkey extends _tunion { | |||||||
| 
 | 
 | ||||||
|   function isValid($value, ?bool &$normalized=null): bool { |   function isValid($value, ?bool &$normalized=null): bool { | ||||||
|     $normalized = is_string($value) || is_int($value); |     $normalized = is_string($value) || is_int($value); | ||||||
|     return is_scalar($value); |     return $normalized || is_scalar($value); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function parse(string $value) { |   function parse(string $value) { | ||||||
| @ -36,18 +36,15 @@ class tkey extends _tunion { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     if (is_string($value) || is_int($value)) { |     if ($result->normalized) { | ||||||
|  |     } elseif (is_string($value) || is_int($value)) { | ||||||
|       $result->setNormalized(); |       $result->setNormalized(); | ||||||
|       return false; |  | ||||||
|     } elseif (is_scalar($value)) { |     } elseif (is_scalar($value)) { | ||||||
|       $value = strval($value); |       $value = strval($value); | ||||||
|       $result->setValid(); |  | ||||||
|       return true; |       return true; | ||||||
|     } else { |  | ||||||
|       $result->setInvalid($value, $schema); |  | ||||||
|       return false; |  | ||||||
|     } |     } | ||||||
|  |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function format($value, $format=null): string { |   function format($value, $format=null): string { | ||||||
|  | |||||||
| @ -35,8 +35,8 @@ class tmixed extends _tsimple { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     $result->setNormalized(); |     if (!$result->normalized) $result->setNormalized(); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -30,7 +30,7 @@ class tpkey extends _tunion { | |||||||
| 
 | 
 | ||||||
|   function isValid($value, ?bool &$normalized=null): bool { |   function isValid($value, ?bool &$normalized=null): bool { | ||||||
|     $normalized = is_string($value) || is_int($value) || is_array($value); |     $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) { |   function parse(string $value) { | ||||||
| @ -41,18 +41,15 @@ class tpkey extends _tunion { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     if (is_string($value) || is_int($value) || is_array($value)) { |     if ($result->normalized) { | ||||||
|  |     } elseif (is_string($value) || is_int($value) || is_array($value)) { | ||||||
|       $result->setNormalized(); |       $result->setNormalized(); | ||||||
|       return false; |  | ||||||
|     } elseif (is_scalar($value)) { |     } elseif (is_scalar($value)) { | ||||||
|       $value = strval($value); |       $value = strval($value); | ||||||
|       $result->setValid(); |  | ||||||
|       return true; |       return true; | ||||||
|     } else { |  | ||||||
|       $result->setInvalid($value, $schema); |  | ||||||
|       return false; |  | ||||||
|     } |     } | ||||||
|  |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function format($value, $format=null): string { |   function format($value, $format=null): string { | ||||||
|  | |||||||
| @ -44,18 +44,15 @@ class trawstring extends _tstring { | |||||||
|    * @var ScalarResult $result |    * @var ScalarResult $result | ||||||
|    * @var ScalarSchema $schema |    * @var ScalarSchema $schema | ||||||
|    */ |    */ | ||||||
|   function verifix(&$value, Result $result, Schema $schema): bool { |   function normalize(&$value, Result $result, Schema $schema): bool { | ||||||
|     if (is_string($value)) { |     if ($result->normalized) { | ||||||
|  |     } elseif (is_string($value)) { | ||||||
|       $result->setNormalized(); |       $result->setNormalized(); | ||||||
|       return false; |  | ||||||
|     } elseif (is_scalar($value)) { |     } elseif (is_scalar($value)) { | ||||||
|       $value = strval($value); |       $value = strval($value); | ||||||
|       $result->setValid(); |  | ||||||
|       return true; |       return true; | ||||||
|     } else { |  | ||||||
|       $result->setInvalid($value, $schema); |  | ||||||
|       return false; |  | ||||||
|     } |     } | ||||||
|  |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function format($value, $format=null): string { |   function format($value, $format=null): string { | ||||||
|  | |||||||
| @ -5,7 +5,69 @@ use nulib\tests\TestCase; | |||||||
| use stdClass; | use stdClass; | ||||||
| 
 | 
 | ||||||
| class KeyAccessTest extends TestCase { | 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(); |     $default = new stdClass(); | ||||||
|     $array = ["null" => null, "false" => false, "empty" => ""]; |     $array = ["null" => null, "false" => false, "empty" => ""]; | ||||||
| 
 | 
 | ||||||
| @ -63,4 +125,91 @@ class KeyAccessTest extends TestCase { | |||||||
|     self::assertTrue($a->available()); |     self::assertTrue($a->available()); | ||||||
|     self::assertSame("", $a->get($default)); |     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 { | class AssocSchemaTest extends TestCase { | ||||||
|   const NULL_SCHEMA = [ |   const NULL_SCHEMA = [ | ||||||
|     "" => ["assoc"], |     "" => [ | ||||||
|  |       "assoc", | ||||||
|  |       "compute_func" => null, | ||||||
|  |       "validate_func" => null, | ||||||
|  |       "ensure_array" => false, | ||||||
|  |       "ensure_keys" => true, | ||||||
|  |       "ensure_order" => true, | ||||||
|  |     ], | ||||||
|     "schema" => null, |     "schema" => null, | ||||||
|     "type" => [null], |     "type" => [null], | ||||||
|     "default" => null, |     "default" => null, | ||||||
| @ -44,7 +51,7 @@ class AssocSchemaTest extends TestCase { | |||||||
|         "type" => ["string"], "nullable" => false, |         "type" => ["string"], "nullable" => false, | ||||||
|         "name" => "a", "pkey" => "a", "header" => "a", |         "name" => "a", "pkey" => "a", "header" => "a", | ||||||
|       ], |       ], | ||||||
|     ]), AssocSchema::normalize(["a" => "string"])); |     ]), AssocSchema::normalize_definition(["a" => "string"])); | ||||||
| 
 | 
 | ||||||
|     self::assertSame(self::schema([ |     self::assertSame(self::schema([ | ||||||
|       "type" => ["array"], "nullable" => true, |       "type" => ["array"], "nullable" => true, | ||||||
| @ -61,7 +68,7 @@ class AssocSchemaTest extends TestCase { | |||||||
|         "type" => ["bool"], "nullable" => false, |         "type" => ["bool"], "nullable" => false, | ||||||
|         "name" => "c", "pkey" => "c", "header" => "c", |         "name" => "c", "pkey" => "c", "header" => "c", | ||||||
|       ], |       ], | ||||||
|     ]), AssocSchema::normalize([ |     ]), AssocSchema::normalize_definition([ | ||||||
|       "a" => "string", |       "a" => "string", | ||||||
|       "b" => "int", |       "b" => "int", | ||||||
|       "c" => "bool", |       "c" => "bool", | ||||||
| @ -90,6 +97,72 @@ class AssocSchemaTest extends TestCase { | |||||||
|         "name" => "c", "pkey" => "c", "header" => "c", |         "name" => "c", "pkey" => "c", "header" => "c", | ||||||
|       ], |       ], | ||||||
|     ]), $schema->getDefinition()); |     ]), $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, |     "messages" => null, | ||||||
|     "formatter_func" => null, |     "formatter_func" => null, | ||||||
|     "format" => null, |     "format" => null, | ||||||
|     "" => ["scalar"], |     "" => [ | ||||||
|  |       "scalar", | ||||||
|  |       "compute_func" => null, | ||||||
|  |       "validate_func" => null, | ||||||
|  |     ], | ||||||
|     "schema" => null, |     "schema" => null, | ||||||
|     "name" => null, |     "name" => null, | ||||||
|     "pkey" => null, |     "pkey" => null, | ||||||
| @ -32,33 +36,33 @@ class ScalarSchemaTest extends TestCase { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function testNormalize() { |   function testNormalize() { | ||||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize(null)); |     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition(null)); | ||||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([])); |     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition([])); | ||||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize([null])); |     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition([null])); | ||||||
|     self::assertException(SchemaException::class, function () { |     self::assertException(SchemaException::class, function () { | ||||||
|       ScalarSchema::normalize([[]]); |       ScalarSchema::normalize_definition([[]]); | ||||||
|     }); |     }); | ||||||
|     self::assertException(SchemaException::class, function () { |     self::assertException(SchemaException::class, function () { | ||||||
|       ScalarSchema::normalize([[null]]); |       ScalarSchema::normalize_definition([[null]]); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     $string = self::schema(["type" => ["string"], "nullable" => false]); |     $string = self::schema(["type" => ["string"], "nullable" => false]); | ||||||
|     self::assertSame($string, ScalarSchema::normalize("string")); |     self::assertSame($string, ScalarSchema::normalize_definition("string")); | ||||||
|     self::assertSame($string, ScalarSchema::normalize(["string"])); |     self::assertSame($string, ScalarSchema::normalize_definition(["string"])); | ||||||
| 
 | 
 | ||||||
|     $nstring = self::schema(["type" => ["string"]]); |     $nstring = self::schema(["type" => ["string"]]); | ||||||
|     self::assertSame($nstring, ScalarSchema::normalize(["?string"])); |     self::assertSame($nstring, ScalarSchema::normalize_definition(["?string"])); | ||||||
|     self::assertSame($nstring, ScalarSchema::normalize(["?string|null"])); |     self::assertSame($nstring, ScalarSchema::normalize_definition(["?string|null"])); | ||||||
|     self::assertSame($nstring, ScalarSchema::normalize(["string|null"])); |     self::assertSame($nstring, ScalarSchema::normalize_definition(["string|null"])); | ||||||
|     self::assertSame($nstring, ScalarSchema::normalize([["?string", "null"]])); |     self::assertSame($nstring, ScalarSchema::normalize_definition([["?string", "null"]])); | ||||||
|     self::assertSame($nstring, ScalarSchema::normalize([["string", "null"]])); |     self::assertSame($nstring, ScalarSchema::normalize_definition([["string", "null"]])); | ||||||
|     self::assertSame($nstring, ScalarSchema::normalize([["string", null]])); |     self::assertSame($nstring, ScalarSchema::normalize_definition([["string", null]])); | ||||||
| 
 | 
 | ||||||
|     $key = self::schema(["type" => ["string", "int"], "nullable" => false]); |     $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]); |     $nkey = self::schema(["type" => ["string", "int"], "nullable" => true]); | ||||||
|     self::assertSame($nkey, ScalarSchema::normalize("?string|int")); |     self::assertSame($nkey, ScalarSchema::normalize_definition("?string|int")); | ||||||
|     self::assertSame($nkey, ScalarSchema::normalize("string|?int")); |     self::assertSame($nkey, ScalarSchema::normalize_definition("string|?int")); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,19 +15,21 @@ class ScalarWrapperTest extends TestCase { | |||||||
|     self::assertSame($normalized, $wrapper->isNormalized(), "normalized"); |     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 = $schema->getWrapper(); | ||||||
|  |     $wrapper->resetParams(["normalize" => $normalize]); | ||||||
|     if ($inputParams !== null) $input = new Input($orig, $inputParams); |     if ($inputParams !== null) $input = new Input($orig, $inputParams); | ||||||
|     else $input = $orig; |     else $input = $orig; | ||||||
|     $wrapper->reset($input, null, $verifix); |     $wrapper->reset($input); | ||||||
|     $this->checkValue($wrapper, $value, $present, $available, $valid, $normalized); |     $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(); |     $wrapper = $schema->getWrapper(); | ||||||
|     if ($inputParams !== null) $orig = new Input($orig, $inputParams); |     if ($inputParams !== null) $orig = new Input($orig, $inputParams); | ||||||
|     self::assertException($exceptionClass, function() use ($wrapper, &$orig, $verifix) { |     self::assertException($exceptionClass, function() use ($wrapper, &$orig, $normalize) { | ||||||
|       $wrapper->reset($orig, null, $verifix); |       $wrapper->resetParams(["normalize" => $normalize]); | ||||||
|  |       $wrapper->reset($orig); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user