From c8dcc6fe279fe3589ca20d13bcbbdc9be2b50285 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 12 May 2025 15:31:32 +0400 Subject: [PATCH 01/12] Init changelog & version 0.5.1p82 --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 047411f..e2f1c9d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,5 @@ +## Release 0.5.1p82 du 12/05/2025-15:31 + ## Release 0.5.1p74 du 12/05/2025-15:28 * `d274a65` améliorer status From 687493fa1e6405820985c8ece4bef41e31ef78ca Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 12 May 2025 15:41:13 +0400 Subject: [PATCH 02/12] modifs.mineures sans commentaires --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eee5634..a4372c8 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,11 @@ git checkout dev82 prel -C -commit="$(git log --grep="Init changelog . version ${version}p82" --format=%H)" +commit="$(git log --grep="Init changelog . version ${version}p82" --format=%H)" && +echo "commit=$commit" + git checkout dev74 + git cherry-pick "$commit" pp -a ~~~ From 1f68c4bac40659e799fcc9e1965d4fe4375f6686 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 14 May 2025 17:43:50 +0400 Subject: [PATCH 03/12] modifs.mineures sans commentaires --- php/src/A.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/php/src/A.php b/php/src/A.php index 253592b..a3973ca 100644 --- a/php/src/A.php +++ b/php/src/A.php @@ -177,6 +177,13 @@ class A { return $pvalue; } + static final function shift(?array &$dest, int $count=1) { + if ($dest === null) return null; + $values = array_slice($dest, 0, $count); + $dest = array_slice($dest, $count); + return $count == 1? $values[0]: $values; + } + static final function pop(&$dest, $key, $default=null) { if ($dest === null) return $default; self::ensure_narray($dest); From 43c53b286ab9334b72bb51fa3f1b5a5a0a6ccc3b Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Thu, 15 May 2025 11:52:33 +0400 Subject: [PATCH 04/12] modifs.mineures sans commentaires --- php/src/A.php | 5 +++-- php/src/db/Capacitor.php | 8 ++++++++ php/src/db/CapacitorChannel.php | 4 ++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/php/src/A.php b/php/src/A.php index a3973ca..75f0284 100644 --- a/php/src/A.php +++ b/php/src/A.php @@ -177,11 +177,12 @@ class A { return $pvalue; } - static final function shift(?array &$dest, int $count=1) { + static final function shift(?array &$dest, int $count=1, $default=null) { if ($dest === null) return null; $values = array_slice($dest, 0, $count); $dest = array_slice($dest, $count); - return $count == 1? $values[0]: $values; + if ($values === []) return $default; + else return $count == 1? $values[0]: $values; } static final function pop(&$dest, $key, $default=null) { diff --git a/php/src/db/Capacitor.php b/php/src/db/Capacitor.php index 0fee8b5..5551f1b 100644 --- a/php/src/db/Capacitor.php +++ b/php/src/db/Capacitor.php @@ -1,6 +1,7 @@ storage->_delete($this->channel, $filter, $func, $args); } + function dbUpdate(array $update) { + return $this->storage->db()->exec(cl::merge([ + "update", + "table" => $this->getTableName(), + ], $update)); + } + function close(): void { $this->storage->close(); } diff --git a/php/src/db/CapacitorChannel.php b/php/src/db/CapacitorChannel.php index c7265bb..df96e2a 100644 --- a/php/src/db/CapacitorChannel.php +++ b/php/src/db/CapacitorChannel.php @@ -482,6 +482,10 @@ class CapacitorChannel implements ITransactor { return $this->capacitor->delete($filter, $func, $args); } + function dbUpdate(array $update) { + return $this->capacitor->dbUpdate($update); + } + function close(): void { $this->capacitor->close(); } From b1fc572fd88906e0eab3537641a33f09005905c2 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Thu, 15 May 2025 18:06:14 +0400 Subject: [PATCH 05/12] CsvReader accepte une instance de IReader --- php/src/file/IReader.php | 2 ++ php/src/file/csv/CsvReader.php | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/php/src/file/IReader.php b/php/src/file/IReader.php index 36a351b..fa96f7d 100644 --- a/php/src/file/IReader.php +++ b/php/src/file/IReader.php @@ -40,4 +40,6 @@ interface IReader extends _IFile { function unserialize(?array $options=null, bool $close=true, bool $alreadyLocked=false); function copyTo(IWriter $dest, bool $closeWriter=false, bool $closeReader=true): void; + + function setCsvFlavour(?string $flavour): void; } diff --git a/php/src/file/csv/CsvReader.php b/php/src/file/csv/CsvReader.php index d923976..317fda0 100644 --- a/php/src/file/csv/CsvReader.php +++ b/php/src/file/csv/CsvReader.php @@ -2,7 +2,9 @@ namespace nulib\file\csv; use nulib\file; +use nulib\file\_IFile; use nulib\file\FileReader; +use nulib\file\IReader; use nulib\file\tab\AbstractReader; use nulib\file\tab\TAbstractReader; @@ -20,7 +22,12 @@ class CsvReader extends AbstractReader { protected ?string $inputEncoding; function getIterator() { - $reader = new FileReader(file::fix_dash($this->input)); + $input = $this->input; + if ($input instanceof IReader) { + $reader = $input; + } else { + $reader = new FileReader(file::fix_dash($input)); + } $inputEncoding = $this->inputEncoding; if ($inputEncoding !== null) { $reader->appendFilter("convert.iconv.$inputEncoding.utf-8"); From d25fd92ffdf9efd3bee9d8172a7f17393228d7a3 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 19 May 2025 05:10:07 +0400 Subject: [PATCH 06/12] bug --- bash/src/pman.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bash/src/pman.sh b/bash/src/pman.sh index 4a2d928..094b269 100644 --- a/bash/src/pman.sh +++ b/bash/src/pman.sh @@ -426,7 +426,7 @@ EOF $(qvals echo "$(awk <"$changelog" ' BEGIN { p = 0 } p == 0 && $0 == "" { p = 1; next } -p == 1 { gsub(/\$/, "\\$", $0); print } +p == 1 { print } ')") >CHANGES.md git add CHANGES.md EOF From 599475043c4bc25ecd0fa09808dcd0295058033a Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Tue, 20 May 2025 06:35:53 +0400 Subject: [PATCH 07/12] =?UTF-8?q?d=C3=A9sactiver=20connexion=20persistante?= =?UTF-8?q?=20par=20d=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- php/src/db/pgsql/Pgsql.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/php/src/db/pgsql/Pgsql.php b/php/src/db/pgsql/Pgsql.php index efc84a4..f765666 100644 --- a/php/src/db/pgsql/Pgsql.php +++ b/php/src/db/pgsql/Pgsql.php @@ -30,7 +30,11 @@ class Pgsql implements IDatabase { protected const OPTIONS = [ - "persistent" => true, + # XXX désactiver les connexions persistantes par défaut + # pour réactiver par défaut, il faudrait vérifier la connexion à chaque fois + # qu'elle est ouverte avec un "select 1". en effet, l'expérience jusqu'ici + # est que la première connexion après un long timeout échoue + "persistent" => false, "force_new" => false, "serial_support" => true, ]; From 17fa5f9db57b0ad9e13d65268cb33734f269e7b9 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 21 May 2025 05:41:11 +0400 Subject: [PATCH 08/12] modifs.mineures sans commentaires --- php/src/db/CapacitorChannel.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/php/src/db/CapacitorChannel.php b/php/src/db/CapacitorChannel.php index df96e2a..6ed4051 100644 --- a/php/src/db/CapacitorChannel.php +++ b/php/src/db/CapacitorChannel.php @@ -13,7 +13,9 @@ class CapacitorChannel implements ITransactor { const TABLE_NAME = null; - const COLUMN_DEFINITIONS = null; + protected function COLUMN_DEFINITIONS(): ?array { + return static::COLUMN_DEFINITIONS; + } const COLUMN_DEFINITIONS = null; const PRIMARY_KEYS = null; @@ -63,7 +65,7 @@ class CapacitorChannel implements ITransactor { $this->useCache = static::USE_CACHE; $this->setup = false; $this->created = false; - $columnDefinitions = cl::withn(static::COLUMN_DEFINITIONS); + $columnDefinitions = $this->COLUMN_DEFINITIONS(); $primaryKeys = cl::withn(static::PRIMARY_KEYS); $migration = cl::withn(static::MIGRATION); $lastMkey = 1; From 43f84843aef17ec810a59f52703cd33e32cbd5b2 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Fri, 23 May 2025 05:02:01 +0400 Subject: [PATCH 09/12] =?UTF-8?q?d=C3=A9placer=20dans=20nur-ture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- php/src/db/cache/CacheChannel.php | 116 ------------------------------ php/src/db/cache/RowsChannel.php | 51 ------------- php/src/db/cache/cache.php | 37 ---------- 3 files changed, 204 deletions(-) delete mode 100644 php/src/db/cache/CacheChannel.php delete mode 100644 php/src/db/cache/RowsChannel.php delete mode 100644 php/src/db/cache/cache.php diff --git a/php/src/db/cache/CacheChannel.php b/php/src/db/cache/CacheChannel.php deleted file mode 100644 index b1f8619..0000000 --- a/php/src/db/cache/CacheChannel.php +++ /dev/null @@ -1,116 +0,0 @@ - "varchar(64) not null", - "id" => "varchar(64) not null", - "date_start" => "datetime", - "duration_" => "text", - "primary key (group_id, id)", - ]; - - static function get_cache_ids($id): array { - if (is_array($id)) { - $keys = array_keys($id); - if (array_key_exists("group_id", $id)) $groupIdKey = "group_id"; - else $groupIdKey = $keys[1] ?? null; - $groupId = $id[$groupIdKey] ?? ""; - if (array_key_exists("id", $id)) $idKey = "id"; - else $idKey = $keys[0] ?? null; - $id = $id[$idKey] ?? ""; - } else { - $groupId = ""; - } - if (preg_match('/^(.*\\\\)?([^\\\\]+)$/', $groupId, $ms)) { - # si le groupe est une classe, faire un hash du package pour limiter la - # longueur du groupe - [$package, $groupId] = [$ms[1], $ms[2]]; - $package = substr(md5($package), 0, 4); - $groupId = "${groupId}_$package"; - } - return ["group_id" => $groupId, "id" => $id]; - } - - function __construct(?string $duration=null, ?string $name=null) { - parent::__construct($name); - $this->duration = $duration ?? static::DURATION; - $this->includes = static::INCLUDES; - $this->excludes = static::EXCLUDES; - } - - protected string $duration; - - protected ?array $includes; - - protected ?array $excludes; - - function getItemValues($item): ?array { - return cl::merge(self::get_cache_ids($item), [ - "item" => null, - ]); - } - - function onCreate($item, array $values, ?array $alwaysNull, ?string $duration=null): ?array { - $now = new DateTime(); - $duration ??= $this->duration; - return [ - "date_start" => $now, - "duration" => new Delay($duration, $now), - ]; - } - - function onUpdate($item, array $values, array $pvalues, ?string $duration=null): ?array { - $now = new DateTime(); - $duration ??= $this->duration; - return [ - "date_start" => $now, - "duration" => new Delay($duration, $now), - ]; - } - - function shouldUpdate($id, bool $noCache=false): bool { - if ($noCache) return true; - - $cacheIds = self::get_cache_ids($id); - $groupId = $cacheIds["group_id"]; - if ($groupId) { - $includes = $this->includes; - $shouldInclude = $includes !== null && in_array($groupId, $includes); - $excludes = $this->excludes; - $shouldExclude = $excludes !== null && in_array($groupId, $excludes); - if (!$shouldInclude || $shouldExclude) return true; - } - - $found = false; - $expired = false; - $this->each($cacheIds, - function($item, $values) use (&$found, &$expired) { - $found = true; - $expired = $values["duration"]->isElapsed(); - }); - return !$found || $expired; - } - - function setCached($id, ?string $duration=null): void { - $cacheIds = self::get_cache_ids($id); - $this->charge($cacheIds, null, [$duration]); - } - - function resetCached($id) { - $cacheIds = self::get_cache_ids($id); - $this->delete($cacheIds); - } -} diff --git a/php/src/db/cache/RowsChannel.php b/php/src/db/cache/RowsChannel.php deleted file mode 100644 index a3f7055..0000000 --- a/php/src/db/cache/RowsChannel.php +++ /dev/null @@ -1,51 +0,0 @@ - "varchar(128) primary key not null", - "all_values" => "mediumtext", - ]; - - function __construct($id, callable $builder, ?string $duration=null) { - $this->cacheIds = $cacheIds = CacheChannel::get_cache_ids($id); - $this->builder = Closure::fromCallable($builder); - $this->duration = $duration; - $name = "{$cacheIds["group_id"]}-{$cacheIds["id"]}"; - parent::__construct($name); - } - - protected array $cacheIds; - - protected Closure $builder; - - protected ?string $duration = null; - - function getItemValues($item): ?array { - $key = array_keys($item)[0]; - $row = $item[$key]; - return [ - "key" => $key, - "item" => $row, - "all_values" => implode(" ", cl::filter_n(cl::with($row))), - ]; - } - - function getIterator(): Traversable { - $cm = cache::get(); - if ($cm->shouldUpdate($this->cacheIds)) { - $this->capacitor->reset(); - foreach (($this->builder)() as $key => $row) { - $this->charge([$key => $row]); - } - $cm->setCached($this->cacheIds, $this->duration); - } - return $this->discharge(false); - } -} diff --git a/php/src/db/cache/cache.php b/php/src/db/cache/cache.php deleted file mode 100644 index 401fb19..0000000 --- a/php/src/db/cache/cache.php +++ /dev/null @@ -1,37 +0,0 @@ - Date: Fri, 23 May 2025 07:16:44 +0400 Subject: [PATCH 10/12] ajout func::withn --- php/src/php/func.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/php/src/php/func.php b/php/src/php/func.php index f2d2883..fcec77b 100644 --- a/php/src/php/func.php +++ b/php/src/php/func.php @@ -478,11 +478,17 @@ class func { } static function with($func, ?array $args=null, bool $strict=true): self { + if ($func instanceof self) return $func; $func = self::_with($func, $args, $strict, $reason); if ($func !== null) return $func; throw self::not_a_callable($func, $reason); } + static function withn($func, ?array $args=null, bool $strict=true): ?self { + if ($func === null) return null; + else return self::with($func, $args, $strict); + } + static function ensure($func, ?array $args=null, bool $strict=true): self { $func = self::with($func, $args, $strict); if (!$func->isBound()) { From 3aec24ddb375b7cf18c10a7f866e5b554ba838e5 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Fri, 23 May 2025 16:53:08 +0400 Subject: [PATCH 11/12] modifs.mineures sans commentaires --- php/src/cv.php | 24 ++++++++++++++++++++---- php/src/file/Stream.php | 21 ++++++++++++++------- php/src/php/time/DateInterval.php | 2 +- php/src/php/time/Delay.php | 9 +++++++-- 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/php/src/cv.php b/php/src/cv.php index 3bd8773..fd61a8e 100644 --- a/php/src/cv.php +++ b/php/src/cv.php @@ -86,6 +86,12 @@ class cv { $b = $tmp; } + /** cloner une valeur */ + static final function clone($value) { + if (is_object($value)) $value = clone $value; + return $value; + } + ############################################################################# /** mettre à jour $dest avec $value si $cond($value) est vrai */ @@ -197,19 +203,29 @@ class cv { } /** - * retourner [$bool, $string, $array] initialisés chacun en fonction du type + * retourner [$bool, $scalar, $array] initialisés chacun en fonction du type * de $value. * * @throws ValueException si $value n'est d'aucun de ces types */ static final function check_bsa($value, ?string $prefix=null, bool $throw_exception=true): array { $bool = is_bool($value)? $value : null; - $string = is_string($value)? $value : null; + $scalar = !is_bool($value) && is_scalar($value)? $value : null; $array = is_array($value)? $value : null; - if ($bool === null && $string === null && $array === null && $throw_exception) { + if ($bool === null && $scalar === null && $array === null && $throw_exception) { throw ValueException::invalid_kind($value, "value", $prefix); } else { - return [$bool, $string, $array]; + return [$bool, $scalar, $array]; + } + } + + static final function subclass_of($value, $class): bool { + if (is_string($value)) { + return $value === $class || is_subclass_of($value, $class); + } elseif (is_object($value)) { + return $value instanceof $class; + } else { + return false; } } diff --git a/php/src/file/Stream.php b/php/src/file/Stream.php index 65216ee..c1d7496 100644 --- a/php/src/file/Stream.php +++ b/php/src/file/Stream.php @@ -299,8 +299,8 @@ class Stream extends AbstractIterator implements IReader, IWriter { /** retourner le contenu du fichier sous forme de chaine */ function getContents(bool $close=true, bool $alreadyLocked=false): string { - $useLocking = $this->useLocking; - if ($useLocking && !$alreadyLocked) $this->lock(LOCK_SH); + $useLocking = $this->useLocking && !$alreadyLocked; + if ($useLocking) $this->lock(LOCK_SH); try { return IOException::ensure_valid(stream_get_contents($this->fd), $this->throwOnError); } finally { @@ -380,7 +380,9 @@ class Stream extends AbstractIterator implements IReader, IWriter { /** @throws IOException */ function ftruncate(int $size=0, bool $rewind=true): self { $fd = $this->getResource(); - IOException::ensure_valid(ftruncate($fd, $size), $this->throwOnError); + $r = ftruncate($fd, $size); + $this->stat = null; + IOException::ensure_valid($r, $this->throwOnError); if ($rewind) rewind($fd); return $this; } @@ -390,6 +392,7 @@ class Stream extends AbstractIterator implements IReader, IWriter { $fd = $this->getResource(); if ($length === null) $r = fwrite($fd, $data); else $r = fwrite($fd, $data, $length); + $this->stat = null; return IOException::ensure_valid($r, $this->throwOnError); } @@ -403,9 +406,13 @@ class Stream extends AbstractIterator implements IReader, IWriter { $line[] = strval($col); } $line = implode($sep, $line); - IOException::ensure_valid(fwrite($fd, "$line\n"), $this->throwOnError); + $r = fwrite($fd, "$line\n"); + $this->stat = null; + IOException::ensure_valid($r, $this->throwOnError); } else { - IOException::ensure_valid(fputcsv($fd, $row, $params[0], $params[1], $params[2]), $this->throwOnError); + $r = fputcsv($fd, $row, $params[0], $params[1], $params[2]); + $this->stat = null; + IOException::ensure_valid($r, $this->throwOnError); } } @@ -471,8 +478,8 @@ class Stream extends AbstractIterator implements IReader, IWriter { } function putContents(string $contents, bool $close=true, bool $alreadyLocked=false): void { - $useLocking = $this->useLocking; - if ($useLocking && !$alreadyLocked) $this->lock(LOCK_EX); + $useLocking = $this->useLocking && !$alreadyLocked; + if ($useLocking) $this->lock(LOCK_EX); try { $this->fwrite($contents); } finally { diff --git a/php/src/php/time/DateInterval.php b/php/src/php/time/DateInterval.php index c9ca935..210e6ba 100644 --- a/php/src/php/time/DateInterval.php +++ b/php/src/php/time/DateInterval.php @@ -29,7 +29,7 @@ class DateInterval extends \DateInterval { } function __construct($duration) { - if (is_int($duration)) $duration = "PT${duration}S"; + if (is_numeric($duration)) $duration = "PT${duration}S"; if ($duration instanceof \DateInterval) { $this->y = $duration->y; $this->m = $duration->m; diff --git a/php/src/php/time/Delay.php b/php/src/php/time/Delay.php index 14a5307..4afcf37 100644 --- a/php/src/php/time/Delay.php +++ b/php/src/php/time/Delay.php @@ -3,6 +3,7 @@ namespace nulib\php\time; use DateTimeInterface; use InvalidArgumentException; +use nulib\ValueException; /** * Class Delay: une durée jusqu'à un moment destination. le moment destination @@ -115,6 +116,10 @@ class Delay { $this->repr = $repr; } + function __clone() { + $this->dest = clone $this->dest; + } + function __serialize(): array { return [$this->dest, $this->repr]; } @@ -130,7 +135,7 @@ class Delay { } function addDuration($duration) { - if (is_int($duration) && $duration < 0) { + if (is_numeric($duration) && $duration < 0) { $this->dest->sub(DateInterval::with(-$duration)); } else { $this->dest->add(DateInterval::with($duration)); @@ -138,7 +143,7 @@ class Delay { } function subDuration($duration) { - if (is_int($duration) && $duration < 0) { + if (is_numeric($duration) && $duration < 0) { $this->dest->add(DateInterval::with(-$duration)); } else { $this->dest->sub(DateInterval::with($duration)); From ec978b9d00ffa44d962e31dda6fbaca72125ea6a Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Fri, 23 May 2025 21:19:13 +0400 Subject: [PATCH 12/12] cosmetic --- php/src/db/CapacitorChannel.php | 8 +++----- php/src/db/CapacitorStorage.php | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/php/src/db/CapacitorChannel.php b/php/src/db/CapacitorChannel.php index 6ed4051..4349e4b 100644 --- a/php/src/db/CapacitorChannel.php +++ b/php/src/db/CapacitorChannel.php @@ -30,19 +30,17 @@ class CapacitorChannel implements ITransactor { static function verifix_name(?string &$name, ?string &$tableName=null): void { if ($name !== null) { $name = strtolower($name); - if ($tableName === null) { - $tableName = str_replace("-", "_", $name) . "_channel"; - } + $tableName ??= str_replace("-", "_", $name) . "_channel"; } else { $name = static::class; if ($name === self::class) { $name = "default"; - if ($tableName === null) $tableName = "default_channel"; + $tableName ??= "default_channel"; } else { $name = preg_replace('/^.*\\\\/', "", $name); $name = preg_replace('/Channel$/', "", $name); $name = lcfirst($name); - if ($tableName === null) $tableName = str::camel2us($name); + $tableName ??= str::camel2us($name); $name = strtolower($name); } } diff --git a/php/src/db/CapacitorStorage.php b/php/src/db/CapacitorStorage.php index ba00c07..9d4cdb1 100644 --- a/php/src/db/CapacitorStorage.php +++ b/php/src/db/CapacitorStorage.php @@ -3,7 +3,6 @@ namespace nulib\db; use nulib\cl; use nulib\db\_private\_migration; -use nulib\db\cache\cache; use nulib\php\func; use nulib\ValueException; use Traversable;