diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..f0e209f --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9173912 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/nur-sery.iml b/.idea/nur-sery.iml new file mode 100644 index 0000000..5d55ddf --- /dev/null +++ b/.idea/nur-sery.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/php-docker-settings.xml b/.idea/php-docker-settings.xml new file mode 100644 index 0000000..bd786be --- /dev/null +++ b/.idea/php-docker-settings.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..31dc4ce --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/phpunit.xml b/.idea/phpunit.xml new file mode 100644 index 0000000..4f8104c --- /dev/null +++ b/.idea/phpunit.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/.keep b/src/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/output/Output.php b/src/output/Output.php new file mode 100644 index 0000000..a716ae1 --- /dev/null +++ b/src/output/Output.php @@ -0,0 +1,22 @@ +printCsv($object); + else $this->printContent([$object]); + } +} diff --git a/src/output/TtyOutput.php b/src/output/TtyOutput.php new file mode 100644 index 0000000..61c1ee9 --- /dev/null +++ b/src/output/TtyOutput.php @@ -0,0 +1,20 @@ +value =& $value; + } + + protected $value; + + function exists($key=null): bool { + if ($key === null) return true; + return $this->value !== null && array_key_exists($key, $this->value); + } + + function get($key=null) { + if ($key === null) return $this->value; + elseif ($this->value === null) return null; + elseif (!array_key_exists($key, $this->value)) return null; + else return $this->value[$key]; + } + + function set($value, $key=null): void { + if ($key === null) $this->value = $value; + else $this->value[$key] = $value; + } +} diff --git a/src/schema/PostInput.php b/src/schema/PostInput.php new file mode 100644 index 0000000..2955aff --- /dev/null +++ b/src/schema/PostInput.php @@ -0,0 +1,21 @@ +schema = $schema; + } + + /** + * @var array schéma normalisé + * + * il y a 3 formes pour un schéma: + * - schéma valeur scalaire [0 => type, 1 => default, ...] + * - schéma tableau séquentiel [schema] + * - schéma tableau associatif [key => schema, ...] + */ + protected $schema; + + /** + * retourner true si le schéma est pour une valeur scalaire, false s'il s'agit + * du schéma d'un tableau (séquentiel ou associatif) + */ + function isScalar(): bool { + return array_key_exists(0, $this->schema) && array_key_exists(1, $this->schema); + } + + /** retourner true si le schéma est pour un tableau séquentiel */ + function isSeq(): bool { + return array_key_exists(0, $this->schema) && !array_key_exists(1, $this->schema); + } + + /** retourner true si le schéma est pour un tableau associatif */ + function isAssoc(): bool { + return !array_key_exists(0, $this->schema) && !array_key_exists(1, $this->schema); + } + + /** + * @var bool true si toutes les clés du schéma doivent exister, avec leur + * valeur par défaut le cas échéant + */ + protected $ppEnsureKeys; + + /** @var bool true si les clés doivent être dans l'ordre du schéma */ + protected $ppOrderKeys; + + /** + * @var bool true si les valeurs doivent être automatiquement vérifiées et + * corrigées + */ + protected $ppVerifix; + + function ensureSchema(&$value, $key=null, ?array $params=null): void { + } + + /** retourner true si la valeur est scalaire */ + function getScalar(&$value, $key=null, ?ScalarValue &$scalar=null): bool { + if (!$this->isScalar()) return false; + if ($value instanceof Input) $input = $value; + else $input = new Input($value); + $scalar = new ScalarValue($input, $this->schema, $key); + return true; + } + + /** retourner true la valeur est un tableau séquentiel */ + function getSeq(&$value, $key=null, ?SeqValue &$seq=null): bool { + if (!$this->isSeq()) return false; + if ($value instanceof Input) $input = $value; + else $input = new Input($value); + $seq = new SeqValue($input, $this->schema, $key); + return true; + } + + /** retourner true la valeur est un tableau associatif */ + function getAssoc(&$value, $key=null, ?AssocValue &$assoc=null): bool { + if (!$this->isAssoc()) return false; + if ($value instanceof Input) $input = $value; + else $input = new Input($value); + $assoc = new AssocValue($input, $this->schema, $key); + return true; + } + + function getValue(&$value, $key=null): IValue { + if ($this->getScalar($input, $key, $scalar)) return $scalar; + elseif ($this->getSeq($input, $key, $seq)) return $seq; + elseif ($this->getAssoc($input, $key, $assoc)) return $assoc; + else throw StateException::unexpected_state(); + } +} diff --git a/src/schema/SchemaException.php b/src/schema/SchemaException.php new file mode 100644 index 0000000..bef20fd --- /dev/null +++ b/src/schema/SchemaException.php @@ -0,0 +1,7 @@ + ["string", null, "" => 0, + "required" => true, + "desc" => "type de la valeur", + ], + "default" => [null, null, "" => 1, + "desc" => "valeur par défaut", + ], + "key" => ["?key", null, "" => "", + "desc" => "clé de la valeur si elle est dans un tableau", + ], + "required" => ["bool", false, + "desc" => "cette valeur est-elle requise?", + ], + "formatter" => ["?callable", null, + "desc" => "signature de la fonction: (value, format, type) => string", + ], + "parser" => ["?callable", null, + "desc" => "signature de la fonction: (value, type) => [value, result]", + ], + "messages" => ["?array", null, + "desc" => "message à afficher en cas d'erreur d'analyse", + ], + ]; + + const MESSAGES = [ + "absent" => "{key}: Vous devez spécifier cette valeur", + "null" => "{key}: Cette valeur ne doit pas être nulle", + "invalid" => "{key}: {orig}: cette valeur est invalide", + ]; +} diff --git a/src/schema/types/IType.php b/src/schema/types/IType.php new file mode 100644 index 0000000..6820ee3 --- /dev/null +++ b/src/schema/types/IType.php @@ -0,0 +1,25 @@ + false]; + foreach (static::KEYS as $key) { + if (!array_key_exists($key, $result)) $result[$key] = null; + } + #XXX interpoler [message] avec les clés de $result + $this->result = $result; + } + + protected $result; + + function __get($name) { + return $this->result[$name]; + } +} diff --git a/src/schema/values/ScalarValue.php b/src/schema/values/ScalarValue.php new file mode 100644 index 0000000..6704947 --- /dev/null +++ b/src/schema/values/ScalarValue.php @@ -0,0 +1,74 @@ +schema = $schema; + $this->reset($value, $key, $verifix); + } + + function isScalar(?ScalarValue &$scalar=null): bool { $scalar = $this; return true; } + function isSeq(?SeqValue &$seq=null): bool { return false; } + function isAssoc(?AssocValue &$assoc=null): bool { return false; } + + function get($default=null) { + $key = $this->key; + if ($key === null) return $this->value; + elseif (array_key_exists($key, $this->value)) return $this->value[$key]; + else return $default; + } + + function set($value): self { + $key = $this->key; + if ($key === null) $this->value = $value; + else $this->value[$key] = $value; + return $this; + } + + /** @var IType */ + protected $type; + + function type(): IType { + if ($this->type === null) $this->type = $this->md()->getType($this->key); + return $this->type; + } + + /** @var ?Result résultat de l'analyse de la valeur */ + protected $result; + + function exists(): bool { + return $this->type()->exists($this->value, $this->key); + } + + /** + * analyser, corriger éventuellement et normaliser la valeur + * + * si la valeur était déjà normalisée, retourner false. + */ + function verifix(bool $throw=true, ?Result &$result=null): bool { + $type = $this->type(); + $key = $this->key; + if ($key === null) $modified = $type->verifix($this->value, $throw, $result); + else $modified = $type->verifix($this->value[$key], $throw, $result); + $this->result = $result; + return $modified; + } + + function parse($value, bool $throw=true, ?Result &$result=null) { + $this->type()->verifix($value, $throw, $result); + $this->set($value); + $this->result = $result; + return $value; + } + + function format(?string $format=null): string { + $type = $this->type(); + $key = $this->key; + if ($key === null) return $type->format($this->value, $format); + else return $type->format($this->value[$key], $format); + } +} diff --git a/src/schema/values/SeqValue.php b/src/schema/values/SeqValue.php new file mode 100644 index 0000000..c72ca81 --- /dev/null +++ b/src/schema/values/SeqValue.php @@ -0,0 +1,20 @@ +md === null) $this->md = Schema::with($this->schema); + return $this->md; + } + + /** @var mixed valeur ou référence d'un tableau contenant la valeur */ + protected $value; + + /** @var string|int clé de la valeur dans le tableau destination */ + protected $key; + + function reset(&$value, $key=null, bool $verifix=true): void { + $this->value =& $value; + $this->key = $key; + $this->result = null; + if ($verifix) $this->verifix(); + } +}