Skip to content

Commit c873451

Browse files
committed
Added Firestore support + fixed some tests
1 parent f5f6095 commit c873451

12 files changed

Lines changed: 122 additions & 87 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
- __Migration guide__
44
- Read the [migration guide](./docs/migration/MigratingFromV8ToV9.md) to upgrade from V8 to V9
55
- __PSR-6__
6-
- Upgraded `psr/cache` dependency to `^3.0` (for PHP-8 types)
6+
- Upgraded `psr/cache` dependency to `^2.0||^3.0` (for PHP-8 types)
77
- `\Psr\Cache\CacheItemInterface::get()` slightly changed to fully comply with missing PSR-6 specification: If the cache item is **NOT** hit, this method will return `NULL`.
88
- __PSR-16__
99
- _To be written when the PSR-16 will be upgraded for PHP-8_
@@ -46,7 +46,8 @@
4646
- `\Psr\Cache\CacheItemInterface::set` will not accept `\Closure` object anymore as method unique parameter
4747
- __Drivers__
4848
- Added `Arangodb` driver support
49-
- Added `Dynamodb` driver support
49+
- Added `Dynamodb` (AWS) driver support
50+
- Added `Firestore` (GCP) driver support
5051
- Removed `Cookie` driver because of its potential dangerosity
5152
- Removed `Couchbase` (SDK 2 support dropped) driver which is now replaced by `Couchbasev3` (SDK 3)
5253
- Removed `Devtrue` and `Devfalse` drivers

CHANGELOG_API.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
- **[BC Break]** Updated `ExtendedCacheItemPoolInterface::getConfigClass()` signature: it is now a **static** method
66
- Added `ExtendedCacheItemPoolInterface::getItemClass()`
77
- Added `ExtendedCacheItemInterface::hasTag(string $tag)` to test if a cache item is tagged with the provided tag
8-
- Added `ExtendedCacheItemInterface::hasTags(array $tags, int $strategy)` to test if a cache item is tagged with ones the provided tags with an optional `$strategy` parameter
8+
- Added `ExtendedCacheItemInterface::hasTag(string $tag)` to test if a cache item is tagged with the provided tag
9+
- Added `ExtendedCacheItemInterface::cloneInto(ExtendedCacheItemInterface $itemTarget, ?ExtendedCacheItemPoolInterface $itemPoolTarget = null)` to clone a cache item into another with an optional pool object
910
- Referenced `TaggableCacheItemPoolInterface::TAG_STRATEGY_*` constants to `TaggableCacheItemInterface::TAG_STRATEGY_*` for more code usability
1011

1112
## 3.0.0

README.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,17 @@ The simplicity of abstraction: One class for many backend cache. You don't need
1818
| Regular drivers | High performances drivers | Development drivers | Cluster-Aggregated drivers |
1919
|---------------------------------|---------------------------------------------------------------|-------------------------------|-----------------------------------|
2020
| `Apcu` *(APC support removed)* | `Arangodb` | `Devnull` | `FullReplicationCluster` |
21-
| `Files` | `Cassandra` | `Devrandom` | `SemiReplicationCluster` |
22-
| `Leveldb` | `CouchBasev3`<br>_(`Couchbase` for SDK 2 support removed)_ | `Memstatic` | `MasterSlaveReplicationCluster` |
23-
| `Memcache(d)` | `Couchdb` | | `RandomReplicationCluster` |
24-
| `Sqlite` | `Dynamodb` | | |
25-
| `Wincache` | `Mongodb` | | |
26-
| `Zend Disk Cache` | `Predis` | | |
27-
| | `Redis` | | |
28-
| | `Ssdb` | | |
29-
| | `Zend Memory Cache` | | |
21+
| `Dynamodb` (AWS) | `Cassandra` | `Devrandom` | `SemiReplicationCluster` |
22+
| `Files` | `CouchBasev3`<br>_(`Couchbase` for SDK 2 support removed)_ | `Devrandom` | `SemiReplicationCluster` |
23+
| `Firestore` (GCP) | `Couchdb` | `Devrandom` | `SemiReplicationCluster` |
24+
| `Leveldb` | `Mongodb` | `Memstatic` | `MasterSlaveReplicationCluster` |
25+
| `Memcache(d)` | `Predis` | | `RandomReplicationCluster` |
26+
| `Sqlite` | `Redis` | | |
27+
| `Wincache` | `Ssdb` | | |
28+
| `Zend Disk Cache` | `Zend Memory Cache` | | |
29+
| | | | |
30+
| | | | |
31+
| | | | |
3032
| | | | |
3133

3234
\* Driver descriptions available in DOCS/DRIVERS.md
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env bash
22

33
composer self-update
4+
composer validate
45
composer install
56
composer require doctrine/couchdb:dev-master phpfastcache/phpssdb:~1.1.0 predis/predis:~1.1.0 mongodb/mongodb:^1.9 triagens/arangodb:^3.8 aws/aws-sdk-php:~3.0

docs/DRIVERS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424
* Devrandom **(Added in V8.0.8)**
2525
* A development driver with configurable factor chance and data length.
2626
* Dynamodb **(Added in V9)**
27-
* A very high-performance NoSQL driver using a key-value pair system. Be careful when flushing the table as it will delete and recreate the table due to a Dynamodb limitation.
27+
* An AWS cloud NoSQL driver using a key-value pair system. Be careful when flushing the table as it will delete and recreate the table due to a Dynamodb limitation.
2828
* Files
2929
* A file driver that use serialization for storing data for regular performances. A _$path_ config must be specified, else the system temporary directory will be used.
30+
* Firestore **(Added in V9)**
31+
* A GCP cloud NoSQL driver using a key-value pair system. Collections are created automatically on-the-fly.
3032
* Leveldb
3133
* A NoSQL driver using a key-value pair system. A _$path_ config must be specified, else the system temporary directory will be used.
3234
* Memcache

lib/Phpfastcache/Cluster/ClusterPoolAbstract.php

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -186,43 +186,20 @@ protected function getStandardizedItem(ExtendedCacheItemInterface $item, Extende
186186
/** @var ExtendedCacheItemInterface $itemPool */
187187
$itemClass = $driverPool->getItemClass();
188188
$itemPool = new $itemClass($this, $item->getKey(), $this->getEventManager());
189-
190-
$this->remapCacheItem($item, $itemPool, $driverPool);
189+
$item->cloneInto($itemPool, $driverPool);
191190

192191
return $itemPool;
193192
}
194193

195194
$itemPool = $driverPool->getItem($item->getKey());
196-
$this->remapCacheItem($item, $itemPool, $driverPool);
195+
$item->cloneInto($itemPool, $driverPool);
197196

198197
return $itemPool;
199198
}
200199

201200
return $item->setEventManager($this->getEventManager());
202201
}
203202

204-
/**
205-
* @param ExtendedCacheItemInterface $itemSource
206-
* @param ExtendedCacheItemInterface $itemTarget
207-
* @param ExtendedCacheItemPoolInterface $driverPool
208-
* @throws PhpfastcacheInvalidArgumentException
209-
* @throws PhpfastcacheLogicException
210-
*/
211-
protected function remapCacheItem(ExtendedCacheItemInterface $itemSource, ExtendedCacheItemInterface $itemTarget, ExtendedCacheItemPoolInterface $driverPool): void
212-
{
213-
$itemTarget->setEventManager($this->getEventManager())
214-
->set($itemSource->get())
215-
->setHit($itemSource->isHit())
216-
->setTags($itemSource->getTags())
217-
->expiresAt($itemSource->getExpirationDate())
218-
->setDriver($driverPool);
219-
220-
if ($driverPool->getConfig()->isItemDetailedDate()) {
221-
$itemTarget->setCreationDate($itemSource->getCreationDate())
222-
->setModificationDate($itemSource->getModificationDate());
223-
}
224-
}
225-
226203
/**
227204
* @return DriverStatistic
228205
*/

lib/Phpfastcache/Core/Item/ExtendedCacheItemInterface.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public function getEncodedKey(): string;
4444

4545
/**
4646
* Returns the raw value, regardless of hit status.
47+
* This method can be called if the cache item is NOT YET
48+
* persisted, and you need to access to its set value.
4749
*
4850
* Although not part of the CacheItemInterface, this method is used by
4951
* the pool for extracting information for saving.
@@ -195,4 +197,10 @@ public function getDataAsJsonString(int $options = JSON_THROW_ON_ERROR, int $dep
195197
* @return bool
196198
*/
197199
public function doesItemBelongToThatDriverBackend(ExtendedCacheItemPoolInterface $driverPool): bool;
200+
201+
/**
202+
* @param ExtendedCacheItemInterface $itemTarget
203+
* @param ExtendedCacheItemPoolInterface|null $itemPoolTarget
204+
*/
205+
public function cloneInto(ExtendedCacheItemInterface $itemTarget, ?ExtendedCacheItemPoolInterface $itemPoolTarget = null): void;
198206
}

lib/Phpfastcache/Core/Item/ExtendedCacheItemTrait.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
use Phpfastcache\Event\EventManagerInterface;
2222
use Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException;
2323
use Phpfastcache\Exceptions\PhpfastcacheLogicException;
24-
use Phpfastcache\Util\ClassNamespaceResolverTrait;
2524

2625
trait ExtendedCacheItemTrait
2726
{
@@ -280,5 +279,24 @@ public function doesItemBelongToThatDriverBackend(ExtendedCacheItemPoolInterface
280279
return $driverPool::getClassNamespace() === self::getClassNamespace();
281280
}
282281

282+
/**
283+
* @throws PhpfastcacheLogicException
284+
* @throws PhpfastcacheInvalidArgumentException
285+
*/
286+
public function cloneInto(ExtendedCacheItemInterface $itemTarget, ?ExtendedCacheItemPoolInterface $itemPoolTarget = null): void
287+
{
288+
$itemTarget->setEventManager($this->getEventManager())
289+
->set($this->getRawValue())
290+
->setHit($this->isHit())
291+
->setTags($this->getTags())
292+
->expiresAt($this->getExpirationDate())
293+
->setDriver($itemPoolTarget ?? $this->driver);
294+
295+
if ($this->driver->getConfig()->isItemDetailedDate()) {
296+
$itemTarget->setCreationDate($this->getCreationDate())
297+
->setModificationDate($this->getModificationDate());
298+
}
299+
}
300+
283301
abstract protected function getDriverClass(): string;
284302
}

lib/Phpfastcache/Drivers/Dynamodb/Config.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ class Config extends ConfigurationOption
4646
public function __construct(array $parameters = [])
4747
{
4848
parent::__construct($parameters);
49-
$this->awsAccessKeyId = $this->getDefaultSuperGlobalAccessor()('SERVER', 'AWS_ACCESS_KEY_ID');
50-
$this->awsSecretAccessKey = $this->getDefaultSuperGlobalAccessor()('SERVER', 'AWS_SECRET_ACCESS_KEY');
49+
$this->awsAccessKeyId = $this->getSuperGlobalAccessor()('SERVER', 'AWS_ACCESS_KEY_ID');
50+
$this->awsSecretAccessKey = $this->getSuperGlobalAccessor()('SERVER', 'AWS_SECRET_ACCESS_KEY');
5151
}
5252

5353
/**

lib/Phpfastcache/Drivers/Firestore/Driver.php

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,27 @@
1515

1616
namespace Phpfastcache\Drivers\Firestore;
1717

18+
use Google\Cloud\Core\Blob as GoogleBlob;
19+
use Google\Cloud\Core\Timestamp as GoogleTimestamp;
1820
use Google\Cloud\Firestore\FirestoreClient as GoogleFirestoreClient;
19-
2021
use Phpfastcache\Cluster\AggregatablePoolInterface;
2122
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
2223
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
2324
use Phpfastcache\Core\Pool\TaggableCacheItemPoolTrait;
2425
use Phpfastcache\Entities\DriverStatistic;
26+
use Phpfastcache\Exceptions\PhpfastcacheDriverConnectException;
27+
use Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException;
28+
use Phpfastcache\Exceptions\PhpfastcacheLogicException;
2529

2630
/**
2731
* Class Driver
2832
* @property Config $config
2933
* @property GoogleFirestoreClient $instance
30-
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
3134
*/
3235
class Driver implements ExtendedCacheItemPoolInterface, AggregatablePoolInterface
3336
{
3437
use TaggableCacheItemPoolTrait;
3538

36-
protected const TTL_FIELD_NAME = 't';
37-
3839
/**
3940
* @return bool
4041
*/
@@ -45,21 +46,24 @@ public function driverCheck(): bool
4546

4647
/**
4748
* @return bool
49+
* @throws PhpfastcacheDriverConnectException
50+
* @throws PhpfastcacheInvalidArgumentException
51+
* @throws PhpfastcacheLogicException
4852
*/
4953
protected function driverConnect(): bool
5054
{
51-
/*
52-
$this->instance = new GoogleFirestoreClient([
53-
//'projectId' => $projectId,
54-
]);*/
55+
$gcpId = $this->getConfig()->getSuperGlobalAccessor()('SERVER', 'GOOGLE_CLOUD_PROJECT');
56+
$gacPath = $this->getConfig()->getSuperGlobalAccessor()('SERVER', 'GOOGLE_APPLICATION_CREDENTIALS');
57+
58+
if (empty($gcpId)) {
59+
throw new PhpfastcacheDriverConnectException('The environment configuration GOOGLE_CLOUD_PROJECT must be set');
60+
}
5561

56-
/* if (!$this->hasCollection()) {
57-
$this->createCollection();
62+
if (empty($gacPath) || !\is_readable($gacPath)) {
63+
throw new PhpfastcacheDriverConnectException('The environment configuration GOOGLE_APPLICATION_CREDENTIALS must be set and the file must be readable.');
5864
}
5965

60-
if (!$this->hasTtlEnabled()) {
61-
$this->enableTtl();
62-
}*/
66+
$this->instance = new GoogleFirestoreClient();
6367

6468
return true;
6569
}
@@ -70,6 +74,13 @@ protected function driverConnect(): bool
7074
*/
7175
protected function driverWrite(ExtendedCacheItemInterface $item): bool
7276
{
77+
$this->instance->collection($this->getConfig()->getCollection())
78+
->document($item->getKey())
79+
->set(
80+
$this->driverPreWrap($item),
81+
['merge' => true]
82+
);
83+
7384
return true;
7485
}
7586

@@ -80,6 +91,15 @@ protected function driverWrite(ExtendedCacheItemInterface $item): bool
8091
*/
8192
protected function driverRead(ExtendedCacheItemInterface $item): ?array
8293
{
94+
$doc = $this->instance->collection($this->getConfig()->getCollection())
95+
->document($item->getKey());
96+
97+
$snapshotData = $doc->snapshot()->data();
98+
99+
if (\is_array($snapshotData)) {
100+
return $this->decodeFirestoreDocument($snapshotData);
101+
}
102+
83103
return null;
84104
}
85105

@@ -89,6 +109,10 @@ protected function driverRead(ExtendedCacheItemInterface $item): ?array
89109
*/
90110
protected function driverDelete(ExtendedCacheItemInterface $item): bool
91111
{
112+
$this->instance->collection($this->getConfig()->getCollection())
113+
->document($item->getKey())
114+
->delete();
115+
92116
return true;
93117
}
94118

@@ -97,44 +121,45 @@ protected function driverDelete(ExtendedCacheItemInterface $item): bool
97121
*/
98122
protected function driverClear(): bool
99123
{
100-
return true;
101-
}
102-
103-
protected function hasCollection(): bool
104-
{
105-
return true;
106-
}
107-
108-
protected function createCollection() :void
109-
{
110-
}
124+
$batchSize = 100;
125+
$collection = $this->instance->collection($this->getConfig()->getCollection());
126+
$documents = $collection->limit($batchSize)->documents();
127+
while (!$documents->isEmpty()) {
128+
foreach ($documents as $document) {
129+
$document->reference()->delete();
130+
}
131+
$documents = $collection->limit($batchSize)->documents();
132+
}
111133

112-
protected function hasTtlEnabled(): bool
113-
{
114134
return true;
115135
}
116136

117-
protected function enableTtl(): void
137+
protected function decodeFirestoreDocument(array $snapshotData): array
118138
{
139+
return \array_map(static function ($datum) {
140+
if ($datum instanceof GoogleTimestamp) {
141+
$date = $datum->get();
142+
if ($date instanceof \DateTimeImmutable) {
143+
return \DateTime::createFromImmutable($date);
144+
}
145+
return $date;
146+
}
147+
148+
if ($datum instanceof GoogleBlob) {
149+
return (string) $datum;
150+
}
151+
152+
return $datum;
153+
}, $snapshotData);
119154
}
120155

121156
public function getStats(): DriverStatistic
122157
{
123-
return new DriverStatistic();
124-
}
125-
126-
protected function encodeDocument(array $data): array
127-
{
128-
$data[self::DRIVER_DATA_WRAPPER_INDEX] = $this->encode($data[self::DRIVER_DATA_WRAPPER_INDEX]);
129-
130-
return $data;
131-
}
132-
133-
protected function decodeDocument(array $data): array
134-
{
135-
$data[self::DRIVER_DATA_WRAPPER_INDEX] = $this->decode($data[self::DRIVER_DATA_WRAPPER_INDEX]);
136-
137-
return $data;
158+
return (new DriverStatistic())
159+
->setData(implode(', ', array_keys($this->itemInstances)))
160+
->setInfo('No info provided by Google Firestore')
161+
->setRawData([])
162+
->setSize(0);
138163
}
139164

140165
public function getConfig(): Config

0 commit comments

Comments
 (0)