249 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nulib\schema\_scalar;
 | |
| 
 | |
| use nulib\php\func;
 | |
| use nulib\ref\schema\ref_analyze;
 | |
| use nulib\ValueException;
 | |
| use nulib\schema\Schema;
 | |
| use nulib\schema\types;
 | |
| use nulib\schema\types\IType;
 | |
| use nulib\schema\Wrapper;
 | |
| use nulib\schema\WrapperContext;
 | |
| 
 | |
| /**
 | |
|  * Class ScalarWrapper
 | |
|  *
 | |
|  * @method ScalarWrapper reset(&$value, $valueKey=null, ?array $params=null)
 | |
|  * @method ScalarResult getResult($key=false)
 | |
|  * @method self set($value, ?array $params=null, $key=false)
 | |
|  * @method self unset(?array $params=null, $key=false)
 | |
|  */
 | |
| class ScalarWrapper extends Wrapper {
 | |
|   function __construct(Schema $schema, &$value=null, $valueKey=null, ?array $params=null, ?WrapperContext $context=null) {
 | |
|     if ($context === null) $context = new WrapperContext($schema, null, null, $params);
 | |
|     $context->result = new ScalarResult();
 | |
|     $this->context = $context;
 | |
| 
 | |
|     if ($value !== null) {
 | |
|       # n'initialiser que si $value n'est pas null
 | |
|       $this->reset($value, $valueKey);
 | |
|     } else {
 | |
|       # il faut au moins que le type soit disponible
 | |
|       $this->resetContext(false);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   protected WrapperContext $context;
 | |
| 
 | |
|   function getKeys(): array {
 | |
|     return ScalarSchema::KEYS;
 | |
|   }
 | |
| 
 | |
|   /** @param string|int|null $key */
 | |
|   function select($key): ScalarWrapper {
 | |
|     if ($key !== null) throw ValueException::invalid_key($key);
 | |
|     return $this;
 | |
|   }
 | |
| 
 | |
|   /** analyser la valeur et résoudre son type */
 | |
|   protected static function _analyze0(WrapperContext $context): int {
 | |
|     /** @var ScalarSchema $schema */
 | |
|     $schema = $context->schema;
 | |
|     $input = $context->input;
 | |
|     $valueKey = $context->valueKey;
 | |
|     /** @var ScalarResult $result */
 | |
|     $result = $context->result;
 | |
| 
 | |
|     $default = $schema->default;
 | |
|     if (!$input->isPresent($valueKey)) {
 | |
|       if ($default !== null) {
 | |
|         $input->set($default, $valueKey);
 | |
|         return $result->setNormalized();
 | |
|       } else {
 | |
|         return $result->setMissing($schema);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     $schemaTypes = $schema->type;
 | |
|     if ($schemaTypes instanceof IType) {
 | |
|       $type = $schemaTypes;
 | |
|     } else {
 | |
|       # type union
 | |
|       $haveType = false;
 | |
|       $types = [];
 | |
|       $type = $firstType = null;
 | |
|       $value = null;
 | |
|       # d'abord chercher un type pour lequel c'est une valeur normalisée
 | |
|       $index = 0;
 | |
|       $haveValue = false;
 | |
|       foreach ($schemaTypes as $key => $name) {
 | |
|         if ($key === $index) {
 | |
|           $index++;
 | |
|           $args = null;
 | |
|         } else {
 | |
|           $args = $name;
 | |
|           $name = $key;
 | |
|         }
 | |
|         $type = types::get($schema->nullable, $name, $args, $schema->getDefinition());
 | |
|         if ($firstType === null) $firstType = $type;
 | |
|         $types[] = $type;
 | |
|         if ($type->isAvailable($input, $valueKey)) {
 | |
|           if (!$haveValue) {
 | |
|             $value = $input->get($valueKey);
 | |
|             $haveValue = true;
 | |
|           }
 | |
|           if ($type->isValid($value, $normalized) && $normalized) {
 | |
|             $haveType = true;
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       # ensuite chercher un type pour lequel la valeur est valide
 | |
|       if (!$haveType) {
 | |
|         foreach ($types as $type) {
 | |
|           if ($type->isAvailable($input, $valueKey) && $type->isValid($value)) {
 | |
|             $haveType = true;
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       # sinon prendre le premier type
 | |
|       if (!$haveType) {
 | |
|         $type = $firstType;
 | |
|       }
 | |
|     }
 | |
|     $context->type = $type;
 | |
| 
 | |
|     if (!$type->isAvailable($input, $valueKey)) {
 | |
|       if ($default !== null) {
 | |
|         $input->set($default, $valueKey);
 | |
|         return $result->setNormalized();
 | |
|       } else {
 | |
|         return $result->setUnavailable($schema);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     $value = $input->get($valueKey);
 | |
|     $missing = $type->getMissingValue($haveMissing);
 | |
|     if ($haveMissing && $value === $missing) {
 | |
|       if ($default !== null) {
 | |
|         $input->set($default, $valueKey);
 | |
|         return $result->setNormalized();
 | |
|       } else {
 | |
|         return $result->setMissing($schema);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     $context->origValue = $context->value = $value;
 | |
|     if ($type->isNull($value)) {
 | |
|       return $result->setNull($schema);
 | |
|     } elseif (is_string($value)) {
 | |
|       return ref_analyze::STRING;
 | |
|     } elseif ($type->isValid($value, $normalized)) {
 | |
|       if ($normalized) return $result->setNormalized();
 | |
|       else return $result->setValid();
 | |
|     } else {
 | |
|       return $result->setInvalid($value, $schema);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @param ScalarWrapper $wrapper
 | |
|    */
 | |
|   static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int {
 | |
|     /** @var ScalarSchema $schema */
 | |
|     $schema = $context->schema;
 | |
|     $input = $context->input;
 | |
|     $valueKey = $context->valueKey;
 | |
|     /** @var ScalarResult $result */
 | |
|     $result = $context->result;
 | |
| 
 | |
|     /** @var func $analyzerFunc */
 | |
|     $analyzerFunc = $schema->analyzerFunc;
 | |
|     if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context, $wrapper]);
 | |
|     else $what = self::_analyze0($context);
 | |
|     if ($what !== ref_analyze::STRING) return $what;
 | |
| 
 | |
|     $value = $context->value;
 | |
|     try {
 | |
|       /** @var func $extractorFunc */
 | |
|       $extractorFunc = $schema->extractorFunc;
 | |
|       if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context, $wrapper]);
 | |
|       else $extracted = $context->type->extract($value);
 | |
|       $context->value = $extracted;
 | |
|     } catch (ValueException $e) {
 | |
|       return $result->setInvalid($context->origValue, $schema, $e);
 | |
|     }
 | |
|     if ($context->type->isNull($extracted)) return $result->setNull($schema);
 | |
| 
 | |
|     try {
 | |
|       /** @var func $parserFunc */
 | |
|       $parserFunc = $schema->parserFunc;
 | |
|       if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context, $wrapper]);
 | |
|       else $parsed = $context->type->parse($extracted);
 | |
|       $context->value = $parsed;
 | |
|     } catch (ValueException $e) {
 | |
|       return $result->setInvalid($context->origValue, $schema, $e);
 | |
|     }
 | |
| 
 | |
|     $normalized = $parsed === $context->origValue;
 | |
|     if ($normalized) {
 | |
|       $input->set($parsed, $valueKey);
 | |
|       return $result->setNormalized();
 | |
|     } else {
 | |
|       $input->set($extracted, $valueKey);
 | |
|       return $result->setValid($parsed);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @param ScalarWrapper $wrapper
 | |
|    */
 | |
|   static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool {
 | |
|     /** @var ScalarSchema $schema */
 | |
|     $schema = $context->schema;
 | |
|     $input = $context->input;
 | |
|     $valueKey = $context->valueKey;
 | |
|     /** @var ScalarResult $result */
 | |
|     $result = $context->result;
 | |
| 
 | |
|     $normalize = false;
 | |
|     $modified = false;
 | |
|     if ($result->resultAvailable) {
 | |
|       if ($result->null) {
 | |
|         # forcer la valeur null, parce que la valeur actuelle est peut-être une
 | |
|         # valeur assimilée à null
 | |
|         $input->set(null, $valueKey);
 | |
|       } elseif ($result->valid && !$result->normalized) {
 | |
|         $normalizedValue = $result->normalizedValue;
 | |
|         if ($normalizedValue !== null) {
 | |
|           # la valeur normalisée est disponible
 | |
|           $input->set($normalizedValue, $valueKey);
 | |
|           $result->normalizedValue = null;
 | |
|           $modified = true;
 | |
|         } else {
 | |
|           # normaliser la valeur
 | |
|           $normalize = true;
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       $normalize = true;
 | |
|     }
 | |
| 
 | |
|     if ($normalize) {
 | |
|       $value = $input->get($valueKey);
 | |
|       /** @var func $normalizerFunc */
 | |
|       $normalizerFunc = $schema->normalizerFunc;
 | |
|       if ($normalizerFunc !== null) {
 | |
|         $orig = $value;
 | |
|         $value = $normalizerFunc->invoke([$orig, $context, $wrapper]);
 | |
|         $modified = $value !== $orig;
 | |
|       } else {
 | |
|         $modified = $context->type->normalize($value, $result, $schema);
 | |
|       }
 | |
|       if ($result->valid) $input->set($value, $valueKey);
 | |
|     }
 | |
|     return $modified;
 | |
|   }
 | |
| }
 |