Skip to content

Commit

Permalink
Add client for speculatively loading values based on URL
Browse files Browse the repository at this point in the history
  • Loading branch information
dlh01 committed Nov 22, 2024
1 parent 436d68f commit df87a6e
Show file tree
Hide file tree
Showing 14 changed files with 515 additions and 154 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

This library adheres to [Semantic Versioning](https://semver.org/) and [Keep a CHANGELOG](https://keepachangelog.com/en/1.0.0/).

## 0.5.0

### Added

- `Client` interface, extending `Alley\WP\Types\Feature`, for implementing a Big Pit client.
- `Items` class for clients that want to keep their own in-memory cache of fetched items.
- `Big_Speculative_Pit` client for preloading items used the last time a URL was requested.

### Changed

- `Big_Pit` is now in the `Alley\WP\Big_Pit` subnamespace, implements the `Client` interface, and must be instantiated with the `new` keyword.
- The `boot()` method on clients must be called manually.
- The `$wpdb->big_pit` property will be unset if the table is not available.

### Removed

- `Big_Pit::instance()` method.

## 0.4.0

### Added
Expand Down
46 changes: 42 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,33 @@ You can install the package via Composer:
composer require alleyinteractive/wp-big-pit
```

## Usage
## API

The `Alley\WP\Big_Pit\Client` interface describes the create-read-update-delete operations that can be used with the Big Pit. You can type hint against this interface when using Big Pit as a dependency.

```php
namespace Alley\WP\Big_Pit;

use Alley\WP\Types\Feature;

interface Client extends Feature {
public function get( string $key, string $group ): mixed;

public function set( string $key, mixed $value, string $group ): void;

public function delete( string $key, string $group ): void;

public function flush_group( string $group ): void;
}
```

Each item in the Big Pit has a key and a group, much like the WordPress object cache. Each key is unique within a group.

### Direct Access
`Client` extends the `Alley\WP\Types\Feature` interface from the [Type Extensions]() library, which includes a `boot()` method for performing side effects.

You must call `boot()` before using the client. If you are compiling features using the `Features` instance from Type Extensions, you can include the Big Pit client, and it will be booted with the rest of your feature classes.

You can perform CRUD operations directly on The Pit:
## Usage

```php
<?php
Expand All @@ -26,14 +46,32 @@ use Alley\WP\Big_Pit;
$external_id = 'abcdef12345';
$api_response = '{"id":"abcdef12345","title":"The Best Movie Ever","rating":5}';

$big_pit = Big_Pit::instance();
$big_pit = new Big_Pit\Big_Pit();
$big_pit->boot();

$big_pit->set( $external_id, $api_response, 'movie_reviews' );
$big_pit->get( $external_id, 'movie_reviews' ); // '{"id":"abcdef12345","title":"The Best Movie Ever","rating":5}'
$big_pit->delete( $external_id, 'movie_reviews' );
$big_pit->flush_group( 'movie_reviews' );
```

### Speculative Client

The `Big_Speculative_Pit` decorator class tracks the items that are fetched during a given request and preloads those items in a single query the next time the same page is requested.

```php
<?php

use Alley\WP\Big_Pit;

$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();

$big_pit = new Big_Pit\Big_Speculative_Pit(
request: $request,
origin: new Big_Pit\Big_Pit(),
);
```

### PSR-16 Cache Adapter

A PSR-16 adapter is available for caching data in The Pit:
Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"php": "^8.1",
"alleyinteractive/composer-wordpress-autoloader": "^1.0",
"alleyinteractive/wp-psr16": "^0.1.0",
"alleyinteractive/wp-type-extensions": "^2.1"
"alleyinteractive/wp-type-extensions": "^2.1",
"symfony/http-foundation": "^6.4"
},
"require-dev": {
"alleyinteractive/alley-coding-standards": "^2.0",
Expand Down Expand Up @@ -68,4 +69,4 @@
],
"tidy": "[ $COMPOSER_DEV_MODE -eq 0 ] || composer normalize"
}
}
}
72 changes: 19 additions & 53 deletions src/class-big-pit.php → src/big-pit/class-big-pit.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,24 @@
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching

namespace Alley\WP;

use Alley\WP\Types\Feature;
namespace Alley\WP\Big_Pit;

/**
* The Big Pit.
*/
final class Big_Pit implements Feature {
/**
* Singleton.
*
* @var self
*/
private static ?self $instance = null;

/**
* Whether the database is ready to accept queries.
*
* @var bool
*/
private bool $ready = false;

final class Big_Pit implements Client {
/**
* Cached values.
*
* @phpstan-var array<string, array<string, mixed>>
* Fetched items.
*
* @var array[]
* @var Items
*/
private array $cache = [];
private readonly Items $items;

/**
* Instance.
*
* @return self
* Constructor.
*/
public static function instance(): self {
if ( null === self::$instance ) {
self::$instance = new self();
self::$instance->boot();
}

return self::$instance;
public function __construct() {
$this->items = new Items();
}

/**
Expand All @@ -67,10 +42,8 @@ public function boot(): void {

try {
$this->upsert();
$this->ready = true;
} catch ( \Exception $e ) {
// Do nothing.
unset( $e );
unset( $wpdb->big_pit );
}
}

Expand All @@ -84,19 +57,12 @@ public function boot(): void {
public function get( string $key, string $group ): mixed {
global $wpdb;

if ( ! $this->ready ) {
if ( ! isset( $wpdb->big_pit ) ) {
return null;
}

if ( isset( $this->cache[ $group ] ) && array_key_exists( $key, $this->cache[ $group ] ) ) {
$value = $this->cache[ $group ][ $key ];

if ( is_object( $value ) ) {
// Don't reuse the same instance across multiple calls.
$value = clone $value;
}

return $value;
if ( $this->items->has( $key, $group ) ) {
return $this->items->get( $key, $group );
}

$value = $wpdb->get_var(

Check failure on line 68 in src/big-pit/class-big-pit.php

View workflow job for this annotation

GitHub Actions / PR Tests PHP 8.1 WordPress latest multisite false

Call to an undefined method object::get_var().
Expand All @@ -111,7 +77,7 @@ public function get( string $key, string $group ): mixed {
$value = maybe_unserialize( $value );
}

$this->cache[ $group ][ $key ] = $value;
$this->items->add( $key, $value, $group );

return $value;
}
Expand All @@ -126,7 +92,7 @@ public function get( string $key, string $group ): mixed {
public function set( string $key, mixed $value, string $group ): void {
global $wpdb;

if ( ! $this->ready ) {
if ( ! isset( $wpdb->big_pit ) ) {
return;
}

Expand Down Expand Up @@ -164,7 +130,7 @@ public function set( string $key, mixed $value, string $group ): void {
);
}

unset( $this->cache[ $group ][ $key ] );
$this->items->remove( $key, $group );
}

/**
Expand All @@ -176,7 +142,7 @@ public function set( string $key, mixed $value, string $group ): void {
public function delete( string $key, string $group ): void {
global $wpdb;

if ( ! $this->ready ) {
if ( ! isset( $wpdb->big_pit ) ) {
return;
}

Expand All @@ -189,7 +155,7 @@ public function delete( string $key, string $group ): void {
[ '%s', '%s' ],
);

unset( $this->cache[ $group ][ $key ] );
$this->items->remove( $key, $group );
}

/**
Expand All @@ -200,7 +166,7 @@ public function delete( string $key, string $group ): void {
public function flush_group( string $group ): void {
global $wpdb;

if ( ! $this->ready ) {
if ( ! isset( $wpdb->big_pit ) ) {
return;
}

Expand All @@ -212,7 +178,7 @@ public function flush_group( string $group ): void {
[ '%s' ],
);

unset( $this->cache[ $group ] );
$this->items->remove_group( $group );
}

/**
Expand Down
Loading

0 comments on commit df87a6e

Please sign in to comment.