Skip to content

Commit 1e9ea2e

Browse files
committed
Merge branch 'cleanup'
2 parents d5beb58 + f9ce66e commit 1e9ea2e

File tree

19 files changed

+218
-223
lines changed

19 files changed

+218
-223
lines changed

scripts/generate.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use Salient\Cli\CliOptionBuilder;
99
use Salient\Console\ConsoleWriter;
1010
use Salient\Container\Container;
11-
use Salient\Contract\Cache\CacheStoreInterface;
11+
use Salient\Contract\Cache\CacheInterface;
1212
use Salient\Contract\Console\ConsoleWriterInterface;
1313
use Salient\Contract\Container\ContainerInterface;
1414
use Salient\Contract\Core\MessageLevel as Level;
@@ -71,7 +71,7 @@
7171

7272
$facades = [
7373
App::class => [ContainerInterface::class, [Container::class], '--desc', 'A facade for the global service container', '--api'],
74-
Cache::class => [CacheStoreInterface::class, [CacheStore::class], '--desc', 'A facade for the global cache store', '--api'],
74+
Cache::class => [CacheInterface::class, [CacheStore::class], '--desc', 'A facade for the global cache store', '--api'],
7575
Config::class => [ConfigurationManager::class, '--api'],
7676
Console::class => [ConsoleWriterInterface::class, [ConsoleWriter::class], '--desc', 'A facade for the global console writer', '--api'],
7777
Err::class => [ErrorHandler::class, '--skip', 'handleShutdown,handleError,handleException'],

src/Toolkit/Cache/CacheStore.php

Lines changed: 46 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,23 @@
22

33
namespace Salient\Cache;
44

5-
use Salient\Contract\Cache\CacheStoreInterface;
5+
use Salient\Contract\Cache\CacheInterface;
66
use Salient\Core\AbstractStore;
77
use DateInterval;
88
use DateTimeImmutable;
99
use DateTimeInterface;
1010
use LogicException;
1111
use SQLite3Result;
12+
use SQLite3Stmt;
1213

1314
/**
1415
* A PSR-16 key-value store backed by a SQLite database
1516
*
1617
* @api
1718
*/
18-
final class CacheStore extends AbstractStore implements CacheStoreInterface
19+
final class CacheStore extends AbstractStore implements CacheInterface
1920
{
21+
private ?SQLite3Stmt $Stmt = null;
2022
private ?int $Now = null;
2123

2224
/**
@@ -103,34 +105,36 @@ public function set($key, $value, $ttl = null): bool
103105
/**
104106
* @phpstan-impure
105107
*/
106-
public function has($key, ?int $maxAge = null): bool
108+
public function has($key): bool
107109
{
108110
$sql = <<<SQL
109111
SELECT
110112
COUNT(*)
111113
FROM
112114
_cache_item
113115
SQL;
114-
$result = $this->queryItems($sql, $key, $maxAge);
116+
$result = $this->queryItems($sql, $key);
115117
/** @var array{int} */
116118
$row = $result->fetchArray(\SQLITE3_NUM);
119+
$this->closeStmt();
117120

118121
return (bool) $row[0];
119122
}
120123

121124
/**
122125
* @phpstan-impure
123126
*/
124-
public function get($key, $default = null, ?int $maxAge = null)
127+
public function get($key, $default = null)
125128
{
126129
$sql = <<<SQL
127130
SELECT
128131
item_value
129132
FROM
130133
_cache_item
131134
SQL;
132-
$result = $this->queryItems($sql, $key, $maxAge);
135+
$result = $this->queryItems($sql, $key);
133136
$row = $result->fetchArray(\SQLITE3_NUM);
137+
$this->closeStmt();
134138

135139
return $row === false
136140
? $default
@@ -140,14 +144,10 @@ public function get($key, $default = null, ?int $maxAge = null)
140144
/**
141145
* @phpstan-impure
142146
*/
143-
public function getInstanceOf($key, string $class, ?object $default = null, ?int $maxAge = null): ?object
147+
public function getInstanceOf($key, string $class, ?object $default = null): ?object
144148
{
145-
$store = $this->maybeAsOfNow();
146-
if (!$store->has($key, $maxAge)) {
147-
return $default;
148-
}
149-
$item = $store->get($key, $default, $maxAge);
150-
if (!is_object($item) || !is_a($item, $class)) {
149+
$item = $this->get($key);
150+
if ($item === null || !is_object($item) || !is_a($item, $class)) {
151151
return $default;
152152
}
153153
return $item;
@@ -156,14 +156,10 @@ public function getInstanceOf($key, string $class, ?object $default = null, ?int
156156
/**
157157
* @phpstan-impure
158158
*/
159-
public function getArray($key, ?array $default = null, ?int $maxAge = null): ?array
159+
public function getArray($key, ?array $default = null): ?array
160160
{
161-
$store = $this->maybeAsOfNow();
162-
if (!$store->has($key, $maxAge)) {
163-
return $default;
164-
}
165-
$item = $store->get($key, $default, $maxAge);
166-
if (!is_array($item)) {
161+
$item = $this->get($key);
162+
if ($item === null || !is_array($item)) {
167163
return $default;
168164
}
169165
return $item;
@@ -172,14 +168,10 @@ public function getArray($key, ?array $default = null, ?int $maxAge = null): ?ar
172168
/**
173169
* @phpstan-impure
174170
*/
175-
public function getInt($key, ?int $default = null, ?int $maxAge = null): ?int
171+
public function getInt($key, ?int $default = null): ?int
176172
{
177-
$store = $this->maybeAsOfNow();
178-
if (!$store->has($key, $maxAge)) {
179-
return $default;
180-
}
181-
$item = $store->get($key, $default, $maxAge);
182-
if (!is_int($item)) {
173+
$item = $this->get($key);
174+
if ($item === null || !is_int($item)) {
183175
return $default;
184176
}
185177
return $item;
@@ -188,14 +180,10 @@ public function getInt($key, ?int $default = null, ?int $maxAge = null): ?int
188180
/**
189181
* @phpstan-impure
190182
*/
191-
public function getString($key, ?string $default = null, ?int $maxAge = null): ?string
183+
public function getString($key, ?string $default = null): ?string
192184
{
193-
$store = $this->maybeAsOfNow();
194-
if (!$store->has($key, $maxAge)) {
195-
return $default;
196-
}
197-
$item = $store->get($key, $default, $maxAge);
198-
if (!is_string($item)) {
185+
$item = $this->get($key);
186+
if ($item === null || !is_string($item)) {
199187
return $default;
200188
}
201189
return $item;
@@ -256,10 +244,10 @@ public function clearExpired(): bool
256244
/**
257245
* @inheritDoc
258246
*/
259-
public function getMultiple($keys, $default = null, ?int $maxAge = null)
247+
public function getMultiple($keys, $default = null)
260248
{
261249
foreach ($keys as $key) {
262-
$values[$key] = $this->get($key, $default, $maxAge);
250+
$values[$key] = $this->get($key, $default);
263251
}
264252
return $values ?? [];
265253
}
@@ -289,44 +277,46 @@ public function deleteMultiple($keys): bool
289277
/**
290278
* @phpstan-impure
291279
*/
292-
public function getItemCount(?int $maxAge = null): int
280+
public function getItemCount(): int
293281
{
294282
$sql = <<<SQL
295283
SELECT
296284
COUNT(*)
297285
FROM
298286
_cache_item
299287
SQL;
300-
$result = $this->queryItems($sql, null, $maxAge);
288+
$result = $this->queryItems($sql);
301289
/** @var array{int} */
302290
$row = $result->fetchArray(\SQLITE3_NUM);
291+
$this->closeStmt();
303292

304293
return $row[0];
305294
}
306295

307296
/**
308297
* @phpstan-impure
309298
*/
310-
public function getAllKeys(?int $maxAge = null): array
299+
public function getItemKeys(): array
311300
{
312301
$sql = <<<SQL
313302
SELECT
314303
item_key
315304
FROM
316305
_cache_item
317306
SQL;
318-
$result = $this->queryItems($sql, null, $maxAge);
307+
$result = $this->queryItems($sql);
319308
while (($row = $result->fetchArray(\SQLITE3_NUM)) !== false) {
320309
$keys[] = $row[0];
321310
}
311+
$this->closeStmt();
322312

323313
return $keys ?? [];
324314
}
325315

326316
/**
327317
* @inheritDoc
328318
*/
329-
public function asOfNow(?int $now = null): CacheStoreInterface
319+
public function asOfNow(?int $now = null): CacheInterface
330320
{
331321
if ($this->Now !== null) {
332322
throw new LogicException(
@@ -378,49 +368,34 @@ private function now(): int
378368
return $this->Now ?? time();
379369
}
380370

381-
/**
382-
* @return static
383-
*/
384-
private function maybeAsOfNow(): self
371+
private function queryItems(string $sql, ?string $key = null): SQLite3Result
385372
{
386-
return $this->Now === null
387-
? $this->asOfNow()
388-
: $this;
389-
}
390-
391-
private function queryItems(string $sql, ?string $key, ?int $maxAge): SQLite3Result
392-
{
393-
$where = [];
394-
$bind = [];
395-
396373
if ($key !== null) {
397374
$where[] = 'item_key = :item_key';
398375
$bind[] = [':item_key', $key, \SQLITE3_TEXT];
399376
}
400377

401-
$bindNow = false;
402-
if ($maxAge === null) {
403-
$where[] = "(expires_at IS NULL OR expires_at > DATETIME(:now, 'unixepoch'))";
404-
$bindNow = true;
405-
} elseif ($maxAge) {
406-
$where[] = "DATETIME(set_at, :max_age) > DATETIME(:now, 'unixepoch')";
407-
$bind[] = [':max_age', "+$maxAge seconds", \SQLITE3_TEXT];
408-
$bindNow = true;
409-
}
410-
if ($bindNow) {
411-
$bind[] = [':now', $this->now(), \SQLITE3_INTEGER];
412-
}
378+
$where[] = "(expires_at IS NULL OR expires_at > DATETIME(:now, 'unixepoch'))";
379+
$bind[] = [':now', $this->now(), \SQLITE3_INTEGER];
413380

414381
$where = implode(' AND ', $where);
415-
if ($where !== '') {
416-
$sql .= " WHERE $where";
417-
}
382+
$sql .= " WHERE $where";
418383

419384
$stmt = $this->prepare($sql);
420385
foreach ($bind as [$param, $value, $type]) {
421386
$stmt->bindValue($param, $value, $type);
422387
}
423388

424-
return $this->execute($stmt);
389+
$result = $this->execute($stmt);
390+
$this->Stmt = $stmt;
391+
return $result;
392+
}
393+
394+
private function closeStmt(): void
395+
{
396+
if ($this->Stmt !== null) {
397+
$this->Stmt->close();
398+
$this->Stmt = null;
399+
}
425400
}
426401
}

src/Toolkit/Cache/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) Luke Arms
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

src/Toolkit/Cache/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# salient/cache
2+
3+
> The cache component of the [Salient toolkit][toolkit]
4+
5+
<p>
6+
<a href="https://packagist.org/packages/salient/toolkit"><img src="https://poser.pugx.org/salient/toolkit/v" alt="Latest Stable Version" /></a>
7+
<a href="https://packagist.org/packages/salient/toolkit"><img src="https://poser.pugx.org/salient/toolkit/license" alt="License" /></a>
8+
<a href="https://github.com/salient-labs/toolkit/actions"><img src="https://github.com/salient-labs/toolkit/actions/workflows/ci.yml/badge.svg" alt="CI Status" /></a>
9+
<a href="https://codecov.io/gh/salient-labs/toolkit"><img src="https://codecov.io/gh/salient-labs/toolkit/graph/badge.svg?token=Y0l9ZeEtrI" alt="Code Coverage" /></a>
10+
</p>
11+
12+
---
13+
14+
`salient/cache` provides a SQLite-backed key-value cache.
15+
16+
- Implements [PSR-16 (Common Interface for Caching Libraries)][PSR-16]
17+
- Multiple cache operations can be grouped into an atomic transaction via a
18+
time-bound instance of the cache[^1] that maintains an exclusive lock on the
19+
underlying database until it goes out of scope or is explicitly closed
20+
21+
## Documentation
22+
23+
[API documentation][api-docs] for `salient/cache` tracks the `main` branch of
24+
the toolkit's [GitHub repository][toolkit], where further documentation can also
25+
be found.
26+
27+
[^1]: See [CacheStore::asOfNow()][asOfNow] for more information.
28+
29+
[api-docs]: https://salient-labs.github.io/toolkit/namespace-Salient.Cache.html
30+
[asOfNow]:
31+
https://salient-labs.github.io/toolkit/Salient.Cache.CacheStore.html#_asOfNow
32+
[PSR-16]: https://www.php-fig.org/psr/psr-16/
33+
[toolkit]: https://github.com/salient-labs/toolkit

src/Toolkit/Container/Application.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Salient\Cache\CacheStore;
66
use Salient\Console\Target\StreamTarget;
7-
use Salient\Contract\Cache\CacheStoreInterface;
7+
use Salient\Contract\Cache\CacheInterface;
88
use Salient\Contract\Console\ConsoleMessageType as MessageType;
99
use Salient\Contract\Container\ApplicationInterface;
1010
use Salient\Contract\Core\MessageLevel as Level;
@@ -500,7 +500,7 @@ final public function stopCache()
500500
return $this;
501501
}
502502

503-
private function checkCache(CacheStoreInterface $cache): bool
503+
private function checkCache(CacheInterface $cache): bool
504504
{
505505
return $cache instanceof CacheStore
506506
&& File::same($this->getCacheDb(false), $cache->getFilename());

src/Toolkit/Container/Container.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use Salient\Container\Exception\ArgumentsNotUsedException;
1414
use Salient\Container\Exception\InvalidServiceException;
1515
use Salient\Container\Exception\ServiceNotFoundException;
16-
use Salient\Contract\Cache\CacheStoreInterface;
16+
use Salient\Contract\Cache\CacheInterface;
1717
use Salient\Contract\Console\ConsoleWriterInterface;
1818
use Salient\Contract\Container\ContainerAwareInterface;
1919
use Salient\Contract\Container\ContainerInterface;
@@ -58,7 +58,7 @@ class Container implements ContainerInterface, FacadeAwareInterface
5858
];
5959

6060
private const DEFAULT_SERVICES = [
61-
CacheStoreInterface::class => [CacheStore::class, ServiceLifetime::SINGLETON],
61+
CacheInterface::class => [CacheStore::class, ServiceLifetime::SINGLETON],
6262
ConsoleWriterInterface::class => [ConsoleWriter::class, ServiceLifetime::SINGLETON],
6363
LoggerInterface::class => [ConsoleLogger::class, ServiceLifetime::INHERIT],
6464
SyncStoreInterface::class => [SyncStore::class, ServiceLifetime::SINGLETON],

0 commit comments

Comments
 (0)