Skip to content

Commit

Permalink
[Cache] Various Updates (#227)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshuaEstes authored Sep 18, 2024
1 parent e89d747 commit b67565d
Show file tree
Hide file tree
Showing 25 changed files with 711 additions and 110 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ To get the diff between two versions, go to https://github.com/SonsOfPHP/sonsofp

## [Unreleased]

* [PR #227](https://github.com/SonsOfPHP/sonsofphp/pull/227) [Cache] FilesystemAdapter, Marshallers, and other updates
* [PR #226](https://github.com/SonsOfPHP/sonsofphp/pull/226) [Registry] New Component and Contract
* [PR #225](https://github.com/SonsOfPHP/sonsofphp/pull/225) Maintenance
* [PR #222](https://github.com/SonsOfPHP/sonsofphp/pull/222) [Assert] New Component
Expand Down
5 changes: 2 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@
"psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0",
"aws/aws-sdk-php": "^3.0",
"liip/imagine-bundle": "^2.0",
"sonsofphp/registry-contract": "0.3.x-dev"
"liip/imagine-bundle": "^2.0"
},
"replace": {
"sonsofphp/bard": "self.version",
Expand Down Expand Up @@ -247,4 +246,4 @@
"SonsOfPHP\\Bridge\\Symfony\\Cqrs\\Tests\\": "src/SonsOfPHP/Bridge/Symfony/Cqrs/Tests"
}
}
}
}
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* [Assert](components/assert.md)
* [Cache](components/cache/README.md)
* [Adapters](components/cache/adapters.md)
* [Marshallers](components/cache/marshallers.md)
* [Clock](components/clock.md)
* [Container](components/container.md)
* [Cookie](components/cookie.md)
Expand Down
6 changes: 3 additions & 3 deletions docs/components/cache/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
title: Cache - Overview
---

# Cache

## Installation

```shell
Expand Down Expand Up @@ -59,6 +57,8 @@ $pool = new ChainAdapter([
]);
```

The `ChainAdapter` will read from all pools and return the first result it finds. So in the above example, if the cache item is not found in `ArrayAdapter`, it will look for it in the `ApcuAdapter` pool.
The `ChainAdapter` will read from all pools and return the first result it
finds. So in the above example, if the cache item is not found in
`ArrayAdapter`, it will look for it in the `ApcuAdapter` pool.

The `ChainAdapter` will also write to and delete from all pools.
64 changes: 64 additions & 0 deletions docs/components/cache/adapters.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,82 @@
---
title: Cache Adapters
---

# Adapters

All adapters implement the PSR-6 `\Psr\Cache\CacheItemPoolInterface` and can be used as
standalone cache pools.

## ApcuAdapter

Requires the APCu extension is loaded and enabled.

```php
<?php

use SonsOfPHP\Componenet\Cache\Adapter\ApcuAdapter;

$cache = new ApcuAdapter();

// You can set a default TTL in seconds and Marshaller as well.
$cache = new ApcuAdapter(60, new CustomMarshaller());
```

## ArrayAdapter

Stores cache items in an internal PHP array.

```php
<?php

use SonsOfPHP\Componenet\Cache\Adapter\ArrayAdapter;

$cache = new ArrayAdapter();
```

## ChainAdapter

The Chain Adapter will take one or more adapters. It will WRITE to all adapters
and will READ from each adapter until it finds a hit and return that cache item.

```php
<?php

use SonsOfPHP\Componenet\Cache\Adapter\ApcuAdapter;
use SonsOfPHP\Componenet\Cache\Adapter\ArrayAdapter;
use SonsOfPHP\Componenet\Cache\Adapter\ChainAdapter;

$cache = new ChainAdapter([
new ArrayAdapter(),
new ApcuAdapter(),
]);
```

## FilesystemAdapter

Stores cache files on disk

```php
<?php

use SonsOfPHP\Componenet\Cache\Adapter\FilesystemAdapter;

$cache = new FilesystemAdapter();

// You can configure the directory, default permissions, default ttl, and
// marshaller
$cache = new FilesystemAdapter('/path/to/cache', 0777, 60, new CustomMarshaller());
```

## NullAdapter

Mainly used for testing, however you could have some checks in place and if
those checks fail, you could fallback to this adapter.

```php
<?php

use SonsOfPHP\Componenet\Cache\Adapter\NullAdapter;

$cache = new NullAdapter();
```
38 changes: 38 additions & 0 deletions docs/components/cache/marshallers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: Cache Marshallers
---

# Marshallers

Using marshallers will allow you to serialize the cache values differently.

## SerializableMarshaller

This marshaller will use php's native `serialize` and `unserialize` functions on
the values.

```php
<?php

use SonsOfPHP\Componenet\Cache\Marshaller\SerializableMarshaller;

$marshaller = new SerializableMarshaller();
```

## JsonMarshaller

The json marshaller will use php's native `json_encode` and `json_decode`
functions to serialize data.

```php
<?php

use SonsOfPHP\Componenet\Cache\Marshaller\JsonMarshaller;

$marshaller = new JsonMarshaller();
```

# Custom Marshaller

You can create a custom marshaller. For example, you could create one the
encrypts/decrypts the values.
90 changes: 90 additions & 0 deletions src/SonsOfPHP/Component/Cache/Adapter/AbstractAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Component\Cache\Adapter;

use Psr\Cache\CacheItemInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use SonsOfPHP\Component\Cache\Marshaller\MarshallerInterface;
use SonsOfPHP\Component\Cache\Marshaller\SerializableMarshaller;

/**
* @author Joshua Estes <joshua@sonsofphp.com>
*/
abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
{
use LoggerAwareTrait;

protected array $deferred = [];

public function __construct(
protected int $defaultTTL = 0,
protected ?MarshallerInterface $marshaller = null,
) {
if (!$this->marshaller instanceof MarshallerInterface) {
$this->marshaller = new SerializableMarshaller();
}
}

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

/**
* {@inheritdoc}
*/
public function getItems(array $keys = []): iterable
{
foreach ($keys as $key) {
yield $key => $this->getItem($key);
}
}

/**
* {@inheritdoc}
*/
public function deleteItems(array $keys): bool
{
$isOk = true;
foreach ($keys as $key) {
if (!$this->deleteItem($key)) {
$this->logger?->debug(sprintf('Unable to delete key "%s".', $key));
$isOk = false;
}
}

return $isOk;
}

/**
* {@inheritdoc}
*/
public function saveDeferred(CacheItemInterface $item): bool
{
$this->deferred[$item->getKey()] = $item;

return true;
}

/**
* {@inheritdoc}
*/
public function commit(): bool
{
$isOk = true;

foreach ($this->deferred as $item) {
if (!$this->save($item)) {
$this->logger?->debug(sprintf('Unable to save key "%s".', $item->getKey()));
$isOk = false;
}
}

$this->deferred = [];

return $isOk;
}
}
73 changes: 15 additions & 58 deletions src/SonsOfPHP/Component/Cache/Adapter/ApcuAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,25 @@
use Psr\Cache\CacheItemInterface;
use SonsOfPHP\Component\Cache\CacheItem;
use SonsOfPHP\Component\Cache\Exception\CacheException;
use SonsOfPHP\Component\Cache\Marshaller\MarshallerInterface;

/**
* @author Joshua Estes <joshua@sonsofphp.com>
*/
class ApcuAdapter implements AdapterInterface
final class ApcuAdapter extends AbstractAdapter
{
private array $deferred = [];

public function __construct()
{
public function __construct(
int $defaultTTL = 0,
?MarshallerInterface $marshaller = null,
) {
if (!extension_loaded('apcu') || !filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOL) || false === apcu_enabled()) {
throw new CacheException('APCu extension is required.');
}

parent::__construct(
defaultTTL: $defaultTTL,
marshaller: $marshaller,
);
}

/**
Expand All @@ -28,22 +34,14 @@ public function __construct()
public function getItem(string $key): CacheItemInterface
{
if ($this->hasItem($key)) {
return (new CacheItem($key, true))->set(apcu_fetch($key));
return (new CacheItem($key, true))
->set($this->marshaller->unmarshall(apcu_fetch($key)))
;
}

return new CacheItem($key, false);
}

/**
* {@inheritdoc}
*/
public function getItems(array $keys = []): iterable
{
foreach ($keys as $key) {
yield $key => $this->getItem($key);
}
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -72,52 +70,11 @@ public function deleteItem(string $key): bool
return apcu_delete($key);
}

/**
* {@inheritdoc}
*/
public function deleteItems(array $keys): bool
{
$ret = true;
foreach ($keys as $key) {
if (!$this->deleteItem($key)) {
$ret = false;
}
}

return $ret;
}

/**
* {@inheritdoc}
*/
public function save(CacheItemInterface $item): bool
{
$this->saveDeferred($item);

return $this->commit();
}

/**
* {@inheritdoc}
*/
public function saveDeferred(CacheItemInterface $item): bool
{
$this->deferred[$item->getKey()] = $item;

return true;
}

/**
* {@inheritdoc}
*/
public function commit(): bool
{
foreach ($this->deferred as $key => $item) {
apcu_store($key, $item->get(), 0);
}

$this->deferred = [];

return true;
return apcu_store($item->getKey(), $this->marshaller->marshall($item->get()), 0);
}
}
Loading

0 comments on commit b67565d

Please sign in to comment.