From 8079d111cc2db824cc9cf90e861951b855d03c4b Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 2 Jul 2025 10:05:14 +0400 Subject: [PATCH] modifs.mineures sans commentaires --- src/cache/CacheChannel.php | 29 ++----------- src/cache/CacheFile.php | 7 +++- src/cache/CacheManager.php | 68 +++++++++++++++++++++++++++++++ src/cache/cache.php | 61 +++++++++++++++++++++++---- tests/cache/.gitignore | 2 +- tests/cache/CursorChannelTest.php | 2 +- tests/cache/_TestCase.php | 2 +- tests/cache/cacheTest.php | 45 +++++++++++++++----- 8 files changed, 169 insertions(+), 47 deletions(-) create mode 100644 src/cache/CacheManager.php diff --git a/src/cache/CacheChannel.php b/src/cache/CacheChannel.php index 49f7dce..91f021d 100644 --- a/src/cache/CacheChannel.php +++ b/src/cache/CacheChannel.php @@ -23,42 +23,19 @@ class CacheChannel extends CapacitorChannel implements IteratorAggregate { "primary key (group_id, id, key_index)", ]; - static function with(CapacitorStorage $storage, ?iterable $rows=null, $cursorId=null): self { + static function with(?iterable $rows=null, $cursorId=null, ?CapacitorStorage $storage=null): self { + $storage ??= cache::storage(); $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 = strval($cursorId[$groupIdKey] ?? ""); - if (array_key_exists("id", $cursorId)) $idKey = "id"; - else $idKey = $keys[0] ?? null; - $id = strval($cursorId[$idKey] ?? ""); - } else { - $groupId = ""; - $id = strval($cursorId); - } - # si le groupe ou le nom sont trop grand, en faire un hash - if (strlen($groupId) > 32) { - $groupId = md5($groupId); - } - if (strlen($id) > 128) { - $id = substr($id, 0, 128 - 32).md5($id); - } - $cursorId = ["group_id" => $groupId, "id" => $id]; - } - /** * @param array|string $cursorId */ function __construct($cursorId) { parent::__construct(); - self::verifix_id($cursorId); + cache::verifix_id($cursorId); $this->cursorId = $cursorId; } diff --git a/src/cache/CacheFile.php b/src/cache/CacheFile.php index 755d91a..a4684f7 100644 --- a/src/cache/CacheFile.php +++ b/src/cache/CacheFile.php @@ -19,6 +19,11 @@ class CacheFile extends SharedFile { const EXT = ".cache"; + static function with($data, ?string $file=null): self { + if ($data instanceof self) return $data; + else return new static($file, $data); + } + protected function ensure_source($source): CacheData { if ($source instanceof CacheData) return $source; if (cv::subclass_of($source, CacheData::class)) return new $source(); @@ -26,7 +31,7 @@ class CacheFile extends SharedFile { throw ValueException::invalid_type($source, CacheData::class); } - function __construct($file, $data=null, ?array $params=null) { + function __construct(?string $file, $data=null, ?array $params=null) { $file ??= path::join(sys_get_temp_dir(), utils::uuidgen()); $file = path::ensure_ext($file, self::EXT); $this->basedir = path::dirname($file); diff --git a/src/cache/CacheManager.php b/src/cache/CacheManager.php new file mode 100644 index 0000000..2b134c9 --- /dev/null +++ b/src/cache/CacheManager.php @@ -0,0 +1,68 @@ +shouldCaches = []; + $this->defaultCache = true; + $this->includes = $includes; + $this->excludes = $excludes; + } + + /** + * @var array tableau {id => shouldCache} indiquant si l'élément id doit être + * mis en cache + */ + protected array $shouldCaches; + + /** + * @var bool valeur par défaut de shouldCache si la valeur n'est pas trouvée + * dans $shouldCache + */ + protected bool $defaultCache; + + /** + * @var array|null groupes à toujours inclure dans le cache. pour les + * identifiants de ces groupe, {@link self::shouldCache()} retourne toujours + * true. + * + * $excludes est prioritaire par rapport à $includes + */ + protected ?array $includes; + + /** + * @var array|null groupes à exclure de la mise en cache. la mise en cache est + * toujours calculée pour les identifiants de ces groupes. + */ + protected ?array $excludes; + + function setNoCache(bool $noCache=true, bool $reset=true): self { + if ($reset) $this->shouldCaches = []; + $this->defaultCache = !$noCache; + return $this; + } + + function shouldCache(string $id, ?string $groupId=null, bool $reset=true): bool { + if ($groupId !== null) { + $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; + } + $cacheId = "$groupId-$id"; + $shouldCache = cl::get($this->shouldCaches, $cacheId, $this->defaultCache); + $this->shouldCaches[$cacheId] = $reset?: $shouldCache; + return $shouldCache; + } +} diff --git a/src/cache/cache.php b/src/cache/cache.php index fce93da..fa0991f 100644 --- a/src/cache/cache.php +++ b/src/cache/cache.php @@ -4,6 +4,7 @@ namespace nulib\cache; use nulib\app; use nulib\db\CapacitorStorage; use nulib\db\sqlite\SqliteStorage; +use nulib\ext\utils; use nulib\php\func; class cache { @@ -15,7 +16,7 @@ class cache { protected static ?CapacitorStorage $storage = null; - protected static function storage(): CapacitorStorage { + static function storage(): CapacitorStorage { return self::$storage ??= new SqliteStorage(self::dbfile()); } @@ -23,13 +24,59 @@ class cache { return self::$storage = $storage; } - 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); + protected static ?CacheManager $manager = null; + + static function manager(): CacheManager { + return self::$manager ??= new CacheManager(); + } + + static function set_manager(CacheManager $manager): CacheManager { + return self::$manager = $manager; + } + + static function nc(bool $noCache=true, bool $reset=false): void { + self::manager()->setNoCache($noCache, $reset); + } + + protected static function should_cache(string $id, ?string $groupId=null, bool $reset=true): bool { + return self::manager()->shouldCache($id, $groupId, $reset); + } + + static function verifix_id(&$cacheId): void { + $cacheId ??= utils::uuidgen(); + if (is_array($cacheId)) { + $keys = array_keys($cacheId); + if (array_key_exists("id", $cacheId)) $idKey = "id"; + else $idKey = $keys[0] ?? null; + $id = strval($cacheId[$idKey] ?? ""); + if (array_key_exists("group_id", $cacheId)) $groupIdKey = "group_id"; + else $groupIdKey = $keys[1] ?? null; + $groupId = strval($cacheId[$groupIdKey] ?? ""); + } else { + $id = strval($cacheId); + $groupId = ""; + } + # si le groupe ou le nom sont trop grand, en faire un hash + if (strlen($groupId) > 32) $groupId = md5($groupId); + if (strlen($id) > 128) $id = substr($id, 0, 128 - 32).md5($id); + $cacheId = ["group_id" => $groupId, "id" => $id]; + } + + static function get(callable $compute, $dataId=null, ?string $file=null) { + self::verifix_id($dataId); + $file ??= "{$dataId["group_id"]}-{$dataId["id"]}"; + $noCache = !self::should_cache($dataId["id"], $dataId["group_id"]); + return CacheFile::with($compute, $file)->get(null, $noCache); + } + + static function all(?iterable $rows, $cursorId=null, ?string $file=null): iterable { + self::verifix_id($cursorId); + $file ??= "{$cursorId["group_id"]}-{$cursorId["id"]}--rows"; + $ccursorId = new CacheFile($file, function() use ($rows, $cursorId) { + CacheChannel::with(null, $cursorId)->build($rows); return $cursorId; }); - return CacheChannel::with(self::storage(), null, $cache->get()); + $noCache = !self::should_cache($cursorId["id"], $cursorId["group_id"]); + return CacheChannel::with(null, $ccursorId->get(null, $noCache)); } } diff --git a/tests/cache/.gitignore b/tests/cache/.gitignore index f59c9dc..d540dae 100644 --- a/tests/cache/.gitignore +++ b/tests/cache/.gitignore @@ -1,2 +1,2 @@ -/capacitor.db* +/*.db* /*.cache diff --git a/tests/cache/CursorChannelTest.php b/tests/cache/CursorChannelTest.php index 4dc498f..6a1aeee 100644 --- a/tests/cache/CursorChannelTest.php +++ b/tests/cache/CursorChannelTest.php @@ -11,7 +11,7 @@ class CursorChannelTest extends _TestCase { ["a" => 1, "b" => 2], ]; - $channel = CacheChannel::with(self::$storage, $data, "numbers"); + $channel = CacheChannel::with($data, "numbers", self::$storage); $count = 0; foreach ($channel as $key => $item) { msg::info("one: $key => {$item["a"]}"); diff --git a/tests/cache/_TestCase.php b/tests/cache/_TestCase.php index 1a4659e..f05875c 100644 --- a/tests/cache/_TestCase.php +++ b/tests/cache/_TestCase.php @@ -12,7 +12,7 @@ class _TestCase extends TestCase { static function setUpBeforeClass(): void { parent::setUpBeforeClass(); msg::set_messenger_class(StdMessenger::class); - self::$storage = new SqliteStorage(__DIR__."/capacitor.db"); + self::$storage = new SqliteStorage(__DIR__."/cache.db"); cache::set_storage(self::$storage); } diff --git a/tests/cache/cacheTest.php b/tests/cache/cacheTest.php index 0f8dbdb..9d24daa 100644 --- a/tests/cache/cacheTest.php +++ b/tests/cache/cacheTest.php @@ -1,16 +1,18 @@ ["a" => "un", "b" => "deux"], + "eng" => ["a" => "one", "b" => "two"], + ["a" => 1, "b" => 2], + ]; + function gendata() { - $data = [ - "fr" => ["a" => "un", "b" => "deux"], - "eng" => ["a" => "one", "b" => "two"], - ["a" => 1, "b" => 2], - ]; - foreach ($data as $key => $item) { + foreach (self::DATA as $key => $item) { msg::info("yield $key"); yield $key => $item; sleep(2); @@ -18,13 +20,36 @@ class cacheTest extends _TestCase { msg::info("fin gendata"); } - function testUsage() { - $data = cache::all($this->gendata(),"gendata"); + function _testRows(iterable $rows) { $count = 0; - foreach ($data as $key => $item) { - msg::info("got $key => ".var_export($item, true)); + foreach ($rows as $key => $row) { + msg::info("got $key => ".var_export($row, true)); $count++; } self::assertSame(3, $count); } + + function testUsage() { + msg::section("all"); + $rows = cache::all($this->gendata(),"gendata"); + $this->_testRows($rows); + + msg::section("get"); + $rows = cache::get(function() { + return self::DATA; + },"gendata"); + $this->_testRows($rows); + } + + function testNc() { + cache::nc(); + + msg::section("first pass"); + $rows = cache::all($this->gendata(),"gendata"); + $this->_testRows($rows); + + msg::section("second pass"); + $rows = cache::all($this->gendata(),"gendata"); + $this->_testRows($rows); + } }