initial commit; version 22.5.12042

This commit is contained in:
2022-12-12 23:28:25 -05:00
commit af1b03d79f
17653 changed files with 22692970 additions and 0 deletions

View File

@ -0,0 +1,28 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster;
use Psr\Cache\CacheItemPoolInterface;
/**
* Interface ClusterInterface Aggregatable
*
* @package Phpfastcache\Cluster
*/
interface AggregatablePoolInterface extends CacheItemPoolInterface
{
}

View File

@ -0,0 +1,105 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster;
use Phpfastcache\Config\ConfigurationOption;
/**
* Interface AggregatorInterface
*
* @package Phpfastcache\Cluster
*/
interface AggregatorInterface
{
/**
* Full replication mechanism
*
* Read on first working (and synchronize if needed, no failure allowed),
* Write on all (no failure allowed),
* Delete on all (no failure allowed)
*
* Conflict on multiple reads: Keep first found item (but sync the others)
* Cluster size: 2 minimum, unlimited
*/
public const STRATEGY_FULL_REPLICATION = 1;
/**
* Semi replication mechanism
*
* Read first working (but do not synchronize, with partial failure allowed),
* Write on all (with partial failure allowed)
* Delete on all (with partial failure allowed)
*
* Conflict on multiple reads: Keep first found item
* Cluster size: 2 minimum, unlimited
*/
public const STRATEGY_SEMI_REPLICATION = 2;
/**
* First pool is master, second is slave
*
* Read from master (but do not synchronize, with master failure only allowed)
* Write on all (with master failure only allowed)
* Delete on all (with master failure only allowed)
*
* Conflict on multiple reads: No, master is exclusive source except if it fails
* Cluster size: 2 exactly: Master & Slave (Exception if more or less)
*/
public const STRATEGY_MASTER_SLAVE = 4;
/**
* Mostly used for development testing
*
* CRUD operations are made on a random-chosen backend from a given cluster.
* This means you have 1 chance out of (n count of pools) to find an existing cache item
* but also to write/delete an non-existing item.
*/
public const STRATEGY_RANDOM_REPLICATION = 8;
/**
* AggregatorInterface constructor.
*
* @param string $clusterAggregatorName
* @param AggregatablePoolInterface ...$driverPools
*/
public function __construct(string $clusterAggregatorName, AggregatablePoolInterface ...$driverPools);
/**
* @param int $strategy
*
* @return ClusterPoolInterface
*/
public function getCluster(int $strategy): ClusterPoolInterface;
/**
* @param string $driverName
* @param ConfigurationOption|NULL $driverConfig
*
* @return void
*/
public function aggregateNewDriver(string $driverName, ConfigurationOption $driverConfig = null): void;
/**
* @param AggregatablePoolInterface $driverPool
*/
public function aggregateDriver(AggregatablePoolInterface $driverPool): void;
/**
* @param AggregatablePoolInterface $driverPool
*/
public function disaggregateDriver(AggregatablePoolInterface $driverPool): void;
}

View File

@ -0,0 +1,174 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster;
use Exception;
use Phpfastcache\CacheManager;
use Phpfastcache\Config\ConfigurationOption;
use Phpfastcache\Exceptions\PhpfastcacheDriverCheckException;
use Phpfastcache\Exceptions\PhpfastcacheDriverException;
use Phpfastcache\Exceptions\PhpfastcacheDriverNotFoundException;
use Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException;
use Phpfastcache\Exceptions\PhpfastcacheInvalidConfigurationException;
use Phpfastcache\Exceptions\PhpfastcacheLogicException;
use ReflectionException;
use stdClass;
/**
* Class ClusterAggregator
*
* @package Phpfastcache\Cluster
*/
class ClusterAggregator implements AggregatorInterface
{
protected $driverPools;
/**
* @var ClusterPoolInterface
*/
protected $cluster;
/**
* @var string
*/
protected $clusterAggregatorName;
/**
* ClusterAggregator constructor.
* @param string $clusterAggregatorName
* @param AggregatablePoolInterface ...$driverPools
* @throws PhpfastcacheLogicException
*/
public function __construct(string $clusterAggregatorName = '', AggregatablePoolInterface ...$driverPools)
{
$clusterAggregatorName = trim($clusterAggregatorName);
if (empty($clusterAggregatorName)) {
try {
$clusterAggregatorName = 'cluster_' . \bin2hex(\random_bytes(15));
} catch (Exception $e) {
$clusterAggregatorName = 'cluster_' . \str_shuffle(\spl_object_hash(new stdClass()));
}
}
$this->clusterAggregatorName = $clusterAggregatorName;
foreach ($driverPools as $driverPool) {
$this->aggregateDriver($driverPool);
}
}
/**
* @param AggregatablePoolInterface $driverPool
*
* @throws PhpfastcacheLogicException
*/
public function aggregateDriver(AggregatablePoolInterface $driverPool): void
{
if ($this->cluster) {
throw new PhpfastcacheLogicException('The cluster has been already build, cannot aggregate more pools.');
}
$splHash = \spl_object_hash($driverPool);
if (!isset($this->driverPools[$splHash])) {
if ($driverPool instanceof ClusterPoolInterface) {
throw new PhpfastcacheLogicException('Recursive cluster aggregation is not allowed !');
}
$this->driverPools[$splHash] = $driverPool;
} else {
throw new PhpfastcacheLogicException('This pool has been already aggregated !');
}
}
/**
* @param string $driverName
* @param ConfigurationOption|null $driverConfig
* @throws PhpfastcacheDriverCheckException
* @throws PhpfastcacheDriverException
* @throws PhpfastcacheDriverNotFoundException
* @throws PhpfastcacheInvalidArgumentException
* @throws PhpfastcacheInvalidConfigurationException
* @throws PhpfastcacheLogicException
* @throws ReflectionException
*/
public function aggregateNewDriver(string $driverName, ConfigurationOption $driverConfig = null): void
{
if ($this->cluster) {
throw new PhpfastcacheLogicException('The cluster has been already build, cannot aggregate more pools.');
}
$this->aggregateDriver(
CacheManager::getInstance($driverName, $driverConfig)
);
}
/**
* @param AggregatablePoolInterface $driverPool
*
* @throws PhpfastcacheLogicException
*/
public function disaggregateDriver(AggregatablePoolInterface $driverPool): void
{
if ($this->cluster) {
throw new PhpfastcacheLogicException('The cluster has been already build, cannot disaggregate pools.');
}
$splHash = \spl_object_hash($driverPool);
if (isset($this->driverPools[$splHash])) {
unset($this->driverPools[$splHash]);
} else {
throw new PhpfastcacheLogicException('This pool was not aggregated !');
}
}
/**
* @param int $strategy
*
* @return ClusterPoolInterface
* @throws PhpfastcacheInvalidArgumentException
*/
public function getCluster(int $strategy = AggregatorInterface::STRATEGY_FULL_REPLICATION): ClusterPoolInterface
{
if (isset(ClusterPoolAbstract::STRATEGY[$strategy])) {
if (!$this->cluster) {
$clusterClass = ClusterPoolAbstract::STRATEGY[$strategy];
$this->cluster = new $clusterClass(
$this->getClusterAggregatorName(),
...\array_values($this->driverPools)
);
/**
* @eventName CacheClusterBuilt
* @param $clusterAggregator AggregatorInterface
* @param $cluster ClusterPoolInterface
*/
$this->cluster->getEventManager()->dispatch('CacheClusterBuilt', $this, $this->cluster);
}
} else {
throw new PhpfastcacheInvalidArgumentException('Unknown cluster strategy');
}
return $this->cluster;
}
/**
* @return string
*/
public function getClusterAggregatorName(): string
{
return $this->clusterAggregatorName;
}
}

View File

@ -0,0 +1,234 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster;
use Phpfastcache\Cluster\Drivers\{FullReplication\FullReplicationCluster,
MasterSlaveReplication\MasterSlaveReplicationCluster,
RandomReplication\RandomReplicationCluster,
SemiReplication\SemiReplicationCluster
};
use Phpfastcache\Config\ConfigurationOption;
use Phpfastcache\Core\{Item\ExtendedCacheItemInterface, Pool\DriverBaseTrait, Pool\ExtendedCacheItemPoolInterface};
use Phpfastcache\Entities\DriverIO;
use Phpfastcache\Entities\DriverStatistic;
use Phpfastcache\EventManager;
use Phpfastcache\Exceptions\PhpfastcacheDriverCheckException;
use Phpfastcache\Exceptions\PhpfastcacheDriverConnectException;
use Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException;
use Phpfastcache\Exceptions\PhpfastcacheInvalidConfigurationException;
use Psr\Cache\{CacheItemInterface, InvalidArgumentException};
use ReflectionException;
/**
* Class ClusterAbstract
*
* @package Phpfastcache\Cluster
*/
abstract class ClusterPoolAbstract implements ClusterPoolInterface
{
use DriverBaseTrait;
use ClusterPoolTrait {
DriverBaseTrait::__construct as private __parentConstruct;
}
public const STRATEGY = [
AggregatorInterface::STRATEGY_FULL_REPLICATION => FullReplicationCluster::class,
AggregatorInterface::STRATEGY_SEMI_REPLICATION => SemiReplicationCluster::class,
AggregatorInterface::STRATEGY_MASTER_SLAVE => MasterSlaveReplicationCluster::class,
AggregatorInterface::STRATEGY_RANDOM_REPLICATION => RandomReplicationCluster::class,
];
/**
* @var ExtendedCacheItemPoolInterface[]
*/
protected $clusterPools;
/**
* ClusterPoolAbstract constructor.
* @param string $clusterName
* @param ExtendedCacheItemPoolInterface ...$driverPools
* @throws PhpfastcacheInvalidArgumentException
* @throws PhpfastcacheDriverCheckException
* @throws PhpfastcacheDriverConnectException
* @throws PhpfastcacheInvalidConfigurationException
* @throws ReflectionException
*/
public function __construct(string $clusterName, ExtendedCacheItemPoolInterface ...$driverPools)
{
if (count($driverPools) < 2) {
throw new PhpfastcacheInvalidArgumentException('A cluster requires at least two pools to be working.');
}
$this->clusterPools = $driverPools;
$this->__parentConstruct(new ConfigurationOption(), $clusterName);
$this->setEventManager(EventManager::getInstance());
}
/**
* @inheritDoc
*/
public function getIO(): DriverIO
{
$IO = new DriverIO();
foreach ($this->clusterPools as $clusterPool) {
$IO->setReadHit($IO->getReadHit() + $clusterPool->getIO()->getReadHit())
->setReadMiss($IO->getReadMiss() + $clusterPool->getIO()->getReadMiss())
->setWriteHit($IO->getWriteHit() + $clusterPool->getIO()->getWriteHit());
}
return $IO;
}
/**
* @inheritDoc
*/
public function getClusterPools(): array
{
return $this->clusterPools;
}
/**
* @inheritDoc
*/
public function getItems(array $keys = [])
{
$items = [];
foreach ($keys as $key) {
$items[$key] = $this->getItem($key);
}
return $items;
}
/**
* Shared method used by All Clusters
*/
/**
* @inheritDoc
*/
public function deleteItems(array $keys)
{
$hasDeletedOnce = false;
foreach ($this->clusterPools as $driverPool) {
if ($result = $driverPool->deleteItems($keys)) {
$hasDeletedOnce = $result;
}
}
// Return true only if at least one backend confirmed the "clear" operation
return $hasDeletedOnce;
}
/**
* @inheritDoc
*/
public function saveDeferred(CacheItemInterface $item)
{
/** @var ExtendedCacheItemInterface $item */
$hasSavedOnce = false;
foreach ($this->clusterPools as $driverPool) {
$poolItem = $this->getStandardizedItem($item, $driverPool);
if ($result = $driverPool->saveDeferred($poolItem)) {
$hasSavedOnce = $result;
}
}
// Return true only if at least one backend confirmed the "commit" operation
return $hasSavedOnce;
}
/**
* @param ExtendedCacheItemInterface $item
* @param ExtendedCacheItemPoolInterface $driverPool
* @return CacheItemInterface
* @throws InvalidArgumentException
*/
protected function getStandardizedItem(ExtendedCacheItemInterface $item, ExtendedCacheItemPoolInterface $driverPool): CacheItemInterface
{
if (!$item->doesItemBelongToThatDriverBackend($driverPool)) {
/**
* Avoid infinite loop
*/
if ($driverPool === $this) {
/** @var ExtendedCacheItemInterface $itemPool */
$itemClass = $driverPool->getClassNamespace() . '\\' . 'Item';
$itemPool = new $itemClass($this, $item->getKey());
$itemPool->setEventManager($this->getEventManager())
->set($item->get())
->setHit($item->isHit())
->setTags($item->getTags())
->expiresAt($item->getExpirationDate())
->setDriver($driverPool);
return $itemPool;
}
return $driverPool->getItem($item->getKey())
->setEventManager($this->getEventManager())
->set($item->get())
->setHit($item->isHit())
->setTags($item->getTags())
->expiresAt($item->getExpirationDate())
->setDriver($driverPool);
}
return $item->setEventManager($this->getEventManager());
}
/**
* Interfaced methods that needs to be faked
*/
/**
* @return DriverStatistic
*/
public function getStats(): DriverStatistic
{
$stats = new DriverStatistic();
$stats->setInfo(
sprintf(
'Using %d pool(s): %s',
\count($this->clusterPools),
\implode(
', ',
\array_map(
static function (ExtendedCacheItemPoolInterface $pool) {
return \get_class($pool);
},
$this->clusterPools
)
)
)
);
$stats->setSize(
(int)\array_sum(
\array_map(
static function (ExtendedCacheItemPoolInterface $pool) {
return $pool->getStats()->getSize();
},
$this->clusterPools
)
)
);
$stats->setData(
(int)\array_map(
static function (ExtendedCacheItemPoolInterface $pool) {
return $pool->getStats()->getData();
},
$this->clusterPools
)
);
return $stats;
}
}

View File

@ -0,0 +1,31 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster;
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
/**
* Interface ClusterInterface
*
* @package Phpfastcache\Cluster
*/
interface ClusterPoolInterface extends ExtendedCacheItemPoolInterface
{
/**
* @return ExtendedCacheItemPoolInterface[]
*/
public function getClusterPools(): array;
}

View File

@ -0,0 +1,72 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster;
use Psr\Cache\CacheItemInterface;
trait ClusterPoolTrait
{
/**
* @return bool
*/
protected function driverCheck(): bool
{
return true;
}
/**
* @return bool
*/
protected function driverConnect(): bool
{
return true;
}
/**
* @param CacheItemInterface $item
* @return null
*/
protected function driverRead(CacheItemInterface $item)
{
return null;
}
/**
* @param CacheItemInterface $item
* @return bool
*/
protected function driverWrite(CacheItemInterface $item): bool
{
return true;
}
/**
* @param CacheItemInterface $item
* @return bool
*/
protected function driverDelete(CacheItemInterface $item): bool
{
return true;
}
/**
* @return bool
*/
protected function driverClear(): bool
{
return true;
}
}

View File

@ -0,0 +1,176 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster\Drivers\FullReplication;
use Phpfastcache\Cluster\ClusterPoolAbstract;
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
use Psr\Cache\CacheItemInterface;
/**
* Class FullReplicationCluster
* @package Phpfastcache\Cluster\Drivers\FullReplication
*/
class FullReplicationCluster extends ClusterPoolAbstract
{
/**
* @inheritDoc
*/
public function getItem($key)
{
/** @var ExtendedCacheItemPoolInterface[] $poolsToResync */
$poolsToResync = [];
/** @var ExtendedCacheItemInterface $item */
$item = null;
foreach ($this->clusterPools as $driverPool) {
$poolItem = $driverPool->getItem($key);
if ($poolItem->isHit()) {
if (!$item) {
$item = $poolItem;
continue;
}
$itemData = $item->get();
$poolItemData = $poolItem->get();
if (\is_object($itemData)
) {
if ($item->get() != $poolItemData) {
$poolsToResync[] = $driverPool;
}
} else {
if ($item->get() !== $poolItemData) {
$poolsToResync[] = $driverPool;
}
}
} else {
$poolsToResync[] = $driverPool;
}
}
if ($item && $item->isHit() && \count($poolsToResync) < \count($this->clusterPools)) {
foreach ($poolsToResync as $poolToResync) {
$poolItem = $poolToResync->getItem($key);
$poolItem->setEventManager($this->getEventManager())
->set($item->get())
->setHit($item->isHit())
->setTags($item->getTags())
->expiresAt($item->getExpirationDate())
->setDriver($poolToResync);
$poolToResync->save($poolItem);
}
}
return $this->getStandardizedItem($item ?? new Item($this, $key), $this);
}
/**
* @inheritDoc
*/
public function hasItem($key)
{
foreach ($this->clusterPools as $driverPool) {
$poolItem = $driverPool->getItem($key);
if ($poolItem->isHit()) {
return true;
}
}
return false;
}
/**
* @inheritDoc
*/
public function clear()
{
$hasClearedOnce = false;
foreach ($this->clusterPools as $driverPool) {
if ($result = $driverPool->clear()) {
$hasClearedOnce = $result;
}
}
// Return true only if at least one backend confirmed the "clear" operation
return $hasClearedOnce;
}
/**
* @inheritDoc
*/
public function deleteItem($key)
{
$hasDeletedOnce = false;
foreach ($this->clusterPools as $driverPool) {
if ($result = $driverPool->deleteItem($key)) {
$hasDeletedOnce = $result;
}
}
// Return true only if at least one backend confirmed the "clear" operation
return $hasDeletedOnce;
}
/**
* @inheritDoc
*/
public function save(CacheItemInterface $item)
{
/** @var ExtendedCacheItemInterface $item */
$hasSavedOnce = false;
foreach ($this->clusterPools as $driverPool) {
$poolItem = $this->getStandardizedItem($item, $driverPool);
if ($result = $driverPool->save($poolItem)) {
$hasSavedOnce = $result;
}
}
// Return true only if at least one backend confirmed the "commit" operation
return $hasSavedOnce;
}
/**
* @inheritDoc
*/
public function saveDeferred(CacheItemInterface $item)
{
/** @var ExtendedCacheItemInterface $item */
$hasSavedOnce = false;
foreach ($this->clusterPools as $driverPool) {
$poolItem = $this->getStandardizedItem($item, $driverPool);
if ($result = $driverPool->saveDeferred($poolItem)) {
$hasSavedOnce = $result;
}
}
// Return true only if at least one backend confirmed the "commit" operation
return $hasSavedOnce;
}
/**
* @inheritDoc
*/
public function commit()
{
$hasCommitOnce = false;
foreach ($this->clusterPools as $driverPool) {
if ($result = $driverPool->commit()) {
$hasCommitOnce = $result;
}
}
// Return true only if at least one backend confirmed the "commit" operation
return $hasCommitOnce;
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Khoa Bui (khoaofgod) <khoaofgod@gmail.com> https://www.phpfastcache.com
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster\Drivers\FullReplication;
use Phpfastcache\Cluster\ItemAbstract;
/**
* Class ClusterItem
* @package Phpfastcache\Cluster
*/
class Item extends ItemAbstract
{
}

View File

@ -0,0 +1,28 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Khoa Bui (khoaofgod) <khoaofgod@gmail.com> https://www.phpfastcache.com
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster\Drivers\MasterSlaveReplication;
use Phpfastcache\Cluster\ItemAbstract;
/**
* Class ClusterItem
* @package Phpfastcache\Cluster
*/
class Item extends ItemAbstract
{
}

View File

@ -0,0 +1,170 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster\Drivers\MasterSlaveReplication;
use Phpfastcache\Cluster\ClusterPoolAbstract;
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
use Phpfastcache\Exceptions\PhpfastcacheDriverCheckException;
use Phpfastcache\Exceptions\PhpfastcacheDriverConnectException;
use Phpfastcache\Exceptions\PhpfastcacheExceptionInterface;
use Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException;
use Phpfastcache\Exceptions\PhpfastcacheInvalidConfigurationException;
use Phpfastcache\Exceptions\PhpfastcacheReplicationException;
use Psr\Cache\CacheItemInterface;
use ReflectionException;
/**
* Class MasterSlaveReplicationCluster
* @package Phpfastcache\Cluster\Drivers\MasterSlaveReplication
*/
class MasterSlaveReplicationCluster extends ClusterPoolAbstract
{
/**
* MasterSlaveReplicationCluster constructor.
* @param string $clusterName
* @param ExtendedCacheItemPoolInterface ...$driverPools
* @throws PhpfastcacheInvalidArgumentException
* @throws PhpfastcacheDriverCheckException
* @throws PhpfastcacheDriverConnectException
* @throws PhpfastcacheInvalidConfigurationException
* @throws ReflectionException
*/
public function __construct(string $clusterName, ExtendedCacheItemPoolInterface ...$driverPools)
{
if (\count($driverPools) !== 2) {
throw new PhpfastcacheInvalidArgumentException('A "master/slave" cluster requires exactly two pools to be working.');
}
parent::__construct($clusterName, ...$driverPools);
}
/**
* @inheritDoc
*/
public function getItem($key)
{
return $this->getStandardizedItem(
$this->makeOperation(
static function (ExtendedCacheItemPoolInterface $pool) use ($key) {
return $pool->getItem($key);
}
) ?? new Item($this, $key),
$this
);
}
/**
* @param callable $operation
* @return mixed
* @throws PhpfastcacheReplicationException
*/
protected function makeOperation(callable $operation)
{
try {
return $operation($this->getMasterPool());
} catch (PhpfastcacheExceptionInterface $e) {
try {
$this->eventManager->dispatch(
'CacheReplicationSlaveFallback',
$this,
\debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function']
);
return $operation($this->getSlavePool());
} catch (PhpfastcacheExceptionInterface $e) {
throw new PhpfastcacheReplicationException('Master and Slave thrown an exception !');
}
}
}
/**
* @return ExtendedCacheItemPoolInterface
*/
protected function getMasterPool(): ExtendedCacheItemPoolInterface
{
return $this->clusterPools[0];
}
/**
* @return ExtendedCacheItemPoolInterface
*/
protected function getSlavePool(): ExtendedCacheItemPoolInterface
{
return $this->clusterPools[1];
}
/**
* @inheritDoc
*/
public function hasItem($key)
{
return $this->makeOperation(
static function (ExtendedCacheItemPoolInterface $pool) use ($key) {
return $pool->hasItem($key);
}
);
}
/**
* @inheritDoc
*/
public function clear()
{
return $this->makeOperation(
static function (ExtendedCacheItemPoolInterface $pool) {
return $pool->clear();
}
);
}
/**
* @inheritDoc
*/
public function deleteItem($key)
{
return $this->makeOperation(
static function (ExtendedCacheItemPoolInterface $pool) use ($key) {
return $pool->deleteItem($key);
}
);
}
/**
* @inheritDoc
*/
public function save(CacheItemInterface $item)
{
return $this->makeOperation(
function (ExtendedCacheItemPoolInterface $pool) use ($item) {
$item->setHit(true);
return $pool->save($this->getStandardizedItem($item, $pool));
}
);
}
/**
* @inheritDoc
*/
public function commit()
{
return $this->makeOperation(
static function (ExtendedCacheItemPoolInterface $pool) {
return $pool->commit();
}
);
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Khoa Bui (khoaofgod) <khoaofgod@gmail.com> https://www.phpfastcache.com
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster\Drivers\RandomReplication;
use Phpfastcache\Cluster\ItemAbstract;
/**
* Class ClusterItem
* @package Phpfastcache\Cluster
*/
class Item extends ItemAbstract
{
}

View File

@ -0,0 +1,59 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster\Drivers\RandomReplication;
use Phpfastcache\Cluster\Drivers\MasterSlaveReplication\MasterSlaveReplicationCluster;
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
use ReflectionException;
use ReflectionMethod;
/**
* Class MasterSlaveReplicationCluster
* @package Phpfastcache\Cluster\Drivers\MasterSlaveReplication
*/
class RandomReplicationCluster extends MasterSlaveReplicationCluster
{
/**
* RandomReplicationCluster constructor.
* @param string $clusterName
* @param ExtendedCacheItemPoolInterface ...$driverPools
* @throws ReflectionException
*/
public function __construct(string $clusterName, ExtendedCacheItemPoolInterface ...$driverPools)
{
(new ReflectionMethod(\get_parent_class(\get_parent_class($this)), __FUNCTION__))
->invoke($this, $clusterName, ...$driverPools);
$randomPool = $driverPools[\random_int(0, \count($driverPools) - 1)];
$this->eventManager->dispatch(
'CacheReplicationRandomPoolChosen',
$this,
$randomPool
);
$this->clusterPools = [$randomPool];
}
/**
* @param callable $operation
* @return mixed
*/
protected function makeOperation(callable $operation)
{
return $operation($this->getMasterPool());
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Khoa Bui (khoaofgod) <khoaofgod@gmail.com> https://www.phpfastcache.com
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster\Drivers\SemiReplication;
use Phpfastcache\Cluster\ItemAbstract;
/**
* Class ClusterItem
* @package Phpfastcache\Cluster
*/
class Item extends ItemAbstract
{
}

View File

@ -0,0 +1,203 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster\Drivers\SemiReplication;
use Phpfastcache\Cluster\ClusterPoolAbstract;
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
use Phpfastcache\Exceptions\PhpfastcacheExceptionInterface;
use Phpfastcache\Exceptions\PhpfastcacheReplicationException;
use Psr\Cache\CacheItemInterface;
/**
* Class FullReplicationCluster
* @package Phpfastcache\Cluster\Drivers\FullReplication
*/
class SemiReplicationCluster extends ClusterPoolAbstract
{
/**
* @inheritDoc
*/
public function getItem($key)
{
/** @var ExtendedCacheItemInterface $item */
$item = null;
$eCount = 0;
foreach ($this->clusterPools as $driverPool) {
try {
$poolItem = $driverPool->getItem($key);
if ($poolItem->isHit()) {
if (!$item) {
$item = $poolItem;
break;
}
}
} catch (PhpfastcacheExceptionInterface $e) {
$eCount++;
}
}
if (\count($this->clusterPools) <= $eCount) {
throw new PhpfastcacheReplicationException('Every pools thrown an exception');
}
return $this->getStandardizedItem($item ?? new Item($this, $key), $this);
}
/**
* @inheritDoc
*/
public function hasItem($key)
{
$eCount = 0;
foreach ($this->clusterPools as $driverPool) {
try {
$poolItem = $driverPool->getItem($key);
if ($poolItem->isHit()) {
return true;
}
} catch (PhpfastcacheExceptionInterface $e) {
$eCount++;
}
}
if (\count($this->clusterPools) <= $eCount) {
throw new PhpfastcacheReplicationException('Every pools thrown an exception');
}
return false;
}
/**
* @inheritDoc
*/
public function clear()
{
$hasClearedOnce = false;
$eCount = 0;
foreach ($this->clusterPools as $driverPool) {
try {
if ($result = $driverPool->clear()) {
$hasClearedOnce = $result;
}
} catch (PhpfastcacheExceptionInterface $e) {
$eCount++;
}
}
if (\count($this->clusterPools) <= $eCount) {
throw new PhpfastcacheReplicationException('Every pools thrown an exception');
}
// Return true only if at least one backend confirmed the "clear" operation
return $hasClearedOnce;
}
/**
* @inheritDoc
*/
public function deleteItem($key)
{
$hasDeletedOnce = false;
$eCount = 0;
foreach ($this->clusterPools as $driverPool) {
try {
if ($result = $driverPool->deleteItem($key)) {
$hasDeletedOnce = $result;
}
} catch (PhpfastcacheExceptionInterface $e) {
$eCount++;
}
}
if (\count($this->clusterPools) <= $eCount) {
throw new PhpfastcacheReplicationException('Every pools thrown an exception');
}
// Return true only if at least one backend confirmed the "clear" operation
return $hasDeletedOnce;
}
/**
* @inheritDoc
*/
public function save(CacheItemInterface $item)
{
/** @var ExtendedCacheItemInterface $item */
$hasSavedOnce = false;
$eCount = 0;
foreach ($this->clusterPools as $driverPool) {
try {
$poolItem = $this->getStandardizedItem($item, $driverPool);
if ($result = $driverPool->save($poolItem)) {
$hasSavedOnce = $result;
}
} catch (PhpfastcacheExceptionInterface $e) {
$eCount++;
}
}
if (\count($this->clusterPools) <= $eCount) {
throw new PhpfastcacheReplicationException('Every pools thrown an exception');
}
// Return true only if at least one backend confirmed the "commit" operation
return $hasSavedOnce;
}
/**
* @inheritDoc
*/
public function saveDeferred(CacheItemInterface $item)
{
/** @var ExtendedCacheItemInterface $item */
$hasSavedOnce = false;
foreach ($this->clusterPools as $driverPool) {
$poolItem = $this->getStandardizedItem($item, $driverPool);
if ($result = $driverPool->saveDeferred($poolItem)) {
$hasSavedOnce = $result;
}
}
// Return true only if at least one backend confirmed the "commit" operation
return $hasSavedOnce;
}
/**
* @inheritDoc
*/
public function commit()
{
$hasCommitOnce = false;
$eCount = 0;
foreach ($this->clusterPools as $driverPool) {
try {
if ($result = $driverPool->commit()) {
$hasCommitOnce = $result;
}
} catch (PhpfastcacheExceptionInterface $e) {
$eCount++;
}
}
if (\count($this->clusterPools) <= $eCount) {
throw new PhpfastcacheReplicationException('Every pools thrown an exception');
}
// Return true only if at least one backend confirmed the "commit" operation
return $hasCommitOnce;
}
}

View File

@ -0,0 +1,48 @@
<?php
/**
*
* This file is part of phpFastCache.
*
* @license MIT License (MIT)
*
* For full copyright and license information, please see the docs/CREDITS.txt file.
*
* @author Khoa Bui (khoaofgod) <khoaofgod@gmail.com> https://www.phpfastcache.com
* @author Georges.L (Geolim4) <contact@geolim4.com>
*
*/
declare(strict_types=1);
namespace Phpfastcache\Cluster;
use Phpfastcache\Core\Item\{ExtendedCacheItemInterface, ItemBaseTrait};
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
use Phpfastcache\Exceptions\{PhpfastcacheInvalidArgumentException};
/**
* Class ClusterItem
* @package Phpfastcache\Cluster
*/
abstract class ItemAbstract implements ExtendedCacheItemInterface
{
use ItemBaseTrait {
ItemBaseTrait::__construct as __BaseConstruct;
}
/**
* @param ExtendedCacheItemPoolInterface $driver
* @return static
* @throws PhpfastcacheInvalidArgumentException
*/
public function setDriver(ExtendedCacheItemPoolInterface $driver)
{
if ($driver instanceof ClusterPoolInterface) {
$this->driver = $driver;
return $this;
}
throw new PhpfastcacheInvalidArgumentException('Invalid driver instance');
}
}