Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cb332b4
still a lot of work to be done on PSR-6 part
llegaz Oct 10, 2025
c2f2fc4
getPoolName rework
llegaz Oct 15, 2025
a73144b
exceptions refactored a bit
llegaz Oct 16, 2025
b434f47
cs and fix getItems
llegaz Oct 16, 2025
df9bdb5
get has and delete OK (for now)
llegaz Oct 16, 2025
550f077
has function rly OK now, but could still be enhanced though adapter p…
llegaz Oct 16, 2025
e6ab302
get functions OK
llegaz Oct 17, 2025
7052b42
fix deleteFromPool, refacto, todo= isValueSet + use serialize with Re…
llegaz Oct 17, 2025
764e799
ofc this isn't good at it is, still in WIP
llegaz Oct 17, 2025
1098c08
removing of old unused funcs as it will be merge into storing and fet…
llegaz Oct 18, 2025
41ff671
no real diffs
llegaz Oct 18, 2025
21c0550
store to pool ~ v1
llegaz Oct 18, 2025
2df1df2
fetch from pool ~ v1
llegaz Oct 18, 2025
076845a
important refacto, PSR16 ok but will have to take back all PSR6 part,…
llegaz Oct 18, 2025
522a821
minor fixes
llegaz Oct 18, 2025
76f1654
delete from pool
llegaz Oct 19, 2025
d3e839e
episode 2 almost done ? todo: fix errors, implement expiration
llegaz Oct 19, 2025
c8de90c
some review annotations added
llegaz Oct 19, 2025
a717541
Tests state added
llegaz Oct 25, 2025
3bb6e40
set expiration on entire pool
llegaz Oct 25, 2025
b662f2d
wip on deferred items and expiration
llegaz Oct 28, 2025
937a780
Almost there indy! On deferred items and refacto
llegaz Oct 28, 2025
001c712
todo: testExpiresAt, testHasItemReturnsFalseWhenDeferredItemIsExpired…
llegaz Oct 29, 2025
b73266a
big fix on expireAt, remember this expiration process implementation …
llegaz Oct 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .phpunit_full
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<?php
/**
* @todo rework tests suite
*/
require_once './.phpunit';
define('SKIP_FUNCTIONAL_TESTS', false);
define('SKIP_INTEGRATION_TESTS', false);
define('LLEGAZ_DEBUG', true);
define('LLEGAZ_DEBUG', true);
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"puf":"@phpunit-func-suite",
"cs":"@phpcsfixer",
"test": "./vendor/bin/phpunit --display-deprecations --display-notices --display-warnings --colors=always --configuration ./phpunit.xml --bootstrap .phpunit_full",
"test-only": "./vendor/bin/phpunit --display-deprecations --display-notices --display-warnings --colors=always --configuration ./phpunit.xml --bootstrap .phpunit_full --filter PoolIntegrationTest::testGetItems",
"test-only": "./vendor/bin/phpunit --display-deprecations --display-notices --display-warnings --colors=always --configuration ./phpunit.xml --bootstrap .phpunit_full --filter PoolIntegrationTest::testExpire",
"test-psr16": "./vendor/bin/phpunit --display-deprecations --display-notices --display-warnings --colors=always --configuration ./phpunit.xml --bootstrap .phpunit_full --filter CacheIntegrationTest",
"test-psr6": "./vendor/bin/phpunit --display-deprecations --display-notices --display-warnings --colors=always --configuration ./phpunit.xml --bootstrap .phpunit_full --filter PoolIntegrationTest",
"phpunit" : "./vendor/bin/phpunit --colors=always --configuration ./phpunit.xml --testsuite unit",
Expand Down
41 changes: 20 additions & 21 deletions src/CacheEntry/CacheEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@

namespace LLegaz\Cache\Entry;

use LLegaz\Cache\RedisCache;
use LLegaz\Cache\Utils;

class CacheEntry extends AbstractCacheEntry
{
private string $key;
private mixed $value = null;
private bool $isHit = false;
private ?int $ttl;
private bool $isTimeStamp;

private ?int $ttl = -1;

public function __construct(string $key)
{
Expand All @@ -28,11 +27,17 @@ public function __construct(string $key)
*/
public function expiresAfter(int|\DateInterval|null $time): static
{
// hexpire
if ($time instanceof \DateInterval) {
$this->ttl = Utils::dateIntervalToSeconds($time);
} elseif (is_int($time)) {
if ($time <= 0) {
$this->ttl = 0;
} else {
$this->ttl = $time;
}
} else {
$this->ttl = $time;
// null case
$this->ttl = RedisCache::DAY_EXPIRATION_TIME; // 24h
}

$this->isTimeStamp = false;
Expand All @@ -42,9 +47,16 @@ public function expiresAfter(int|\DateInterval|null $time): static

public function expiresAt(?\DateTimeInterface $expiration): static
{
// hexpireat
$this->ttl = $expiration->getTimestamp();
$this->isTimeStamp = true;
if ($expiration) {
if (Utils::getNow()->diff($expiration)->invert > 0) {
$this->ttl = 0;
} else {
$this->ttl = $expiration->getTimestamp();
}
} else {
// null case
$this->ttl = RedisCache::DAY_EXPIRATION_TIME; // 24h
}

return $this;
}
Expand Down Expand Up @@ -83,19 +95,6 @@ public function set($value): static
return $this;
}

/**
* if true = expireAt
*
*
* this bool seems useless @todo : remove it
*
* @return bool
*/
public function isTimeStamp(): bool
{
return $this->isTimeStamp;
}

/**
* return TTL in seconds OR unix timestamp
*
Expand Down
131 changes: 100 additions & 31 deletions src/CacheEntryPool/CacheEntryPool.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class CacheEntryPool implements CacheItemPoolInterface
*/
private array $deferredItems = [];

protected const HASH_DB_PREFIX = 'DEFAULT_Cache_Pool';
protected const HASH_DB_PREFIX = 'Cache_Pool';

/**
* @todo use Psr\SimpleCache\CacheInterface ?
Expand All @@ -63,6 +63,11 @@ public function __construct(RedisEnhancedCache $cache, ?string $pool = null)
$this->poolName = $this->getPoolName($pool ?? '');
}

public function __destruct()
{
$this->commit();
}

/**
* Deletes all items in the pool.
*
Expand All @@ -71,9 +76,14 @@ public function __construct(RedisEnhancedCache $cache, ?string $pool = null)
*/
public function clear(): bool
{
$e = new \Exception();
dump('pool cleared by : ', $e->getTrace()[1]['function']);

try {
$this->cache->set($this->poolName, null);
$this->cache->delete($this->poolName);
unset($this->deferredItems);
$this->deferredItems = [];
} catch (Exception $e) {
return false;
}
Expand All @@ -83,16 +93,25 @@ public function clear(): bool

public function deleteItem(string $key): bool
{
if ($this->isDeferred($key)) {
unset($this->deferredItems[$key]);

return $this->isDeferred($key);
}

return $this->deleteItems([$key]);

}

public function deleteItems(array $keys): bool
{
$bln = $this->cache->deleteFromPool($keys, $this->poolName);
dump('deleteItems', $keys, $bln);
foreach ($keys as $key) {
if ($this->isDeferred($key)) {
unset($this->deferredItems[$key]);
}
}

return $bln;/*$this->cache->deleteFromPool($keys, $this->poolName);*/
return $this->cache->deleteFromPool($keys, $this->poolName);

}

Expand All @@ -114,15 +133,21 @@ public function deleteItems(array $keys): bool
*/
public function getItem(string $key): CacheItemInterface
{
$value = $this->cache->fetchFromPool($key, $this->poolName);
//dump('getItem', $key, $value);
/** @todo handle hit, ttl */
$item = new CacheEntry($key);
if ($this->exist($value)) {
$item->set($value);
/** @todo handle hit, ttl, refacto */
if ($this->isDeferred($key)) {
$item = clone $this->deferredItems[$key];
$item->hit();
} else {
$value = $this->cache->fetchFromPool($key, $this->poolName);
$item = new CacheEntry($key);
if ($this->cache->exist($value)) {
$item->set($value);
$item->hit();
}
}

dump('getItem: ' . $item->getKey(). ' - TTL= ' . $item->getTTL() . ' - ' . (is_string($item->get()) ? $item->get() : print_r($item->get())));
dump($item);
return $item;

}
Expand All @@ -149,15 +174,20 @@ public function getItems(array $keys = []): iterable
$values = $this->cache->fetchFromPool($keys, $this->poolName);
//dump($values);
foreach ($keys as $key) {
/** @todo handle hit, ttl */
$item = new CacheEntry($key);
if (isset($values[$key]) && $this->exist($values[$key])) {
$item->set($values[$key]);
/** @todo handle hit, ttl and refacto */
if ($this->isDeferred($key)) {
$item = clone $this->deferredItems[$key];
$item->hit();
} else {
$item = new CacheEntry($key);
if (isset($values[$key]) && $this->cache->exist($values[$key])) {
$item->set($values[$key]);
$item->hit();
}
}
//$items[] = $item;
/**
* because of his code (wrong)
* because of this code (that is wrong IMHO)
* https://github.com/php-cache/integration-tests/blob/fb7d78718f1e5bbfd7c63e5c5734000999ac7366/src/CachePoolTest.php#L208C40-L208C44
*/
$items[$key] = $item;
Expand Down Expand Up @@ -185,13 +215,17 @@ public function getItems(array $keys = []): iterable
*/
public function hasItem(string $key): bool
{
return $this->cache->hasInPool($key, $this->poolName);
return isset($this->deferredItems[$key]) || $this->cache->hasInPool($key, $this->poolName);

}

/**
* Persists a cache item immediately.
*
* <b>CRITICAL</b>: if an expiration time, a ttl or expiration date is set, THEN the
* <b>ENTIRE pool </b> will be expired ! Thus deleting all the pool's key values (hash fields).
* @caution @warning
*
* @param CacheItemInterface $item
* The cache item to save.
*
Expand All @@ -200,8 +234,24 @@ public function hasItem(string $key): bool
*/
public function save(CacheItemInterface $item): bool
{
//dump("save", $item);
return $this->cache->storeToPool([$item->getKey() => $item->get()], $this->poolName);
dump('savi,ng', $this->isExpired($item), $item);
if ($this->isExpired($item)) {
return false;
}

dump('save', $item);
$bln = $this->cache->storeToPool([$item->getKey() => $item->get()], $this->poolName);
if ($bln && $item instanceof CacheEntry && $item->getTTL() > 0) {
/**
* /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\
* /!\ expires entire pool! /!\
* /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\
*/
//dump('/!\ expires entire pool! /!\\', $this->poolName, $item->getTTL());
$bln = $this->cache->setHsetPoolExpiration($this->poolName, $item->getTTL());
}

return $bln;
}

/**
Expand All @@ -215,9 +265,15 @@ public function save(CacheItemInterface $item): bool
*/
public function saveDeferred(CacheItemInterface $item): bool
{
$this->deferredItems[] = $item;
if (!$this->isExpired($item)) {
$this->deferredItems[$item->getKey()] = clone $item;

return true;
return true;
} elseif (isset($this->deferredItems[$item->getKey()])) {
unset($this->deferredItems[$item->getKey()]);
}

return false;
}

/**
Expand All @@ -228,17 +284,29 @@ public function saveDeferred(CacheItemInterface $item): bool
*/
public function commit(): bool
{
$deferred = [];
foreach ($this->deferredItems as $item) {
$deferred[$item->getKey()] = $item->get();
if (!count($this->deferredItems)) {

return true;
}

foreach ($this->deferredItems as $key => $item) {
if (!$this->isExpired($item)) {
$deferred[$key] = $item->get();
}
unset($this->deferredItems[$key]);
}
$this->deferredItems = [];

return $this->cache->storeToPool($deferred);
return $this->cache->storeToPool($deferred, $this->poolName);
}

public function printCachePool(): string
{
return $this->cache->printCacheHash($this->poolName);
}

/**
*
* @todo may be rework this a bit (DEFAULT everywhere lol)
*
* @param mixed $poolSuffix
* @return string
Expand All @@ -247,16 +315,17 @@ protected function getPoolName(string $poolSuffix): string
{
return strlen($poolSuffix) ?
self::HASH_DB_PREFIX . "_{$poolSuffix}" :
self::HASH_DB_PREFIX
'DEFAULT_' . self::HASH_DB_PREFIX
;
}

private function exist(mixed $value): bool
private function isExpired(CacheEntry $item): bool
{
if (is_string($value) && strlen($value) && $value === RedisEnhancedCache::DOES_NOT_EXIST) {
return false;
}
return !($item instanceof CacheEntry) || $item->getTTL() === 0;
}

return true;
private function isDeferred(string $key): bool
{
return isset($this->deferredItems[$key]) && ($this->deferredItems[$key] instanceof CacheEntry);
}
}
10 changes: 6 additions & 4 deletions src/Exception/InvalidArgumentException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

namespace LLegaz\Cache\Exception;

use Psr\Cache\InvalidArgumentException as Psr6CacheInterface;
use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInterface;
use Psr\Cache\InvalidArgumentException as Psr6InvalidArgumentException;
use Psr\SimpleCache\InvalidArgumentException as Psr16InvalidArgumentException;

/**
* inspired from Symfony Cache (by Nicolas Grekas)
*
* @todo rework all package exceptions ?
* PSR-6 and PSR-16 <b>InvalidArgumentException</b>
*
* @todo rework / clean all package(s) exceptions ?
*/
class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface
class InvalidArgumentException extends \InvalidArgumentException implements Psr6InvalidArgumentException, Psr16InvalidArgumentException
{
}
8 changes: 5 additions & 3 deletions src/Exception/InvalidKeyException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

namespace LLegaz\Cache\Exception;

use Psr\SimpleCache\InvalidArgumentException;

class InvalidKeyException extends \Exception implements InvalidArgumentException
/**
*
* PSR-6 and PSR-16 <b>InvalidArgumentException</b>
*/
class InvalidKeyException extends InvalidArgumentException
{
public function __construct(string $message = 'RedisCache says "Can\'t do shit with this Key"' . PHP_EOL, int $code = 400, \Throwable $previous = null)
{
Expand Down
8 changes: 5 additions & 3 deletions src/Exception/InvalidKeysException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

namespace LLegaz\Cache\Exception;

use Psr\SimpleCache\InvalidArgumentException;

class InvalidKeysException extends \Exception implements InvalidArgumentException
/**
*
* PSR-6 and PSR-16 <b>InvalidArgumentException</b>
*/
class InvalidKeysException extends InvalidArgumentException
{
public function __construct(string $message = 'RedisCache says "Can\'t do shit with those keys"' . PHP_EOL, int $code = 400, \Throwable $previous = null)
{
Expand Down
Loading