Skip to content

Commit

Permalink
feat: redis driver (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
azuradara authored Jul 13, 2024
1 parent 4ccbf5f commit d477907
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 19 deletions.
7 changes: 7 additions & 0 deletions src/Contracts/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,11 @@ public function get(string $name, $context);
* @param (callable(mixed $context): mixed) $resolver
*/
public function define(string $name, callable $resolver): void;

/**
* @param mixed $context
* @param mixed $value
*
*/
public function set(string $name, $context, $value): void;
}
46 changes: 46 additions & 0 deletions src/Drivers/ArrayDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace YouCanShop\Foggle\Drivers;

use Illuminate\Contracts\Events\Dispatcher;
use stdClass;
use YouCanShop\Foggle\Contracts\Driver;

class ArrayDriver implements Driver
Expand All @@ -13,6 +14,12 @@ class ArrayDriver implements Driver
/** @var array<string, (callable(mixed $context): mixed)> */
protected array $resolvers;

/** @var array<string, array<string, mixed>> */
protected array $resolved = [];

/** @var stdClass */
protected stdClass $unknown;

/**
* @param Dispatcher $dispatcher
* @param array<string, (callable(mixed $context): mixed)> $resolvers
Expand All @@ -21,6 +28,8 @@ public function __construct(Dispatcher $dispatcher, array $resolvers)
{
$this->dispatcher = $dispatcher;
$this->resolvers = $resolvers;

$this->unknown = new stdClass;
}

/**
Expand All @@ -36,9 +45,46 @@ public function defined(): array
*/
public function get(string $name, $context)
{
$key = foggle()->serialize($context);

if (isset($this->resolved[$name][$key])) {
return $this->resolved[$name][$key];
}

return with(
$this->resolveValue($name, $context),
function ($value) use ($name, $key) {
if ($value === $this->unknown) {
return false;
}

$this->set($name, $key, $value);

return $value;
}
);
}

/**
* @param mixed $context
*
* @return mixed
*/
protected function resolveValue(string $name, $context)
{
if (!array_key_exists($name, $this->resolvers)) {
return $this->unknown;
}

return $this->resolvers[$name]($context);
}

public function set(string $name, $context, $value): void
{
$this->resolved[$name] = $this->resolved[$name] ?? [];
$this->resolved[$name][foggle()->serialize($context)] = $value;
}

/**
* @inheritDoc
*/
Expand Down
77 changes: 66 additions & 11 deletions src/Drivers/Decorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

use Closure;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Symfony\Component\Finder\Finder;
use YouCanShop\Foggle\Contracts\Driver;
use YouCanShop\Foggle\Contracts\Foggable;
use YouCanShop\Foggle\FeatureInteraction;
use YouCanShop\Foggle\Lazily;

Expand All @@ -15,31 +17,41 @@
*/
class Decorator implements Driver
{
/** @var Collection<int, array{ name: string, context: mixed, value: mixed}> */
protected $cache;

/** @var string */
private string $name;

/** @var Driver */
private Driver $driver;

/** @var Container */
private Container $container;

/**
* @param string $name
* @param Driver $driver
* @param Container $container
* @param Collection<int, array{ name: string, context: mixed, value: mixed}> $cache
*/
public function __construct(
string $name,
Driver $driver,
Container $container
Container $container,
Collection $cache
) {
$this->name = $name;
$this->driver = $driver;
$this->container = $container;
$this->cache = $cache;
}

public function discover(string $namespace = 'App\\Features', ?string $path = null): void
{
$namespace = Str::finish($namespace, '\\');

$entries = (new Finder)
->files()
->name('*.php')
->depth(0)
->in($path ?? base_path('app/Features'));
$entries = (new Finder)->files()->name('*.php')->depth(0)->in($path ?? base_path('app/Features'));

collect($entries)->each(fn($file) => $this->define("$namespace{$file->getBasename('.php')}"));
}
Expand All @@ -59,10 +71,7 @@ public function define(string $name, callable $resolver = null): void
$this->driver->define($name, function ($context) use ($name, $resolver) {
if ($resolver instanceof Lazily) {
$resolver = with(
$this->container[$resolver->feature],
fn($i) => method_exists($i, 'resolve')
? $i->resolve($context)
: $i($context)
$this->container[$resolver->feature], fn($i) => method_exists($i, 'resolve') ? $i->resolve($context) : $i($context)
);
}

Expand All @@ -88,14 +97,60 @@ protected function resolve(string $name, callable $resolver, $context)

public function get(string $name, $context)
{
return $this->driver->get($name, $context);
$context = $this->parseContext($context);

$item = $this->cache->whereStrict('context', foggle()->serialize($context))->whereStrict('name', $name)->first();

if ($item !== null) {
return $item['value'];
}

$value = $this->driver->get($name, $context);
$this->cPut($name, $context, $value);

return $value;
}

protected function parseContext($context)
{
return $context instanceof Foggable ? $context->foggleId() : $context;
}

/**
* @param string $name
* @param mixed $context
* @param mixed $value
*
* @return void
*/
protected function cPut(string $name, $context, $value): void
{
$key = foggle()->serialize($context);

$index = $this->cache->search(
fn($i) => $i['name'] === $name && $i['context'] === $key
);

$index === false ? $this->cache[] = ['name' => $name, 'context' => $key, 'value' => $value] : $this->cache[$index] = [
'name' => $name,
'context' => $key,
'value' => $value,
];
}

public function defined(): array
{
return $this->driver->defined();
}

public function set(string $name, $context, $value): void
{
$context = $this->parseContext($context);
$this->driver->set($name, $context, $value);

$this->cPut($name, $context, $value);
}

/**
* @param string $name
* @param array $arguments
Expand Down
102 changes: 102 additions & 0 deletions src/Drivers/RedisDriver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace YouCanShop\Foggle\Drivers;

use Illuminate\Contracts\Config\Repository as Config;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Redis\Connections\Connection;
use Illuminate\Redis\RedisManager;
use stdClass;
use YouCanShop\Foggle\Contracts\Driver;

class RedisDriver implements Driver
{
protected stdClass $unknown;

protected string $name;
protected string $prefix;
protected RedisManager $redis;
protected Config $config;
protected Dispatcher $dispatcher;

/** @var array<string, (callable(mixed $context): mixed)> */
protected array $resolvers;

public function __construct(
string $name,
array $resolvers,
RedisManager $redis,
Config $config,
Dispatcher $dispatcher
) {
$this->name = $name;
$this->resolvers = $resolvers;
$this->redis = $redis;
$this->config = $config;
$this->dispatcher = $dispatcher;

$this->unknown = new stdClass;
$this->prefix = $this->config->get("foggle.stores.$this->name.prefix");
}

public function get(string $name, $context)
{
$key = foggle()->serialize($context);

$result = $this->connection()->command(
'HGET',
["$this->prefix:$name", $key]
);

if ($result) {
return $result;
}

return with(
$this->resolveValue($name, $context),
function ($value) use ($name, $key) {
if ($value === $this->unknown) {
return false;
}

$this->set($name, $key, $value);

return $value;
}
);
}

protected function connection(): Connection
{
return $this->redis->connection(
$this->config->get("foggle.stores.$this->name.connection")
);
}

protected function resolveValue(string $feature, $context)
{
if (!array_key_exists($feature, $this->resolvers)) {
return $this->unknown;
}

return $this->resolvers[$feature]($context);
}

public function set(string $name, $context, $value): void
{
if ($context) {
$key = foggle()->serialize($context);
$this->connection()->command('HSET', ["$this->prefix:$name", $key, $value]);
}
}

public function defined(): array
{
return array_keys($this->resolvers);
}

public function define(string $name, callable $resolver): void
{
$this->resolvers[$name] = $resolver;
}
}
10 changes: 8 additions & 2 deletions src/FogGen.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ class FogGen
{
/**
* @param string $path The config's dotted path
* @param string $separator
*
* @return callable
*/
public static function inconfig(string $path): callable
public static function inconfig(string $path, string $separator = ','): callable
{
return function ($context) use ($path) {
return function ($context) use ($path, $separator) {
$config = config($path, []);

if (is_string($config)) {
$config = splode($config, $separator);
}

if (in_array('*', $config)) {
return true;
}
Expand Down
Loading

0 comments on commit d477907

Please sign in to comment.