diff --git a/.pman.yml b/.composer.pman.yml similarity index 100% rename from .pman.yml rename to .composer.pman.yml diff --git a/src/php/access/KeyAccess.php b/src/php/access/KeyAccess.php index 79948b2..356d297 100644 --- a/src/php/access/KeyAccess.php +++ b/src/php/access/KeyAccess.php @@ -118,33 +118,71 @@ class KeyAccess extends AbstractAccess { ]); } - function ensureAssoc(array $keys): void { - $index = 0; + 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 ($dest !== null && array_key_exists($key, $dest)) continue; + 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[$key] = $dest[$index]; + $dest[$destKey] = $dest[$index]; unset($dest[$index]); $index++; } } } - function ensureKeys(array $defaults): void { + 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) { - if ($dest === null || !array_key_exists($key, $dest)) { - $dest[$key] = $defaults[$key]; + $destKey = "$prefix$key$suffix"; + if ($dest === null || !array_key_exists($destKey, $dest)) { + $dest[$destKey] = $defaults[$key]; } } } - function ensureOrder(array $keys): void { + 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); } } diff --git a/src/php/access/PropertyAccess.php b/src/php/access/PropertyAccess.php index eadf80d..522c9db 100644 --- a/src/php/access/PropertyAccess.php +++ b/src/php/access/PropertyAccess.php @@ -5,7 +5,9 @@ use nulib\StateException; use nulib\str; use ReflectionClass; use ReflectionException; +use ReflectionNamedType; use ReflectionProperty; +use stdClass; class PropertyAccess extends AbstractAccess { const PROTECT_DEST = true; @@ -32,24 +34,32 @@ class PropertyAccess extends AbstractAccess { protected ?ReflectionProperty $property; + private function _getName(string $key): string { + return $this->mapNames? str::us2camel($key): $key; + } private function _setName(?string $name): void { - if ($this->mapNames) $name = str::us2camel($name); + if ($name !== null) $name = $this->_getName($name); $this->name = $name; } - private function _setDest(?object $dest): void { - $this->dest = $dest; + private function _getProperty(?string $name, ?ReflectionClass $class, ?object $object=null): ?ReflectionProperty { $property = null; - $name = $this->name; - if ($dest !== null && $name !== null) { - $class = new ReflectionClass($dest); + 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) { } } - $this->property = $property; + 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 { @@ -135,4 +145,39 @@ class PropertyAccess extends AbstractAccess { if ($key === null) return $this; return new ChainAccess($this, $key); } + + function ensureAssoc(array $keys, ?array $params=null): void { + # NOP + } + + 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]; + } + } + } + + function ensureOrder(array $keys, ?array $params=null): void { + # NOP + } } diff --git a/tests/wip/php/access/KeyAccessTest.php b/tests/wip/php/access/KeyAccessTest.php index 5247d98..8911ccd 100644 --- a/tests/wip/php/access/KeyAccessTest.php +++ b/tests/wip/php/access/KeyAccessTest.php @@ -126,9 +126,9 @@ class KeyAccessTest extends TestCase { self::assertSame("", $a->get($default)); } - private function _ensureAssoc(?array $orig, ?array $expected, array $keys) { + private function _ensureAssoc(?array $orig, ?array $expected, array $keys, ?array $params=null) { $v = $orig; $a = new KeyAccess($v); - $a->ensureAssoc($keys); + $a->ensureAssoc($keys, $params); self::assertSame($expected, $v); } function testEnsureAssoc() { @@ -150,9 +150,9 @@ class KeyAccessTest extends TestCase { $this->_ensureAssoc([1, 2], [1, "a" => 2], $keys); } - private function _ensureKeys(?array $orig, ?array $expected, array $defaults) { + private function _ensureKeys(?array $orig, ?array $expected, array $defaults, ?array $params=null) { $v = $orig; $a = new KeyAccess($v); - $a->ensureKeys($defaults); + $a->ensureKeys($defaults, $params); self::assertSame($expected, $v); } function testEnsureKeys() { @@ -168,12 +168,27 @@ class KeyAccessTest extends TestCase { $this->_ensureKeys(["a" => 1, "b" => 2, "c" => 3, "x"], ["a" => 1, "b" => 2, "c" => 3, "x"], $defaults); } - private function _ensureAssocKeysOrder(?array $orig, ?array $expected, array $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); - $a->ensureKeys($defaults); - $a->ensureOrder($keys); + $a->ensureAssoc($keys, $params); + $a->ensureKeys($defaults, $params); + $a->ensureOrder($keys, $params); self::assertSame($expected, $v); } function testEnsureAssocKeysOrder() { @@ -183,11 +198,18 @@ class KeyAccessTest extends TestCase { $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], [3 => 4, "a" => 1, "b" => 2, "c" => 3], $defaults); - $this->_ensureAssocKeysOrder(["c" => 3, 1], ["c" => 3, "a" => 1, "b" => false], $defaults); - $this->_ensureAssocKeysOrder(["c" => 3, "b" => 2, 1], ["c" => 3, "b" => 2, "a" => 1], $defaults); - $this->_ensureAssocKeysOrder(["c" => 3, "b" => 2, "a" => 1], ["c" => 3, "b" => 2, "a" => 1], $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_", + ]); } }