modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2025-10-05 16:44:17 +04:00
parent 36bae9ef7d
commit c9c40644eb
10 changed files with 160 additions and 162 deletions

46
src/cache/CacheData.php vendored Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace nulib\cache;
use nulib\cl;
use nulib\php\func;
use Traversable;
/**
* Interface CacheData: gestion d'une donnée mise en cache
*/
abstract class CacheData {
function __construct(?string $name, $compute) {
$this->name = $name ?? "";
$this->compute = func::withn($compute ?? static::COMPUTE);
}
protected string $name;
function getName() : string {
return $this->name;
}
protected ?func $compute;
/** calculer la donnée */
function compute() {
$compute = $this->compute;
$data = $compute !== null? $compute->invoke(): null;
return $data;
}
/** spécifier le chemin du cache à partir du fichier de base */
abstract function setDatafile(?string $basefile): void;
/** indiquer si le cache existe */
abstract function exists(): bool;
/** charger la donnée depuis le cache */
abstract function load();
/** sauvegarder la donnée dans le cache et la retourner */
abstract function save($data);
/** supprimer le cache */
abstract function delete();
}

View File

@ -22,21 +22,21 @@ class CacheFile extends SharedFile {
else return new static($file, $data);
}
protected static function ensure_source($data, ?IDataCache &$source, bool $allowArray=true): bool {
if ($data === null || $data instanceof IDataCache) {
protected static function ensure_source($data, ?CacheData &$source, bool $allowArray=true): bool {
if ($data === null || $data instanceof CacheData) {
$source = $data;
} elseif (is_subclass_of($data, IDataCache::class)) {
} elseif (is_subclass_of($data, CacheData::class)) {
$source = new $data();
} elseif (func::is_callable($data)) {
$source = new DataCache(null, $data);
$source = new DataCacheData(null, $data);
} elseif (is_array($data) && $allowArray) {
return false;
} elseif (is_iterable($data)) {
$source = new DataCache(null, static function() use ($data) {
$source = new DataCacheData(null, static function() use ($data) {
yield from $data;
});
} else {
throw ValueException::invalid_type($source, IDataCache::class);
throw ValueException::invalid_type($source, CacheData::class);
}
return true;
}
@ -53,13 +53,8 @@ class CacheFile extends SharedFile {
$data ??= $params["data"] ?? null;
$this->sources = null;
if (self::ensure_source($data, $source)) {
if ($source !== null) {
$source->setDatafile($basefile);
$key = $source->getName();
} else {
$key = "";
}
$this->sources = [$key => $source];
if ($source !== null) $source->setDatafile($basefile);
$this->sources = ["" => $source];
} else {
$sources = [];
$index = 0;
@ -89,7 +84,7 @@ class CacheFile extends SharedFile {
protected bool $cacheNull;
/** @var ?IDataCache[] */
/** @var ?CacheData[] */
protected ?array $sources;
/**
@ -268,7 +263,7 @@ class CacheFile extends SharedFile {
$data = $source->load();
}
if ($this->shouldCache($data)) {
$source->save($data);
$data = $source->save($data);
} else {
# ne pas garder le fichier s'il ne faut pas mettre en cache
$source->delete();

36
src/cache/CursorCacheData.php vendored Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace nulib\cache;
class CursorCacheData extends CacheData {
function __construct(array $cursorId, $compute=null, ?CursorChannel $channel=null) {
$name = $cursorId["group_id"];
if ($name) $name .= "_";
$name .= $cursorId["id"];
parent::__construct($name, $compute);
$channel ??= (new CursorChannel($cursorId))->initStorage(cache::storage());
$this->channel = $channel;
}
function setDatafile(?string $basefile): void {
}
protected CursorChannel $channel;
function exists(): bool {
return $this->channel->count() > 0;
}
function load() {
return $this->channel;
}
function save($data) {
if (!is_iterable($data)) $data = [$data];
$this->channel->rechargeAll($data);
return $this->channel;
}
function delete() {
$this->channel->delete(null);
}
}

View File

@ -7,7 +7,7 @@ use nulib\os\path;
use nulib\php\func;
use Traversable;
class DataCache implements IDataCache {
class DataCacheData extends CacheData {
/** @var string identifiant de cette donnée */
const NAME = null;
@ -15,22 +15,14 @@ class DataCache implements IDataCache {
const COMPUTE = null;
function __construct(?string $name=null, $compute=null, ?string $basefile=null) {
$this->name = $name ?? static::NAME ?? "";
$this->compute = func::withn($compute ?? static::COMPUTE);
$name ??= static::NAME ?? "";
$compute ??= static::COMPUTE;
parent::__construct($name, $compute);
$this->setDatafile($basefile);
}
protected string $name;
function getName() : string {
return $this->name;
}
protected ?func $compute;
function compute() {
$compute = $this->compute;
$data = $compute !== null? $compute->invoke(): null;
$data = parent::compute();
if ($data instanceof Traversable) $data = cl::all($data);
return $data;
}
@ -56,8 +48,9 @@ class DataCache implements IDataCache {
return file::reader($this->datafile)->unserialize();
}
function save($data): void {
function save($data) {
file::writer($this->datafile)->serialize($data);
return $data;
}
function delete(): void {

View File

@ -1,27 +0,0 @@
<?php
namespace nulib\cache;
/**
* Interface IDataCache: gestion d'une donnée mise en cache
*/
interface IDataCache {
function getName(): string;
/** calculer la donnée */
function compute();
/** spécifier le chemin du cache à partir du fichier de base */
function setDatafile(?string $basefile): void;
/** indiquer si le cache existe */
function exists(): bool;
/** charger la donnée depuis le cache */
function load();
/** sauvegarder la donnée dans le cache */
function save($data): void;
/** supprimer le cache */
function delete();
}

19
src/cache/TODO.md vendored
View File

@ -1,21 +1,6 @@
# nulib\cache
* [ ] CacheChannel: stocker aussi la clé primaire, ce qui permet de récupérer la donnée correspondante dans la source
* Cache:
* une méthode calcule + stocke la valeur
* valeur scalaire: compute() puis écriture dans un fichier cache
* valeur itérable: compute() puis enregistre dans un channel
* une méthode récupère la valeur
* valeur scalaire: lecture du fichier cache
* valeur itérable: parcours des valeurs du channel
ainsi, il faudra peut-être deux classes: DataCache et CursorCache qui
implémentent ces deux fonctionnalités
* cache::get() utilise DataCache
* cache::all() utilise CursorCache
peut-être qu'on peut garder CacheFile et le fonctionnement ci-dessus est
implémenté dans CacheData? à voir
* [ ] CacheChannel: stocker aussi la clé primaire, ce qui permet de récupérer
la donnée correspondante dans la source?
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

12
src/cache/cache.php vendored
View File

@ -72,9 +72,9 @@ class cache {
return new CacheFile($file, $data, $params);
}
static function cache($dataId, callable $compute, ?array $params=null): CacheFile {
static function cache($dataId, $data, ?array $params=null): CacheFile {
self::verifix_id($dataId);
return self::new($dataId, null, $compute, $params);
return self::new($dataId, null, $data, $params);
}
static function get($dataId, $data, ?array $params=null) {
@ -84,10 +84,10 @@ class cache {
return $cache->get(null, $noCache);
}
static function all($cursorId, ?iterable $rows, ?array $params=null): iterable {
self::verifix_id($dataId);
$noCache = !self::should_cache($dataId["id"], $dataId["group_id"]);
$cache = self::new($cursorId, "_rows", new CursorCache($cursorId, $rows), $params);
static function all($cursorId, $rows, ?array $params=null): ?iterable {
self::verifix_id($cursorId);
$noCache = !self::should_cache($cursorId["id"], $cursorId["group_id"]);
$cache = self::new($cursorId, "_rows", new CursorCacheData($cursorId, $rows), $params);
return $cache->get(null, $noCache);
}
}

View File

@ -2,7 +2,7 @@
<?php
require __DIR__.'/../vendor/autoload.php';
use nulib\cache\DataCache;
use nulib\cache\DataCacheData;
use nulib\cache\CacheFile;
use nulib\ext\yaml;
use nulib\os\sh;
@ -43,7 +43,7 @@ if (in_array("one", $what)) {
}
if (in_array("two", $what)) {
$two = new CacheFile("two", new DataCache(null, function () {
$two = new CacheFile("two", new DataCacheData(null, function () {
return 2;
}), [
"duration" => $duration,
@ -52,11 +52,11 @@ if (in_array("two", $what)) {
}
if (in_array("three", $what)) {
$data31 = new DataCache("data31name", function () {
$data31 = new DataCacheData("data31name", function () {
return 31;
});
$data32 = new DataCache(null, function () {
$data32 = new DataCacheData(null, function () {
return 32;
});

View File

@ -1,66 +0,0 @@
<?php
namespace nulib\cache;
use nulib\db\Capacitor;
use nulib\db\sqlite\Sqlite;
use nulib\db\sqlite\SqliteStorage;
use nulib\output\msg;
use nulib\output\std\StdMessenger;
use nulib\tests\TestCase;
use Traversable;
class SourceTest extends TestCase {
static function setUpBeforeClass(): void {
msg::set_messenger_class(StdMessenger::class);
}
/** @param Traversable|iterable $dest */
protected static function assertSource($dest): void {
self::assertSame([
["pk" => 1, "s" => null, "i" => null, "b" => null],
["pk" => 2, "s" => "false", "i" => 0, "b" => 0],
["pk" => 3, "s" => "first", "i" => 1, "b" => 1],
["pk" => 4, "s" => "second", "i" => 2, "b" => 1],
], iterator_to_array($dest));
}
function testDirect() {
$destStorage = new SqliteStorage(new Sqlite(__DIR__.'/dest.db'));
new Capacitor($destStorage, $dest = new CursorChannel("source"));
$sourceDb = new SourceDb();
$dest->rechargeAll($sourceDb->all("select * from source"));
$this->assertSource($dest);
}
function testCache() {
$getSource = function() {
$db = new SourceDb();
msg::info("query source");
yield from $db->all("select * from source");
};
$params = [
"duration" => 2,
"override_duration" => true,
];
msg::info("initial");
cache::nc();
$dest = cache::all("source", $getSource(), $params);
$this->assertSource($dest);
msg::info("cached");
$dest = cache::all("source", $getSource(), $params);
$this->assertSource($dest);
sleep(4);
msg::info("expired");
$dest = cache::all("source", $getSource(), $params);
$this->assertSource($dest);
sleep(4);
msg::info("end");
}
}

View File

@ -21,38 +21,46 @@ class cacheTest extends _TestCase {
msg::note("fin gendata");
}
function _testRows(iterable $rows) {
function _testRows(iterable $rows, int $expectedCount) {
$count = 0;
foreach ($rows as $key => $row) {
msg::info("got $key => {a={$row["a"]}, b={$row["b"]}}");
$parts = ["got $key => {"];
$i = 0;
foreach ($row as $k => $v) {
if ($i++ > 0) $parts[] = ", ";
$parts[] = "$k=$v";
}
$parts[] = "}";
msg::info(implode("", $parts));
$count++;
}
self::assertSame(3, $count);
self::assertSame($expectedCount, $count);
}
function _testGet(string $dataId, callable $gencompute) {
function _testGet(string $dataId, int $expectedCount, callable $gencompute) {
msg::section($dataId);
cache::nc();
cache::nc(true, true);
msg::step("premier");
$rows = cache::get($dataId, $gencompute());
$this->_testRows($rows);
$this->_testRows($rows, $expectedCount);
msg::step("deuxième");
$rows = cache::get($dataId, $gencompute());
$this->_testRows($rows);
$this->_testRows($rows, $expectedCount);
msg::step("vider le cache");
cache::nc(true, true);
msg::step("premier");
$rows = cache::get($dataId, $gencompute());
$this->_testRows($rows);
$this->_testRows($rows, $expectedCount);
msg::step("deuxième");
$rows = cache::get($dataId, $gencompute());
$this->_testRows($rows);
$this->_testRows($rows, $expectedCount);
}
function testGetStatic() {
$this->_testGet("getStatic", function() {
$this->_testGet("getStatic", 3, function () {
return static function () {
msg::note("getdata");
return self::DATA;
@ -61,12 +69,40 @@ class cacheTest extends _TestCase {
}
function testGetGenerator() {
$this->_testGet("getGenerator", function() {
$this->_testGet("getGenerator", 3, function () {
return $this->gendata();
});
}
function testAll() {
function _testAll(string $cursorId, int $expectedCount, callable $gencompute) {
msg::section($cursorId);
cache::nc(true, true);
msg::step("premier");
$rows = cache::all($cursorId, $gencompute());
$this->_testRows($rows, $expectedCount);
msg::step("deuxième");
$rows = cache::all($cursorId, $gencompute());
$this->_testRows($rows, $expectedCount);
msg::step("vider le cache");
cache::nc(true, true);
msg::step("premier");
$rows = cache::all($cursorId, $gencompute());
$this->_testRows($rows, $expectedCount);
msg::step("deuxième");
$rows = cache::all($cursorId, $gencompute());
$this->_testRows($rows, $expectedCount);
}
function testAllGenerator() {
$this->_testAll("allGenerator", 4, function() {
return static function() {
$db = new SourceDb();
msg::note("query source");
yield from $db->all("select * from source");
};
});
}
}