modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2025-07-02 10:05:14 +04:00
parent 8612a9dae4
commit 8079d111cc
8 changed files with 169 additions and 47 deletions

View File

@ -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;
}

View File

@ -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);

68
src/cache/CacheManager.php vendored Normal file
View File

@ -0,0 +1,68 @@
<?php
namespace nulib\cache;
use nulib\cl;
/**
* Class CacheManager: un gestionnaire de cache permettant de désactiver la mise
* en cache d'une valeur dans le cadre d'une session.
*
* en effet, si on désactive le cache, il doit être réactivé après que la valeur
* est calculée, pour éviter qu'une valeur soit calculée encore et encore dans
* une session de travail
*/
class CacheManager {
function __construct(?array $includes=null, ?array $excludes=null) {
$this->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;
}
}

61
src/cache/cache.php vendored
View File

@ -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));
}
}

View File

@ -1,2 +1,2 @@
/capacitor.db*
/*.db*
/*.cache

View File

@ -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"]}");

View File

@ -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);
}

View File

@ -1,16 +1,18 @@
<?php
namespace nulib\cache;
use nulib\cl;
use nulib\output\msg;
class cacheTest extends _TestCase {
const DATA = [
"fr" => ["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);
}
}