modifs.mineures sans commentaires
This commit is contained in:
parent
36bae9ef7d
commit
c9c40644eb
46
src/cache/CacheData.php
vendored
Normal file
46
src/cache/CacheData.php
vendored
Normal 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();
|
||||
}
|
25
src/cache/CacheFile.php
vendored
25
src/cache/CacheFile.php
vendored
@ -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
36
src/cache/CursorCacheData.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
@ -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 {
|
27
src/cache/IDataCache.php
vendored
27
src/cache/IDataCache.php
vendored
@ -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
19
src/cache/TODO.md
vendored
@ -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
12
src/cache/cache.php
vendored
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
});
|
||||
|
||||
|
66
tests/cache/SourceTest.php
vendored
66
tests/cache/SourceTest.php
vendored
@ -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");
|
||||
}
|
||||
}
|
62
tests/cache/cacheTest.php
vendored
62
tests/cache/cacheTest.php
vendored
@ -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");
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user