diff --git a/php/src/php/types/_schema.php b/php/src/php/types/_schema.php new file mode 100644 index 0000000..e0cc6e9 --- /dev/null +++ b/php/src/php/types/_schema.php @@ -0,0 +1,58 @@ + vrawstring::class, + "string" => vstring::class, + "text" => vtext::class, + "bool" => vbool::class, + "int" => vint::class, + "float" => vfloat::class, + "array" => varray::class, + "func" => vfunc::class, + "raw" => vraw::class, + "mixed" => vmixed::class, + "key" => vkey::class, + "pkey" => vpkey::class, + "content" => vcontent::class, + "datetime" => vdatetime::class, + "date" => vdate::class, + "time" => vtime::class, + ]; + + static function get_types($schema, &$default = null, ?bool &$required = null): array { + if (is_array($schema)) { + $types = $schema["type"] ?? $schema[0] ?? null; + $default = $schema["default"] ?? $schema[1] ?? null; + $required = vbool::with($schema["required"] ?? false); + } elseif (is_string($schema)) { + $types = $schema; + $default = null; + $required = false; + } else { + throw exceptions::invalid_value($schema, "schema"); + } + if (is_string($types)) { + $types = explode(",", $types); + } elseif (!is_array($types)) { + throw exceptions::invalid_value($types, "types"); + } + return $types; + } + + static function get_vclass(array $types, ?bool &$nullable): ?string { + foreach ($types as $type) { + $vclass = self::VCLASSES[$type] ?? null; + if ($vclass !== null) { + $nullable = str::del_prefix($type, "?"); + return $vclass; + } + } + return null; + } +} diff --git a/php/src/php/types/assoc.php b/php/src/php/types/assoc.php new file mode 100644 index 0000000..5380296 --- /dev/null +++ b/php/src/php/types/assoc.php @@ -0,0 +1,75 @@ + $kschema) { + $types = _schema::get_types($kschema, $default, $required); + $exists = array_key_exists($key, $array); + if (!$exists) { + if ($required) return false; + else continue; + } + $vclass = _schema::get_vclass($types, $nullable); + # le test échoue si le type n'est pas supporté + if ($vclass === null) return false; + $value = $array[$key]; + if ($value === null) { + if (!$nullable) return false; + } else { + if (!$vclass::isa($value, $strict)) return false; + } + } + return true; + } + + /** + * s'assurer que $array est conforme au schéma + * - les clés ne sont pas créées si elles n'existent pas, sauf si la valeur + * est requise ou si une valeur par défaut non nulle est fournie + */ + public static function ensure_schema(&$array, array $schema): bool { + $ensured = true; + foreach ($schema as $key => $kschema) { + $types = _schema::get_types($kschema, $default, $required); + + $exists = array_key_exists($key, $array); + if (!$exists) { + if ($required) { + if ($default !== null) { + $array[$key] = $default; + } else { + throw exceptions::missing_value(null, "array", "$key est obligatoire"); + } + } elseif ($default !== null) { + $array[$key] = $default; + } + continue; + } + + $vclass = _schema::get_vclass($types, $nullable); + # le test échoue si le type n'est pas supporté + if ($vclass === null) { + $ensured = false; + continue; + } + + $value = $array[$key]; + if ($nullable) { + $array[$key] = $vclass::withn($value); + } else { + if ($value === null) $value = $default; + $array[$key] = $vclass::with($value); + } + } + return $ensured; + } +} diff --git a/php/src/php/types/scalar.php b/php/src/php/types/scalar.php new file mode 100644 index 0000000..0a34b5b --- /dev/null +++ b/php/src/php/types/scalar.php @@ -0,0 +1,34 @@ + "string", + "int" => "int", + ], [ + "string" => 5, + "int" => "42", + ], [ + "string" => "5", + "int" => 42, + ]); + } + + const SCHEMA_S = ["s" => "string"]; + const SCHEMA_SD = ["s" => ["string", null]]; + const SCHEMA_SND = ["s" => ["string", "not null"]]; + const SCHEMA_RS = ["s" => ["string", "required" => true]]; + const SCHEMA_RSD = ["s" => ["string", null, "required" => true]]; + const SCHEMA_RSND = ["s" => ["string", "not null", "required" => true]]; + const SCHEMA_NS = ["s" => "?string"]; + const SCHEMA_NSD = ["s" => ["?string", null]]; + const SCHEMA_NSND = ["s" => ["?string", "not null"]]; + const SCHEMA_NRS = ["s" => ["?string", "required" => true]]; + const SCHEMA_NRSD = ["s" => ["?string", null, "required" => true]]; + const SCHEMA_NRSND = ["s" => ["?string", "not null", "required" => true]]; + + function testEnsureSchema2() { + self::assertEnsureOk(self::SCHEMA_S, [], []); + self::assertEnsureFail(self::SCHEMA_S, ["s" => null], ["s" => null]); + self::assertEnsureOk(self::SCHEMA_S, ["s" => "not null"], ["s" => "not null"]); + self::assertEnsureOk(self::SCHEMA_NS, [], []); + self::assertEnsureOk(self::SCHEMA_NS, ["s" => null], ["s" => null]); + self::assertEnsureOk(self::SCHEMA_NS, ["s" => "not null"], ["s" => "not null"]); + + self::assertEnsureOk(self::SCHEMA_SD, [], []); + self::assertEnsureFail(self::SCHEMA_SD, ["s" => null], ["s" => null]); + self::assertEnsureOk(self::SCHEMA_SD, ["s" => "not null"], ["s" => "not null"]); + self::assertEnsureOk(self::SCHEMA_NSD, [], []); + self::assertEnsureOk(self::SCHEMA_NSD, ["s" => null], ["s" => null]); + self::assertEnsureOk(self::SCHEMA_NSD, ["s" => "not null"], ["s" => "not null"]); + + self::assertEnsureOk(self::SCHEMA_SND, [], ["s" => "not null"]); + self::assertEnsureOk(self::SCHEMA_SND, ["s" => null], ["s" => "not null"]); + self::assertEnsureOk(self::SCHEMA_SND, ["s" => "not null"], ["s" => "not null"]); + self::assertEnsureOk(self::SCHEMA_NSND, [], ["s" => "not null"]); + self::assertEnsureOk(self::SCHEMA_NSND, ["s" => null], ["s" => null]); + self::assertEnsureOk(self::SCHEMA_NSND, ["s" => "not null"], ["s" => "not null"]); + + self::assertEnsureFail(self::SCHEMA_RS, [], []); + self::assertEnsureFail(self::SCHEMA_RS, ["s" => null], []); + self::assertEnsureOk(self::SCHEMA_RS, ["s" => "not null"], ["s" => "not null"]); + self::assertEnsureFail(self::SCHEMA_NRS, [], []); + self::assertEnsureOk(self::SCHEMA_NRS, ["s" => null], ["s" => null]); + self::assertEnsureOk(self::SCHEMA_NRS, ["s" => "not null"], ["s" => "not null"]); + + self::assertEnsureFail(self::SCHEMA_RSD, [], []); + self::assertEnsureFail(self::SCHEMA_RSD, ["s" => null], []); + self::assertEnsureOk(self::SCHEMA_RSD, ["s" => "not null"], ["s" => "not null"]); + self::assertEnsureFail(self::SCHEMA_NRSD, [], []); + self::assertEnsureOk(self::SCHEMA_NRSD, ["s" => null], ["s" => null]); + self::assertEnsureOk(self::SCHEMA_NRSD, ["s" => "not null"], ["s" => "not null"]); + + self::assertEnsureOk(self::SCHEMA_RSND, [], ["s" => "not null"]); + self::assertEnsureOk(self::SCHEMA_RSND, ["s" => null], ["s" => "not null"]); + self::assertEnsureOk(self::SCHEMA_RSND, ["s" => "not null"], ["s" => "not null"]); + self::assertEnsureOk(self::SCHEMA_NRSND, [], ["s" => "not null"]); + self::assertEnsureOk(self::SCHEMA_NRSND, ["s" => null], ["s" => null]); + self::assertEnsureOk(self::SCHEMA_NRSND, ["s" => "not null"], ["s" => "not null"]); + } +} diff --git a/php/tests/php/types/scalarTest.php b/php/tests/php/types/scalarTest.php new file mode 100644 index 0000000..44bd667 --- /dev/null +++ b/php/tests/php/types/scalarTest.php @@ -0,0 +1,60 @@ +