modifs.mineures sans commentaires
This commit is contained in:
		
							parent
							
								
									df13496a86
								
							
						
					
					
						commit
						79b8c33d4f
					
				
							
								
								
									
										173
									
								
								src/cache/CacheChannel.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										173
									
								
								src/cache/CacheChannel.php
									
									
									
									
										vendored
									
									
								
							| @ -1,116 +1,127 @@ | |||||||
| <?php | <?php | ||||||
| namespace nulib\cache; | namespace nulib\cache; | ||||||
| 
 | 
 | ||||||
|  | use IteratorAggregate; | ||||||
| use nulib\cl; | use nulib\cl; | ||||||
| use nulib\db\CapacitorChannel; | use nulib\db\CapacitorChannel; | ||||||
| use nulib\php\time\DateTime; | use nulib\db\CapacitorStorage; | ||||||
| use nulib\php\time\Delay; | use nulib\ext\utils; | ||||||
|  | use nulib\php\func; | ||||||
|  | use Traversable; | ||||||
| 
 | 
 | ||||||
| class CacheChannel extends CapacitorChannel { | class CacheChannel extends CapacitorChannel implements IteratorAggregate { | ||||||
|   /** @var int durée de vie par défaut du cache */ |   const NAME = "cache"; | ||||||
|   const DURATION = "1D"; // jusqu'au lendemain
 |   const TABLE_NAME = "cache"; | ||||||
| 
 |  | ||||||
|   const INCLUDES = null; |  | ||||||
| 
 |  | ||||||
|   const EXCLUDES = null; |  | ||||||
| 
 | 
 | ||||||
|   const COLUMN_DEFINITIONS = [ |   const COLUMN_DEFINITIONS = [ | ||||||
|     "group_id" => "varchar(64) not null", |     "group_id" => "varchar(32) not null", // groupe de curseur
 | ||||||
|     "id" => "varchar(64) not null", |     "id" => "varchar(128) not null", // nom du curseur
 | ||||||
|     "date_start" => "datetime", |     "key_index" => "integer not null", | ||||||
|     "duration_" => "text", |     "key" => "varchar(128) not null", | ||||||
|     "primary key (group_id, id)", |     "search" => "varchar(255)", | ||||||
|  | 
 | ||||||
|  |     "primary key (group_id, id, key_index)", | ||||||
|   ]; |   ]; | ||||||
| 
 | 
 | ||||||
|   static function get_cache_ids($id): array { |   static function with(CapacitorStorage $storage, ?iterable $rows=null, $cursorId=null): self { | ||||||
|     if (is_array($id)) { |     $channel = (new static($cursorId))->initStorage($storage); | ||||||
|       $keys = array_keys($id); |     if ($rows !== null) $channel->build($rows); | ||||||
|       if (array_key_exists("group_id", $id)) $groupIdKey = "group_id"; |     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; |       else $groupIdKey = $keys[1] ?? null; | ||||||
|       $groupId = $id[$groupIdKey] ?? ""; |       $groupId = strval($cursorId[$groupIdKey] ?? ""); | ||||||
|       if (array_key_exists("id", $id)) $idKey = "id"; |       if (array_key_exists("id", $cursorId)) $idKey = "id"; | ||||||
|       else $idKey = $keys[0] ?? null; |       else $idKey = $keys[0] ?? null; | ||||||
|       $id = $id[$idKey] ?? ""; |       $id = strval($cursorId[$idKey] ?? ""); | ||||||
|     } else { |     } else { | ||||||
|       $groupId = ""; |       $groupId = ""; | ||||||
|  |       $id = strval($cursorId); | ||||||
|     } |     } | ||||||
|     if (preg_match('/^(.*\\\\)?([^\\\\]+)$/', $groupId, $ms)) { |     # si le groupe ou le nom sont trop grand, en faire un hash
 | ||||||
|       # si le groupe est une classe, faire un hash du package pour limiter la
 |     if (strlen($groupId) > 32) { | ||||||
|       # longueur du groupe
 |       $groupId = md5($groupId); | ||||||
|       [$package, $groupId] = [$ms[1], $ms[2]]; |  | ||||||
|       $package = substr(md5($package), 0, 4); |  | ||||||
|       $groupId = "${groupId}_$package"; |  | ||||||
|     } |     } | ||||||
|     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); |    * @param array|string $cursorId | ||||||
|     $this->duration = $duration ?? static::DURATION; |    */ | ||||||
|     $this->includes = static::INCLUDES; |   function __construct($cursorId) { | ||||||
|     $this->excludes = static::EXCLUDES; |     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 { |   protected int $index = 0; | ||||||
|     return cl::merge(self::get_cache_ids($item), [ | 
 | ||||||
|       "item" => null, |   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 { |   function reset(bool $recreate=false): void { | ||||||
|     $now = new DateTime(); |     $this->index = 0; | ||||||
|     $duration ??= $this->duration; |     parent::reset($recreate); | ||||||
|     return [ |  | ||||||
|       "date_start" => $now, |  | ||||||
|       "duration" => new Delay($duration, $now), |  | ||||||
|     ]; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function onUpdate($item, array $row, array $prow, ?string $duration=null): ?array { |   function chargeAll(?iterable $items, $func=null, ?array $args=null): int { | ||||||
|     $now = new DateTime(); |     $this->index = 0; | ||||||
|     $duration ??= $this->duration; |     if ($items === null) return 0; | ||||||
|     return [ |     $count = 0; | ||||||
|       "date_start" => $now, |     if ($func !== null) $func = func::with($func, $args)->bind($this); | ||||||
|       "duration" => new Delay($duration, $now), |     foreach ($items as $key => $item) { | ||||||
|     ]; |       $count += $this->charge($item, $func, [$key]); | ||||||
|  |     } | ||||||
|  |     return $count; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function shouldUpdate($id, bool $noCache=false): bool { |   function build(?iterable $items): self { | ||||||
|     if ($noCache) return true; |     $this->reset(true); | ||||||
| 
 |     $this->chargeAll($items); | ||||||
|     $cacheIds = self::get_cache_ids($id); |     return $this; | ||||||
|     $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; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|     $found = false; |   function getIterator(): Traversable { | ||||||
|     $expired = false; |     $rows = $this->dbAll([ | ||||||
|     $this->each($cacheIds, |       "cols" => ["key", "item__"], | ||||||
|       function($row) use (&$found, &$expired) { |       "where" => $this->getBaseFilter(), | ||||||
|         $found = true; |     ]); | ||||||
|         $expired = $row["duration"]->isElapsed(); |     foreach ($rows as $row) { | ||||||
|       }); |       $key = $row["key"]; | ||||||
|     return !$found || $expired; |       $item = $this->unserialize($row["item__"]); | ||||||
|  |       yield $key => $item; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|   function setCached($id, ?string $duration=null): void { |  | ||||||
|     $cacheIds = self::get_cache_ids($id); |  | ||||||
|     $this->charge($cacheIds, null, [$duration]); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   function resetCached($id) { |  | ||||||
|     $cacheIds = self::get_cache_ids($id); |  | ||||||
|     $this->delete($cacheIds); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										53
									
								
								src/cache/CacheDataChannel.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										53
									
								
								src/cache/CacheDataChannel.php
									
									
									
									
										vendored
									
									
								
							| @ -1,53 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace nulib\cache; |  | ||||||
| 
 |  | ||||||
| use Closure; |  | ||||||
| use IteratorAggregate; |  | ||||||
| use nulib\cache\CacheChannel; |  | ||||||
| use nulib\cache\cache; |  | ||||||
| use nulib\cl; |  | ||||||
| use nulib\db\CapacitorChannel; |  | ||||||
| use Traversable; |  | ||||||
| 
 |  | ||||||
| class CacheDataChannel extends CapacitorChannel implements IteratorAggregate { |  | ||||||
|   const COLUMN_DEFINITIONS = [ |  | ||||||
|     "key" => "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); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										6
									
								
								src/cache/CacheFile.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								src/cache/CacheFile.php
									
									
									
									
										vendored
									
									
								
							| @ -3,6 +3,7 @@ namespace nulib\cache; | |||||||
| 
 | 
 | ||||||
| use Exception; | use Exception; | ||||||
| use nulib\cv; | use nulib\cv; | ||||||
|  | use nulib\ext\utils; | ||||||
| use nulib\file; | use nulib\file; | ||||||
| use nulib\file\SharedFile; | use nulib\file\SharedFile; | ||||||
| use nulib\os\path; | use nulib\os\path; | ||||||
| @ -26,10 +27,7 @@ class CacheFile extends SharedFile { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function __construct($file, $data=null, ?array $params=null) { |   function __construct($file, $data=null, ?array $params=null) { | ||||||
|     if ($file === null) { |     $file ??= path::join(sys_get_temp_dir(), utils::uuidgen()); | ||||||
|       $rand = bin2hex(random_bytes(8)); |  | ||||||
|       $file = path::join(sys_get_temp_dir(), $rand); |  | ||||||
|     } |  | ||||||
|     $file = path::ensure_ext($file, self::EXT); |     $file = path::ensure_ext($file, self::EXT); | ||||||
|     $this->basedir = path::dirname($file); |     $this->basedir = path::dirname($file); | ||||||
|     $basename = path::filename($file); |     $basename = path::filename($file); | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								src/cache/TODO.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/cache/TODO.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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 | ||||||
							
								
								
									
										37
									
								
								src/cache/cache.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								src/cache/cache.php
									
									
									
									
										vendored
									
									
								
							| @ -2,11 +2,9 @@ | |||||||
| namespace nulib\cache; | namespace nulib\cache; | ||||||
| 
 | 
 | ||||||
| use nulib\app; | use nulib\app; | ||||||
| use nulib\cache\CacheChannel; |  | ||||||
| use nulib\cache\CacheDataChannel; |  | ||||||
| use nulib\db\Capacitor; |  | ||||||
| use nulib\db\CapacitorStorage; | use nulib\db\CapacitorStorage; | ||||||
| use nulib\db\sqlite\SqliteStorage; | use nulib\db\sqlite\SqliteStorage; | ||||||
|  | use nulib\php\func; | ||||||
| 
 | 
 | ||||||
| class cache { | class cache { | ||||||
|   protected static ?string $dbfile = null; |   protected static ?string $dbfile = null; | ||||||
| @ -17,30 +15,21 @@ class cache { | |||||||
| 
 | 
 | ||||||
|   protected static ?CapacitorStorage $storage = null; |   protected static ?CapacitorStorage $storage = null; | ||||||
| 
 | 
 | ||||||
|  |   protected static function storage(): CapacitorStorage { | ||||||
|  |     return self::$storage ??= new SqliteStorage(self::dbfile()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   static function set_storage(CapacitorStorage $storage): CapacitorStorage { |   static function set_storage(CapacitorStorage $storage): CapacitorStorage { | ||||||
|     return self::$storage = $storage; |     return self::$storage = $storage; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   protected static function get_storage(): CapacitorStorage { |   static function all(?iterable $rows, $cursorId=null): iterable { | ||||||
|     return self::$storage ??= new SqliteStorage(self::dbfile()); |     CacheChannel::verifix_id($cursorId); | ||||||
|   } |     $file = "row-{$cursorId["group_id"]}-{$cursorId["id"]}"; | ||||||
| 
 |     $cache = new CacheFile($file, function() use ($rows, $cursorId) { | ||||||
|   protected static ?CacheChannel $channel = null; |       CacheChannel::with(self::storage(), null, $cursorId)->build($rows); | ||||||
| 
 |       return $cursorId; | ||||||
|   static function set(?CacheChannel $channel): CacheChannel { |     }); | ||||||
|     $channel ??= new CacheChannel(); |     return CacheChannel::with(self::storage(), null, $cache->get()); | ||||||
|     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; |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								tests/cache/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/cache/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | /capacitor.db* | ||||||
|  | /*.cache | ||||||
							
								
								
									
										22
									
								
								tests/cache/CursorChannelTest.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tests/cache/CursorChannelTest.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | <?php | ||||||
|  | namespace nulib\cache; | ||||||
|  | 
 | ||||||
|  | use nulib\output\msg; | ||||||
|  | 
 | ||||||
|  | class CursorChannelTest extends _TestCase { | ||||||
|  |   function testUsage() { | ||||||
|  |     $data = [ | ||||||
|  |       "fr" => ["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); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								tests/cache/_TestCase.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tests/cache/_TestCase.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | <?php | ||||||
|  | namespace nulib\cache; | ||||||
|  | 
 | ||||||
|  | use nulib\db\sqlite\SqliteStorage; | ||||||
|  | use nulib\output\msg; | ||||||
|  | use nulib\output\std\StdMessenger; | ||||||
|  | use nulib\tests\TestCase; | ||||||
|  | 
 | ||||||
|  | class _TestCase extends TestCase { | ||||||
|  |   protected static SqliteStorage $storage; | ||||||
|  | 
 | ||||||
|  |   static function setUpBeforeClass(): void { | ||||||
|  |     parent::setUpBeforeClass(); | ||||||
|  |     msg::set_messenger_class(StdMessenger::class); | ||||||
|  |     self::$storage = new SqliteStorage(__DIR__."/capacitor.db"); | ||||||
|  |     cache::set_storage(self::$storage); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static function tearDownAfterClass(): void { | ||||||
|  |     parent::tearDownAfterClass(); | ||||||
|  |     self::$storage->close(); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								tests/cache/cacheTest.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								tests/cache/cacheTest.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | <?php | ||||||
|  | namespace nulib\cache; | ||||||
|  | 
 | ||||||
|  | use nulib\output\msg; | ||||||
|  | 
 | ||||||
|  | class cacheTest extends _TestCase { | ||||||
|  |   function gendata() { | ||||||
|  |     $data = [ | ||||||
|  |       "fr" => ["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); | ||||||
|  |   } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user