Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extensions #30

Merged
merged 7 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ parameters:
count: 1
path: performance/Shopify/Database.php

-
message: "#^Strict comparison using \\!\\=\\= between null and null will always evaluate to false\\.$#"
count: 1
path: src/Profiler/Profiler.php

-
message: "#^Parameter \\#3 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#"
count: 1
Expand Down
5 changes: 3 additions & 2 deletions src/Attributes/Hidden.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
use Attribute;

/**
* This attribute can be used to mark a drop method as hidden,
* so it won't be exposed to the liquid context.
* This attribute can be used to hide:
* - a drop method, so it won't be exposed to the liquid context.
* - a FiltersProvider method, so it won't be registered as a filter.
*/
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY)]
class Hidden {}
2 changes: 2 additions & 0 deletions src/Concerns/ContextAware.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

namespace Keepsuit\Liquid\Concerns;

use Keepsuit\Liquid\Attributes\Hidden;
use Keepsuit\Liquid\Render\RenderContext;

trait ContextAware
{
protected RenderContext $context;

#[Hidden]
public function setContext(RenderContext $context): void
{
$this->context = $context;
Expand Down
29 changes: 29 additions & 0 deletions src/Contracts/LiquidExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Keepsuit\Liquid\Contracts;

use Keepsuit\Liquid\Filters\FiltersProvider;
use Keepsuit\Liquid\Tag;

interface LiquidExtension
{
/**
* @return array<class-string<Tag>>
*/
public function getTags(): array;

/**
* @return array<class-string<FiltersProvider>>
*/
public function getFiltersProviders(): array;

/**
* @return array<string,mixed>
*/
public function getRegisters(): array;

/**
* @return array<NodeVisitor>
*/
public function getNodeVisitors(): array;
}
12 changes: 12 additions & 0 deletions src/Contracts/NodeVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Keepsuit\Liquid\Contracts;

use Keepsuit\Liquid\Nodes\Node;

interface NodeVisitor
{
public function enterNode(Node $node): void;

public function leaveNode(Node $node): void;
}
87 changes: 82 additions & 5 deletions src/Environment.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
namespace Keepsuit\Liquid;

use Keepsuit\Liquid\Contracts\LiquidErrorHandler;
use Keepsuit\Liquid\Contracts\LiquidExtension;
use Keepsuit\Liquid\Contracts\LiquidFileSystem;
use Keepsuit\Liquid\ErrorHandlers\DefaultErrorHandler;
use Keepsuit\Liquid\Exceptions\LiquidException;
use Keepsuit\Liquid\Extensions\StandardExtension;
use Keepsuit\Liquid\FileSystems\BlankFileSystem;
use Keepsuit\Liquid\Parse\ParseContext;
use Keepsuit\Liquid\Render\RenderContext;
use Keepsuit\Liquid\Render\RenderContextOptions;
use Keepsuit\Liquid\Render\ResourceLimits;
use Keepsuit\Liquid\Support\Arr;
use Keepsuit\Liquid\Support\FilterRegistry;
use Keepsuit\Liquid\Support\TagRegistry;

Expand All @@ -30,27 +33,39 @@ class Environment

public readonly RenderContextOptions $defaultRenderContextOptions;

/**
* @var array<class-string<LiquidExtension>, LiquidExtension>
*/
protected array $extensions = [];

public function __construct(
?TagRegistry $tagRegistry = null,
?FilterRegistry $filterRegistry = null,
?LiquidFileSystem $fileSystem = null,
?LiquidErrorHandler $errorHandler = null,
?ResourceLimits $defaultResourceLimits = null,
?RenderContextOptions $defaultRenderContextOptions = null,
public readonly bool $profile = false,
/** @var LiquidExtension[] $extensions */
array $extensions = [],
) {
$this->tagRegistry = $tagRegistry ?? TagRegistry::default();
$this->filterRegistry = $filterRegistry ?? FilterRegistry::default();
$this->tagRegistry = $tagRegistry ?? new TagRegistry;
$this->filterRegistry = $filterRegistry ?? new FilterRegistry;
$this->fileSystem = $fileSystem ?? new BlankFileSystem;
$this->errorHandler = $errorHandler ?? new DefaultErrorHandler;
$this->defaultResourceLimits = $defaultResourceLimits ?? new ResourceLimits;
$this->defaultRenderContextOptions = $defaultRenderContextOptions ?? new RenderContextOptions;

foreach ($extensions as $extension) {
$this->addExtension($extension);
}
}

public static function default(): Environment
{
if (! isset(self::$defaultEnvironment)) {
self::$defaultEnvironment = new self;
self::$defaultEnvironment = new Environment(
extensions: [new StandardExtension]
);
}

return self::$defaultEnvironment;
Expand Down Expand Up @@ -91,7 +106,6 @@ public function newRenderContext(
data: $data,
staticData: $staticData,
registers: $registers,
profile: $this->profile,
options: $options ?? $this->defaultRenderContextOptions,
resourceLimits: $resourceLimits,
environment: $this
Expand All @@ -115,4 +129,67 @@ public function parseTemplate(string $templateName): Template

return Template::parse($this->newParseContext(), $source, $templateName);
}

public function addExtension(LiquidExtension $extension): static
{
$this->extensions[$extension::class] = $extension;

foreach ($extension->getTags() as $tag) {
$this->tagRegistry->register($tag);
}

foreach ($extension->getFiltersProviders() as $filtersProvider) {
$this->filterRegistry->register($filtersProvider);
}

return $this;
}

/**
* @param class-string<LiquidExtension> $extensionClass
*/
public function removeExtension(string $extensionClass): static
{
$extension = $this->extensions[$extensionClass] ?? null;

if ($extension === null) {
return $this;
}

unset($this->extensions[$extensionClass]);

foreach ($extension->getTags() as $tag) {
$this->tagRegistry->delete($tag::tagName());
}

foreach ($extension->getFiltersProviders() as $filtersProvider) {
$this->filterRegistry->delete($filtersProvider);
}

return $this;
}

/**
* @return array<LiquidExtension>
*/
public function getExtensions(): array
{
return array_values($this->extensions);
}

public function getNodeVisitors(): array
{
return Arr::flatten(Arr::map(
$this->getExtensions(),
fn (LiquidExtension $extension) => $extension->getNodeVisitors()
));
}

public function getRegisters(): array
{
return array_merge(...Arr::map(
$this->getExtensions(),
fn (LiquidExtension $extension) => $extension->getRegisters()
));
}
}
29 changes: 18 additions & 11 deletions src/EnvironmentFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
namespace Keepsuit\Liquid;

use Keepsuit\Liquid\Contracts\LiquidErrorHandler;
use Keepsuit\Liquid\Contracts\LiquidExtension;
use Keepsuit\Liquid\Contracts\LiquidFileSystem;
use Keepsuit\Liquid\ErrorHandlers\DefaultErrorHandler;
use Keepsuit\Liquid\Extensions\StandardExtension;
use Keepsuit\Liquid\FileSystems\BlankFileSystem;
use Keepsuit\Liquid\Filters\FiltersProvider;
use Keepsuit\Liquid\Render\RenderContextOptions;
Expand All @@ -26,16 +28,21 @@ final class EnvironmentFactory

protected RenderContextOptions $defaultRenderContextOptions;

protected bool $profile = false;
/**
* @var array<class-string<LiquidExtension>, LiquidExtension>
*/
protected array $extensions = [];

public function __construct()
{
$this->tagRegistry = TagRegistry::default();
$this->filterRegistry = FilterRegistry::default();
$this->tagRegistry = new TagRegistry;
$this->filterRegistry = new FilterRegistry;
$this->fileSystem = new BlankFileSystem;
$this->errorHandler = new DefaultErrorHandler;
$this->resourceLimits = new ResourceLimits;
$this->defaultRenderContextOptions = new RenderContextOptions;

$this->addExtension(new StandardExtension);
}

public static function new(): EnvironmentFactory
Expand Down Expand Up @@ -78,13 +85,6 @@ public function setResourceLimits(ResourceLimits $resourceLimits): EnvironmentFa
return $this;
}

public function setProfile(bool $profile = true): EnvironmentFactory
{
$this->profile = $profile;

return $this;
}

public function setRethrowErrors(bool $rethrowErrors = true): EnvironmentFactory
{
$this->defaultRenderContextOptions = new RenderContextOptions(
Expand Down Expand Up @@ -138,6 +138,13 @@ public function registerFilters(string $filtersProvider): EnvironmentFactory
return $this;
}

public function addExtension(LiquidExtension $extension): EnvironmentFactory
{
$this->extensions[$extension::class] = $extension;

return $this;
}

public function build(): Environment
{
return new Environment(
Expand All @@ -146,7 +153,7 @@ public function build(): Environment
fileSystem: $this->fileSystem,
defaultResourceLimits: $this->resourceLimits,
defaultRenderContextOptions: $this->defaultRenderContextOptions,
profile: $this->profile,
extensions: array_values($this->extensions),
);
}
}
28 changes: 28 additions & 0 deletions src/Extensions/Extension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Keepsuit\Liquid\Extensions;

use Keepsuit\Liquid\Contracts\LiquidExtension;

abstract class Extension implements LiquidExtension
{
public function getTags(): array
{
return [];
}

public function getFiltersProviders(): array
{
return [];
}

public function getRegisters(): array
{
return [];
}

public function getNodeVisitors(): array
{
return [];
}
}
29 changes: 29 additions & 0 deletions src/Extensions/ProfilerExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Keepsuit\Liquid\Extensions;

use Keepsuit\Liquid\Profiler\Profiler;
use Keepsuit\Liquid\Profiler\ProfilerNodeVisitor;

class ProfilerExtension extends Extension
{
public function __construct(
protected Profiler $profiler,
/** Enable tags profiling */
protected bool $tags = false,
/** Enable variables profiling */
protected bool $variables = false,
) {}

public function getNodeVisitors(): array
{
return [new ProfilerNodeVisitor(tags: $this->tags, variables: $this->variables)];
}

public function getRegisters(): array
{
return [
'profiler' => $this->profiler,
];
}
}
38 changes: 38 additions & 0 deletions src/Extensions/StandardExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Keepsuit\Liquid\Extensions;

use Keepsuit\Liquid\Filters\StandardFilters;
use Keepsuit\Liquid\Tags;

class StandardExtension extends Extension
{
public function getTags(): array
{
return [
Tags\AssignTag::class,
Tags\BreakTag::class,
Tags\CaptureTag::class,
Tags\CaseTag::class,
Tags\ContinueTag::class,
Tags\CycleTag::class,
Tags\DecrementTag::class,
Tags\EchoTag::class,
Tags\ForTag::class,
Tags\IfChanged::class,
Tags\IfTag::class,
Tags\IncrementTag::class,
Tags\LiquidTag::class,
Tags\RenderTag::class,
Tags\TableRowTag::class,
Tags\UnlessTag::class,
];
}

public function getFiltersProviders(): array
{
return [
StandardFilters::class,
];
}
}
Loading
Loading