246 lines
7.8 KiB
PHP
246 lines
7.8 KiB
PHP
<?php
|
|
namespace nur\sery\wip\schema\_scalar;
|
|
|
|
use nulib\php\func;
|
|
use nulib\ref\schema\ref_analyze;
|
|
use nulib\ValueException;
|
|
use nur\sery\wip\schema\_assoc\AssocWrapper;
|
|
use nur\sery\wip\schema\_assoc\AssocWrapperContext;
|
|
use nur\sery\wip\schema\Schema;
|
|
use nur\sery\wip\schema\types;
|
|
use nur\sery\wip\schema\types\IType;
|
|
use nur\sery\wip\schema\Wrapper;
|
|
use nur\sery\wip\schema\WrapperContext;
|
|
|
|
/**
|
|
* Class ScalarWrapper
|
|
*
|
|
* @method ScalarWrapper reset(&$value, $valueKey=null, ?array $params=null)
|
|
* @method ScalarResult getResult($key=false)
|
|
* @method self set($value, ?array $params=null, $key=false)
|
|
* @method self unset(?array $params=null, $key=false)
|
|
*/
|
|
class ScalarWrapper extends Wrapper {
|
|
function __construct(Schema $schema, &$value=null, $valueKey=null, ?array $params=null, ?WrapperContext $context=null) {
|
|
if ($context === null) $context = new WrapperContext($schema, null, null, $params);
|
|
$context->result = new ScalarResult();
|
|
$this->context = $context;
|
|
|
|
# calculer manuellemet throw ici parce que WrapperContext le met à true par
|
|
# défaut. on veut pouvoir mettre temporairement throw à false si jamais il
|
|
# n'est pas spécifié par l'utilisateur
|
|
$throw = $params["throw"] ?? null;
|
|
# 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 isScalar(?ScalarWrapper &$wrapper=null): bool { $wrapper = $this; return true; }
|
|
|
|
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);
|
|
$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(?array $params, WrapperContext $context, Wrapper $wrapper): 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(?array $params, WrapperContext $context, Wrapper $wrapper): bool {
|
|
/** @var ScalarSchema $schema */
|
|
$schema = $context->schema;
|
|
$input = $context->input;
|
|
$valueKey = $context->valueKey;
|
|
/** @var ScalarResult $result */
|
|
$result = $context->result;
|
|
|
|
$verifix = 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);
|
|
$result->normalizedValue = null;
|
|
$modified = true;
|
|
} else {
|
|
# normaliser la valeur
|
|
$verifix = true;
|
|
}
|
|
}
|
|
} else {
|
|
$verifix = true;
|
|
}
|
|
|
|
if ($verifix) {
|
|
$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->verifix($value, $result, $schema);
|
|
}
|
|
if ($result->valid) $input->set($value, $valueKey);
|
|
}
|
|
return $modified;
|
|
}
|
|
}
|