Development log - 2022/42

Development log - 2022/42

Stash

Stash

A new addition to DecodeLabs this week, Stash brings a comprehensive caching system to the ecosystem.

Implementing both PSR-6 Cache and PSR-16 Simple Cache standard interfaces, Stash provides simple and flexible means to store volatile data quickly and easily.

use DecodeLabs\Stash;

$myCache = Stash::get('MyCache');

if(!$cache->has('myValue')) {
    $cache->set('myValue', [1, 2, 3]);
}

$total = 0;

foreach($cache->get('myValue', []) as $number) {
    $total += $number;
}

$cache->delete('myValue');

Use the fetch method to ensure a cache value is in place in one call:

$myValue = $myCache->fetch('myValue', function() {
    return [1, 2, 3]; // Only called if key not found in cache
});

Array access methods provide quick offset access to cache data:

if(!isset($myCache['myValue'])) {
    $myCache['myValue'] = 'Hello world';
}

echo $myCache['myValue'];
unset($MyCache['myValue']);

Object access works the same way as ArrayAccess, but with the PSR6 Cache Item object as the return:

$item = $myCache->myValue;

if(!$item->isHit()) {
    $item->set('Hello world');
}

echo $item->get();
$item->delete();

Drivers

The following drivers are available out of the box:

  • Memcache
  • Redis
  • Predis (native PHP redis client)
  • APCu
  • File (serialized data)
  • PhpFile (var_export data)
  • PhpArray (in memory)
  • Blackhole (nothing stored)

However, Stash uses Archetype to load driver classes so additional drivers may be provided by implementing your own Resolver.

By default, Stash will use the best-fit driver for your environment, starting with Memcache, through Redis and APCu, and falling back on the File store.

Please see the Github page for more.

Archetype

Archetype has received a significant addition this week, the Scanner interface.

Resolver classes that implement this interface are able to define the means to scan arbitrary file locations for classes that would exist within the registered namespace.

The ScannerTrait helper trait provides built-in functionality for looking up autoloader paths in Composer's generated loader structure, though you could implement this functionality in whatever form your context requires:

use DecodeLabs\Archetype\Scanner;
use DecodeLabs\Archetype\ScannerTrait;
use Generator;

class MyResolver implements Scanner {
    use ScannerTrait;

    …

    public function scanClasses(): Generator
    {
        // $path => $class
        yield from $this->scanNamespaceClasses(\My\Namespace::class);
    }

    …

}

This can then be used from the front end like so:

foreach(Archetype::scanClasses(\My\Interface::class) as $filePath => $class) {
    $obj = new $class();
}

Coercion

Coercion received some extra additions this week, now able to convert iterables to array, and coerce values to DateTimeInterface and DateInterval objects.

The conversion of iterables is especially helpful when constructing interfaces that can receive iterable as a parameter type - while PHP8.2 will support passing iterable as a type to iterator_to_array(), previous versions require extra workarounds.

use DecodeLabs\Coercion;

$myDate = Coercion::toDateTime('yesterday');
$myInterval = Coercion::toDateIntervalOrNull('+3 days');
$myArray = Coercion::iterableToArray($myIterable);

Exceptional

A small but critical update to Exceptional saw the addition of checks for interface_exists() and trait_exists() in the Autoloader - this clears up some tricky bugs when working with libraries that provide interfaces that Exceptions should implement, such as the PSR Cache interfaces.

By previously just checking for the existence of a class only, the Autoloader could attempt to auto-generate an Exceptional class in place of an interface that actually already exists. This would cause a fatal error with very difficult to trace messages and stack traces.