Skip to content

Commit

Permalink
Merge pull request #13 from keepsuit/drop-metadata
Browse files Browse the repository at this point in the history
extract drop metadata only once per drop class
  • Loading branch information
cappuc authored Mar 4, 2024
2 parents 95f1766 + ef09bcb commit 1b8e640
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 68 deletions.
70 changes: 6 additions & 64 deletions src/Drop.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,15 @@

use Keepsuit\Liquid\Concerns\ContextAware;
use Keepsuit\Liquid\Contracts\IsContextAware;
use Keepsuit\Liquid\Drops\Cache;
use Keepsuit\Liquid\Drops\Hidden;
use Keepsuit\Liquid\Exceptions\UndefinedDropMethodException;
use Keepsuit\Liquid\Support\DropMetadata;
use Keepsuit\Liquid\Support\Str;
use ReflectionClass;
use ReflectionMethod;
use Traversable;

class Drop implements IsContextAware
{
use ContextAware;

/**
* @var string[]
*/
private ?array $invokableMethods = null;

/**
* @var string[]
*/
private ?array $cacheableMethods = null;
private ?DropMetadata $metadata = null;

private array $cache = [];

Expand All @@ -40,8 +28,8 @@ public function __toString(): string

public function __get(string $name): mixed
{
$invokableMethods = $this->getInvokableMethods();
$cacheableMethods = $this->getCacheableMethods();
$invokableMethods = $this->getMetadata()->invokableMethods;
$cacheableMethods = $this->getMetadata()->cacheableMethods;

$possibleNames = [
$name,
Expand Down Expand Up @@ -85,54 +73,8 @@ public function __get(string $name): mixed
return null;
}

protected function getInvokableMethods(): array
protected function getMetadata(): DropMetadata
{
if ($this->invokableMethods === null) {
$this->init();
}

return $this->invokableMethods;
}

protected function getCacheableMethods(): array
{
if ($this->cacheableMethods === null) {
$this->init();
}

return $this->cacheableMethods;
}

/**
* @phpstan-assert !null $this->invokableMethods
* @phpstan-assert !null $this->cacheableMethods
*/
private function init(): void
{
$blacklist = array_map(
fn (ReflectionMethod $method) => $method->getName(),
(new ReflectionClass(Drop::class))->getMethods(ReflectionMethod::IS_PUBLIC)
);

if ($this instanceof Traversable) {
$blacklist = [...$blacklist, 'current', 'next', 'key', 'valid', 'rewind'];
}

$publicMethods = (new ReflectionClass($this))->getMethods(ReflectionMethod::IS_PUBLIC);

$visibleMethodNames = array_map(
fn (ReflectionMethod $method) => $method->getAttributes(Hidden::class) !== [] ? null : $method->getName(),
$publicMethods
);

$this->invokableMethods = array_values(array_filter(
array_diff($visibleMethodNames, $blacklist),
fn (?string $name) => $name !== null && ! str_starts_with($name, '__')
));

$this->cacheableMethods = array_values(array_filter(array_map(
fn (ReflectionMethod $method) => $method->getAttributes(Cache::class) !== [] ? $method->getName() : null,
$publicMethods
)));
return $this->metadata ??= DropMetadata::init($this);
}
}
62 changes: 62 additions & 0 deletions src/Support/DropMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Keepsuit\Liquid\Support;

use Keepsuit\Liquid\Drop;
use Keepsuit\Liquid\Drops\Cache;
use Keepsuit\Liquid\Drops\Hidden;
use ReflectionClass;
use ReflectionMethod;
use Traversable;

final class DropMetadata
{
/**
* @var array<string,mixed>
*/
protected static array $cache = [];

public static function init(Drop $drop): DropMetadata
{
if (isset(self::$cache[get_class($drop)])) {
return self::$cache[get_class($drop)];
}

$blacklist = array_map(
fn (ReflectionMethod $method) => $method->getName(),
(new ReflectionClass(Drop::class))->getMethods(ReflectionMethod::IS_PUBLIC)
);

if ($drop instanceof Traversable) {
$blacklist = [...$blacklist, 'current', 'next', 'key', 'valid', 'rewind'];
}

$publicMethods = (new ReflectionClass($drop))->getMethods(ReflectionMethod::IS_PUBLIC);

$visibleMethodNames = array_map(
fn (ReflectionMethod $method) => $method->getAttributes(Hidden::class) !== [] ? null : $method->getName(),
$publicMethods
);

$invokableMethods = array_values(array_filter(
array_diff($visibleMethodNames, $blacklist),
fn (?string $name) => $name !== null && ! str_starts_with($name, '__')
));

$cacheableMethods = array_values(array_filter(array_map(
fn (ReflectionMethod $method) => $method->getAttributes(Cache::class) !== [] ? $method->getName() : null,
$publicMethods
)));

return self::$cache[get_class($drop)] = new DropMetadata(
invokableMethods: $invokableMethods,
cacheableMethods: $cacheableMethods
);
}

public function __construct(
public readonly array $invokableMethods,
public readonly array $cacheableMethods
) {
}
}
18 changes: 14 additions & 4 deletions tests/Integration/DropTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use Keepsuit\Liquid\Render\RenderContext;
use Keepsuit\Liquid\Tests\Stubs\CachableDrop;
use Keepsuit\Liquid\Tests\Stubs\ContextDrop;
use Keepsuit\Liquid\Tests\Stubs\EnumerableDrop;
use Keepsuit\Liquid\Tests\Stubs\ProductDrop;
Expand Down Expand Up @@ -105,13 +106,22 @@
expect(renderTemplate('{{ collection }}', ['collection' => new EnumerableDrop()]))->toBe(EnumerableDrop::class);
});

test('invokable methods', function () {
expect(invade(new ProductDrop())->getInvokableMethods())->toBe(['texts', 'catchall', 'context']);
expect(invade(new EnumerableDrop())->getInvokableMethods())->toBe(['size', 'first', 'count', 'min', 'max']);
test('drop metadata', function () {
expect(invade(new ProductDrop())->getMetadata())
->invokableMethods->toBe(['texts', 'catchall', 'context'])
->cacheableMethods->toBe([]);

expect(invade(new EnumerableDrop())->getMetadata())
->invokableMethods->toBe(['size', 'first', 'count', 'min', 'max'])
->cacheableMethods->toBe([]);

expect(invade(new CachableDrop())->getMetadata())
->invokableMethods->toBe(['notCached', 'cached'])
->cacheableMethods->toBe(['cached']);
});

it('can cache drop method calls', function () {
$drop = new \Keepsuit\Liquid\Tests\Stubs\CachableDrop();
$drop = new CachableDrop();

expect($drop)
->notCached->toBe(0)
Expand Down

0 comments on commit 1b8e640

Please sign in to comment.