From 79b8c33d4f5b4cff02c78bb8d5e6a39927893bb8 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Tue, 1 Jul 2025 17:53:52 +0400 Subject: [PATCH] modifs.mineures sans commentaires --- src/cache/CacheChannel.php | 173 ++++++++++++++++-------------- src/cache/CacheDataChannel.php | 53 --------- src/cache/CacheFile.php | 6 +- src/cache/TODO.md | 8 ++ src/cache/cache.php | 37 +++---- tests/cache/.gitignore | 2 + tests/cache/CursorChannelTest.php | 22 ++++ tests/cache/_TestCase.php | 23 ++++ tests/cache/cacheTest.php | 30 ++++++ 9 files changed, 192 insertions(+), 162 deletions(-) delete mode 100644 src/cache/CacheDataChannel.php create mode 100644 src/cache/TODO.md create mode 100644 tests/cache/.gitignore create mode 100644 tests/cache/CursorChannelTest.php create mode 100644 tests/cache/_TestCase.php create mode 100644 tests/cache/cacheTest.php diff --git a/src/cache/CacheChannel.php b/src/cache/CacheChannel.php index 4272a99..49f7dce 100644 --- a/src/cache/CacheChannel.php +++ b/src/cache/CacheChannel.php @@ -1,116 +1,127 @@ "varchar(64) not null", - "id" => "varchar(64) not null", - "date_start" => "datetime", - "duration_" => "text", - "primary key (group_id, id)", + "group_id" => "varchar(32) not null", // groupe de curseur + "id" => "varchar(128) not null", // nom du curseur + "key_index" => "integer not null", + "key" => "varchar(128) not null", + "search" => "varchar(255)", + + "primary key (group_id, id, key_index)", ]; - 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"; + static function with(CapacitorStorage $storage, ?iterable $rows=null, $cursorId=null): self { + $channel = (new static($cursorId))->initStorage($storage); + if ($rows !== null) $channel->build($rows); + return $channel; + } + + static function verifix_id(&$cursorId): void { + $cursorId ??= utils::uuidgen(); + if (is_array($cursorId)) { + $keys = array_keys($cursorId); + if (array_key_exists("group_id", $cursorId)) $groupIdKey = "group_id"; else $groupIdKey = $keys[1] ?? null; - $groupId = $id[$groupIdKey] ?? ""; - if (array_key_exists("id", $id)) $idKey = "id"; + $groupId = strval($cursorId[$groupIdKey] ?? ""); + if (array_key_exists("id", $cursorId)) $idKey = "id"; else $idKey = $keys[0] ?? null; - $id = $id[$idKey] ?? ""; + $id = strval($cursorId[$idKey] ?? ""); } else { $groupId = ""; + $id = strval($cursorId); } - 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"; + # si le groupe ou le nom sont trop grand, en faire un hash + if (strlen($groupId) > 32) { + $groupId = md5($groupId); } - return ["group_id" => $groupId, "id" => $id]; + if (strlen($id) > 128) { + $id = substr($id, 0, 128 - 32).md5($id); + } + $cursorId = ["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; + /** + * @param array|string $cursorId + */ + function __construct($cursorId) { + parent::__construct(); + self::verifix_id($cursorId); + $this->cursorId = $cursorId; } - protected string $duration; + protected array $cursorId; - protected ?array $includes; + function getCursorId(): array { + return $this->cursorId; + } - protected ?array $excludes; + function getBaseFilter(): ?array { + return $this->cursorId; + } - function getItemValues($item): ?array { - return cl::merge(self::get_cache_ids($item), [ - "item" => null, + protected int $index = 0; + + protected function getSearch($item): ?string { + $search = cl::filter_n(cl::with($item)); + $search = implode(" ", $search); + return substr($search, 0, 255); + } + + function getItemValues($item, $key=null): ?array { + $index = $this->index++; + $key = $key ?? $index; + $key = substr(strval($key), 0, 128); + return cl::merge($this->cursorId, [ + "key_index" => $index, + "key" => $key, + "search" => $this->getSearch($item), ]); } - function onCreate($item, array $row, ?array $alwaysNull, ?string $duration=null): ?array { - $now = new DateTime(); - $duration ??= $this->duration; - return [ - "date_start" => $now, - "duration" => new Delay($duration, $now), - ]; + function reset(bool $recreate=false): void { + $this->index = 0; + parent::reset($recreate); } - function onUpdate($item, array $row, array $prow, ?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; + function chargeAll(?iterable $items, $func=null, ?array $args=null): int { + $this->index = 0; + if ($items === null) return 0; + $count = 0; + if ($func !== null) $func = func::with($func, $args)->bind($this); + foreach ($items as $key => $item) { + $count += $this->charge($item, $func, [$key]); } - - $found = false; - $expired = false; - $this->each($cacheIds, - function($row) use (&$found, &$expired) { - $found = true; - $expired = $row["duration"]->isElapsed(); - }); - return !$found || $expired; + return $count; } - function setCached($id, ?string $duration=null): void { - $cacheIds = self::get_cache_ids($id); - $this->charge($cacheIds, null, [$duration]); + function build(?iterable $items): self { + $this->reset(true); + $this->chargeAll($items); + return $this; } - function resetCached($id) { - $cacheIds = self::get_cache_ids($id); - $this->delete($cacheIds); + function getIterator(): Traversable { + $rows = $this->dbAll([ + "cols" => ["key", "item__"], + "where" => $this->getBaseFilter(), + ]); + foreach ($rows as $row) { + $key = $row["key"]; + $item = $this->unserialize($row["item__"]); + yield $key => $item; + } } } diff --git a/src/cache/CacheDataChannel.php b/src/cache/CacheDataChannel.php deleted file mode 100644 index af885d6..0000000 --- a/src/cache/CacheDataChannel.php +++ /dev/null @@ -1,53 +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/src/cache/CacheFile.php b/src/cache/CacheFile.php index 18cc295..6cab2ba 100644 --- a/src/cache/CacheFile.php +++ b/src/cache/CacheFile.php @@ -3,6 +3,7 @@ namespace nulib\cache; use Exception; use nulib\cv; +use nulib\ext\utils; use nulib\file; use nulib\file\SharedFile; use nulib\os\path; @@ -26,10 +27,7 @@ class CacheFile extends SharedFile { } function __construct($file, $data=null, ?array $params=null) { - if ($file === null) { - $rand = bin2hex(random_bytes(8)); - $file = path::join(sys_get_temp_dir(), $rand); - } + $file ??= path::join(sys_get_temp_dir(), utils::uuidgen()); $file = path::ensure_ext($file, self::EXT); $this->basedir = path::dirname($file); $basename = path::filename($file); diff --git a/src/cache/TODO.md b/src/cache/TODO.md new file mode 100644 index 0000000..4356aa0 --- /dev/null +++ b/src/cache/TODO.md @@ -0,0 +1,8 @@ +# nulib\cache + +* CacheChannel + * spécifier des clés supplémentaires utilisable dans la recherche +* CacheFile + * pour $data===null, stocker dans le fichier de cache directement + +-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary \ No newline at end of file diff --git a/src/cache/cache.php b/src/cache/cache.php index 46f93b0..fce93da 100644 --- a/src/cache/cache.php +++ b/src/cache/cache.php @@ -2,11 +2,9 @@ namespace nulib\cache; use nulib\app; -use nulib\cache\CacheChannel; -use nulib\cache\CacheDataChannel; -use nulib\db\Capacitor; use nulib\db\CapacitorStorage; use nulib\db\sqlite\SqliteStorage; +use nulib\php\func; class cache { protected static ?string $dbfile = null; @@ -17,30 +15,21 @@ class cache { protected static ?CapacitorStorage $storage = null; + protected static function storage(): CapacitorStorage { + return self::$storage ??= new SqliteStorage(self::dbfile()); + } + static function set_storage(CapacitorStorage $storage): CapacitorStorage { return self::$storage = $storage; } - protected static function get_storage(): CapacitorStorage { - return self::$storage ??= new SqliteStorage(self::dbfile()); - } - - protected static ?CacheChannel $channel = null; - - static function set(?CacheChannel $channel): CacheChannel { - $channel ??= new CacheChannel(); - new Capacitor(self::get_storage(), $channel); - return self::$channel = $channel; - } - - static function get(): CacheChannel { - if (self::$channel !== null) return self::$channel; - else return self::set(null); - } - - static function new(?CacheDataChannel $channel, $id=null, ?callable $builder=null): CacheDataChannel { - $channel ??= new CacheDataChannel($id, $builder); - new Capacitor(self::get_storage(), $channel); - return $channel; + static function all(?iterable $rows, $cursorId=null): iterable { + CacheChannel::verifix_id($cursorId); + $file = "row-{$cursorId["group_id"]}-{$cursorId["id"]}"; + $cache = new CacheFile($file, function() use ($rows, $cursorId) { + CacheChannel::with(self::storage(), null, $cursorId)->build($rows); + return $cursorId; + }); + return CacheChannel::with(self::storage(), null, $cache->get()); } } diff --git a/tests/cache/.gitignore b/tests/cache/.gitignore new file mode 100644 index 0000000..f59c9dc --- /dev/null +++ b/tests/cache/.gitignore @@ -0,0 +1,2 @@ +/capacitor.db* +/*.cache diff --git a/tests/cache/CursorChannelTest.php b/tests/cache/CursorChannelTest.php new file mode 100644 index 0000000..4dc498f --- /dev/null +++ b/tests/cache/CursorChannelTest.php @@ -0,0 +1,22 @@ + ["a" => "un", "b" => "deux"], + "eng" => ["a" => "one", "b" => "two"], + ["a" => 1, "b" => 2], + ]; + + $channel = CacheChannel::with(self::$storage, $data, "numbers"); + $count = 0; + foreach ($channel as $key => $item) { + msg::info("one: $key => {$item["a"]}"); + $count++; + } + self::assertSame(3, $count); + } +} diff --git a/tests/cache/_TestCase.php b/tests/cache/_TestCase.php new file mode 100644 index 0000000..1a4659e --- /dev/null +++ b/tests/cache/_TestCase.php @@ -0,0 +1,23 @@ +close(); + } +} diff --git a/tests/cache/cacheTest.php b/tests/cache/cacheTest.php new file mode 100644 index 0000000..0f8dbdb --- /dev/null +++ b/tests/cache/cacheTest.php @@ -0,0 +1,30 @@ + ["a" => "un", "b" => "deux"], + "eng" => ["a" => "one", "b" => "two"], + ["a" => 1, "b" => 2], + ]; + foreach ($data as $key => $item) { + msg::info("yield $key"); + yield $key => $item; + sleep(2); + } + msg::info("fin gendata"); + } + + function testUsage() { + $data = cache::all($this->gendata(),"gendata"); + $count = 0; + foreach ($data as $key => $item) { + msg::info("got $key => ".var_export($item, true)); + $count++; + } + self::assertSame(3, $count); + } +}