Skip to content

Commit

Permalink
Polish and Readme
Browse files Browse the repository at this point in the history
  • Loading branch information
overclokk committed Apr 23, 2023
1 parent aa4742c commit e2b16dc
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 42 deletions.
115 changes: 85 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2FItalyStrap%2Fstorage%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/ItalyStrap/storage/master)

Storage API for WordPress the OOP way
This package provides API for WordPress Transients, Cache, Options, and ThemeMods.

## Table Of Contents

Expand All @@ -32,11 +33,16 @@ This package adheres to the [SemVer](http://semver.org/) specification and will
### What is the purpose of this library?

The first idea is to have a common API for all the storage system in WordPress, like [WordPress Cache API](https://developer.wordpress.org/reference/classes/wp_object_cache/), [WordPress Transients API](https://developer.wordpress.org/apis/transients/), [WordPress Options API](https://developer.wordpress.org/apis/options/), [WordPress Theme Mods API](https://codex.wordpress.org/Theme_Modification_API) and so on.
These libraries try to uniform some WordPress API that have similar behavior under the same umbrella.
This means that you could extend the API of this library to create your own storage system.
To name a few you can use this API for metadata, post meta, user meta, and so on.

### Why the Storage word for the library?

In this case the `Storage` word is used to refer to some data stored in a DB table or in a file as well as in memory like `array` or `object`, but in those case you may need to create your own class.
Now in this library you can find API for `Options` (or similar) and API for `Cache` (or similar), you may ask why :-D, the first reason is that I already have a library called [Cache](https://github.com/ItalyStrap/cache) used to implements the PSR-6 and PSR-16, so I don't want to duplicate the name, and the second reason group all the storage system you could find in WordPress in a single library.

Now in this library you can find API for `Options` (or similar) and API for `Cache` (or similar), you may ask why :-D, the first reason is that I already have a library called [Cache](https://github.com/ItalyStrap/cache) used to implements the PSR-6 and PSR-16, so I don't want to duplicate the name, and the second reason is to group all the similar storage system you could find in WordPress.

Right now the interfaces used in this library are placed in another library called [Common](https://github.com/ItalyStrap/common) because I want to use them in other libraries.

### Why the OOP way?
Expand All @@ -45,23 +51,27 @@ With this library you can inject the storage system you need to use in your clas

This API takes some concept from the [PSR-16](https://www.php-fig.org/psr/psr-16/), and also it could be applied to other kind of storage system in WordPress not only for the Transients, Options, Mods, etc., to naming a few you can use this API for metadata, post meta, user meta, and so on.

If you need a PSR-6 or PSR-16 implementation for WordPress transient and cache you can use the [ItalyStrap\Cache](https://github.com/ItalyStrap/cache) package and this library used as a driver.
If you need a PSR-6 or PSR-16 implementation for WordPress transient and cache you can use the [ItalyStrap\Cache](https://github.com/ItalyStrap/cache) package and this library used as a driver, remember that you can use only the driver that extend the Cache API and not the Store API because of the missing TTL in the Store API.

Think of this like a wrapper around the WordPress Transients API, Options API, Mods API, etc. but with some differences.

### Differences with the WordPress Transients API, Options API, Mods API, etc.

The most important difference is the return value of the `::get()` method, in the WordPress Transients API, Cache API, Option API and so on the `\get_*()` functions return `false` if the result does not exist or has expired, in this API the `::get()` method return `null` if the result does not exist or has expired.
The most important difference is the return value of the `Class::get()` method, in the WordPress Transients API, Cache API, Option API and so on the `\get_*()` functions return `false` if the result does not exist or has expired, in this API the `Class::get()` method return `null` if the result does not exist or has expired.

I'm not a fan of the `null` value but this adheres to the PSR-16 specification where no value means `null` and `false` could be a valid value.

### The expiration time

The `StoreInterface::class` has no expiration time so think of it like a forever storage system that never expire.

If you need to store a value for a specific time you should use the `CacheInterface::class`.

The second difference is that if you provide a `0` value as the expiration time it means you will store the value per `0` second and not `forever` like WordPress does.

Maybe you would ask why?

Because if you provide a `0` second to the expiration time you are telling to the library to store the value for `0` second and not forever (f**k), it does not make any sense to use the `0` value as `forever`.
Because if you provide a `0` second to the expiration time you are telling to the library to store the value for `0` second and not forever (f**k), it does not make any sense to use the `0` value as `forever` (if you have a very good reason to do that please open an issue, and we will discuss about it).
If you want to store a value forever just use the `::set()` method without the expiration time at all or pass `null` as the expiration time, internally the library will set expiration time to `1 year`, and `1 year` should be enough for a `forever` value.

### The expiration time and the Cache API
Expand All @@ -71,10 +81,10 @@ If you install some other plugin that provide to you another implementation of t

### Why the `::update()` method exists?

You may ask why the `::update()` method exists, in fact you could use the `::set()` method to update a value, there was only a `\wp_cache_replace()` function in the WordPress Cache API that return false if value does not exist and `\update_option()` function that instead create a value if does not exist, so I decided to create the `::update()` method to have the same behavior of the Options API.
You may ask why the `::update()` method exists, in fact you could use the `::set()` method to update a value, there was only a `\wp_cache_replace()` function in the WordPress Cache API that return false if value does not exist and `\update_option()` function that instead create a value if it does not exist, so I decided to create the `::update()` method to have the same behavior of the Options API to all other storage system.

It is a bad decision?
I left to you to decide :-D
I leave the decision to you :-D
I think this method is not necessary, but I decided to leave it there for the sake of completeness.

## Basic Usage
Expand Down Expand Up @@ -169,6 +179,8 @@ From [WordPress Transients API docs](https://codex.wordpress.org/Transients_API)

### Timer constants

In the WordPress environment you can use the following constants to set the expiration time if you want.

```php
const MINUTE_IN_SECONDS = 60; // (seconds)
const HOUR_IN_SECONDS = 60 * MINUTE_IN_SECONDS;
Expand All @@ -190,6 +202,42 @@ if (false === ($special_data_to_save = \get_transient('special_data_to_save')))
}
```

And this is the same example above but with this library.

```php
declare(strict_types=1);

namespace Your\Namespace;

use ItalyStrap\Storage\Transient;
$transient = new Transient();

if (false === ($special_data_to_save = $transient->get('special_data_to_save'))) {
// It wasn't there, so regenerate the data and save the transient
$special_data_to_save = ['some-key' => 'come value'];
$transient->set('special_data_to_save', $special_data_to_save, 12 * HOUR_IN_SECONDS);
}
```

The same is with the `Cache::class` class.


```php
declare(strict_types=1);

namespace Your\Namespace;

use ItalyStrap\Storage\Cache;

$cache = new Cache();

if (false === ($special_data_to_save = $cache->get('special_data_to_save'))) {
// It wasn't there, so regenerate the data and save the transient
$special_data_to_save = ['some-key' => 'come value'];
$cache->set('special_data_to_save', $special_data_to_save, 12 * HOUR_IN_SECONDS);
}
```

### Transient API

```php
Expand Down Expand Up @@ -234,23 +282,6 @@ null === $transient->get('mod_1'); // true
null === $transient->get('mod_2'); // true
```


```php
declare(strict_types=1);

namespace Your\Namespace;

use ItalyStrap\Storage\Transient;

$transient = new Transient();

if (false === ($special_data_to_save = $transient->get('special_data_to_save'))) {
// It wasn't there, so regenerate the data and save the transient
$special_data_to_save = ['some-key' => 'come value'];
$transient->set('special_data_to_save', $special_data_to_save, 12 * HOUR_IN_SECONDS);
}
```

### Cache API

```php
Expand Down Expand Up @@ -295,25 +326,49 @@ null === $cache->get('mod_1'); // true
null === $cache->get('mod_2'); // true
```

## Advanced Usage

### How to crete keyword for the Cache API and Transients API

It is good idea to prefix the keyword with some other string like your namespace, your class name, method name, and so on, you could also use constant, this will help you to avoid conflicts with other plugins or themes.

A simple example:

```php
declare(strict_types=1);

namespace Your\Namespace;

use ItalyStrap\Storage\Cache;
use ItalyStrap\Storage\CacheInterface;use phpDocumentor\Reflection\Types\Mixed_;

$cache = new Cache();

if (false === ($special_data_to_save = $cache->get('special_data_to_save'))) {
// It wasn't there, so regenerate the data and save the transient
$special_data_to_save = ['some-key' => 'come value'];
$cache->set('special_data_to_save', $special_data_to_save, 12 * HOUR_IN_SECONDS);
$your_class = new class($cache) {
private $cache;
private $prefix = 'your_namespace';

public function __construct(CacheInterface $cache)
{
$this->cache = $cache;
}

public function getSomething(): mixed
{
$keyword = $this->prefix . __CLASS__ . __METHOD__;

if (false === ($data = $this->cache->get($keyword))) {
// It wasn't there, so regenerate the data and save the transient
$data = ['some-key' => 'come value'];
$this->cache->set($keyword, $data, 12 * HOUR_IN_SECONDS);
}

return $data;
}
};

}
```

## Advanced Usage

## Contributing

All feedback / bug reports / pull requests are welcome.
Expand Down
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
"psalm": [
"@php ./vendor/bin/psalm --no-cache"
],
"clean": [
"@php ./vendor/bin/codecept clean"
],
"tests": [
"@unit",
"@wpunit"
Expand Down
31 changes: 19 additions & 12 deletions src/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,25 @@ class Cache implements CacheInterface, ClearableInterface, IncrDecrInterface
{
use NormalizeTtlTrait;

private string $group;

public function __construct(string $group = 'default')
{
$this->group = $group;
}

public function set(string $key, $value, ?int $ttl = null): bool
{
$ttl = $this->parseTtl($ttl);
return \wp_cache_set($key, $value, 'default', $ttl);
return \wp_cache_set($key, $value, $this->group, $ttl);
}

public function get(string $key, $default = null)
{
/**
* @var mixed $value
*/
$value = \wp_cache_get($key, 'default');
$value = \wp_cache_get($key, $this->group);
if ($value === 0) {
return $value;
}
Expand All @@ -43,7 +50,7 @@ public function update(string $key, $value, ?int $ttl = null): bool

public function delete(string $key): bool
{
return \wp_cache_delete($key, 'default');
return \wp_cache_delete($key, $this->group);
}

/**
Expand All @@ -55,7 +62,7 @@ public function delete(string $key): bool
public function increment(string $key, int $offset = 1)
{
/** @psalm-suppress MixedReturnStatement */
return \wp_cache_incr($key, $offset, 'default');
return \wp_cache_incr($key, $offset, $this->group);
}

/**
Expand All @@ -67,7 +74,7 @@ public function increment(string $key, int $offset = 1)
public function decrement(string $key, int $offset = 1)
{
/** @psalm-suppress MixedReturnStatement */
return \wp_cache_decr($key, $offset, 'default');
return \wp_cache_decr($key, $offset, $this->group);
}

public function clear(): bool
Expand All @@ -77,9 +84,9 @@ public function clear(): bool

public function setMultiple(iterable $values, ?int $ttl = null): bool
{
$newValues = $this->convertArray($values);
$newValues = $this->iteratorToArray($values);
$ttl = $this->parseTtl($ttl);
foreach (\wp_cache_set_multiple($newValues, 'default', $ttl) as $value) {
foreach (\wp_cache_set_multiple($newValues, $this->group, $ttl) as $value) {
if (!$value) {
return false;
}
Expand All @@ -94,19 +101,19 @@ public function setMultiple(iterable $values, ?int $ttl = null): bool
*/
public function getMultiple(iterable $keys, $default = null): iterable
{
$newValues = $this->convertArray($keys);
$newValues = $this->iteratorToArray($keys);
/**
* @var mixed $value
*/
foreach (\wp_cache_get_multiple($newValues, 'default') as $key => $value) {
foreach (\wp_cache_get_multiple($newValues, $this->group) as $key => $value) {
yield $key => $value ?: $default;
}
}

public function deleteMultiple(iterable $keys): bool
{
$newValues = $this->convertArray($keys);
$result = \wp_cache_delete_multiple($newValues, 'default');
$newValues = $this->iteratorToArray($keys);
$result = \wp_cache_delete_multiple($newValues, $this->group);
foreach ($result as $value) {
if (!$value) {
return false;
Expand All @@ -120,7 +127,7 @@ public function deleteMultiple(iterable $keys): bool
* @param iterable $values
* @return array
*/
private function convertArray(iterable $values): array
private function iteratorToArray(iterable $values): array
{
return $values instanceof \Traversable ? \iterator_to_array($values) : $values;
}
Expand Down

0 comments on commit e2b16dc

Please sign in to comment.