Skip to content

Commit

Permalink
Merge pull request #23 from keepsuit/improved-iterable-support
Browse files Browse the repository at this point in the history
Improved iterable support
  • Loading branch information
cappuc authored May 30, 2024
2 parents 3f9b7bd + 454f38b commit 47fb6b2
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 22 deletions.
20 changes: 14 additions & 6 deletions src/Filters/StandardFilters.php
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,9 @@ public function floor(int|float $input): int
}

/**
* Combines all of the items in an array into a single string, separated by a space.
* Combines all the items in an array into a single string, separated by a space.
*/
public function join(array $input, string $glue = ' '): string
public function join(iterable $input, string $glue = ' '): string
{
return implode($glue, $this->mapToLiquid($input));
}
Expand Down Expand Up @@ -385,7 +385,7 @@ public function replaceLast(string $input, string $search, string $replace): str
/**
* Reverses the order of the items in an array.
*/
public function reverse(array $input): array
public function reverse(iterable $input): array
{
return array_reverse($this->mapToLiquid($input));
}
Expand All @@ -401,20 +401,28 @@ public function round(int|float $input, int $precision = 0): int|float
/**
* Returns the size of an array or a string.
*/
public function size(string|array|null $input): int
public function size(string|iterable|null $input): int
{
if ($input === null) {
return 0;
}

if (is_iterable($input) && ! is_array($input)) {
$input = iterator_to_array($input);
}

return is_array($input) ? count($input) : Str::length($input);
}

/**
* Returns a substring or series of array items, starting at a given 0-based index.
*/
public function slice(string|array|null $input, int $start, int $length = 1): string|array
public function slice(string|iterable|null $input, int $start, int $length = 1): string|array
{
if (is_iterable($input) && ! is_array($input)) {
$input = iterator_to_array($input);
}

$count = static::size($input);

if (abs($start) >= $count) {
Expand Down Expand Up @@ -626,7 +634,7 @@ public function truncatewords(?string $input, int $words = 15, string $ellipsis
/**
* Removes any duplicate items in an array.
*/
public function uniq(array $input, ?string $property = null): array
public function uniq(iterable $input, ?string $property = null): array
{
return Arr::unique($this->mapToLiquid($input), $property);
}
Expand Down
16 changes: 8 additions & 8 deletions src/Nodes/VariableLookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,17 @@ public function evaluate(RenderContext $context): mixed

assert(is_string($key) || is_int($key));

$object = match (true) {
is_array($object) && array_key_exists($key, $object) => $context->internalContextLookup($object, $key),
is_object($object) => $context->internalContextLookup($object, $key),
in_array($i, $this->lookupFilters) => $this->applyFilter($context, $object, (string) $key),
default => $context->internalContextLookup($object, $key),
};

if ($object instanceof MissingValue) {
$nextObject = $context->internalContextLookup($object, $key);

if ($nextObject instanceof MissingValue && is_iterable($object) && in_array($i, $this->lookupFilters)) {
$nextObject = $context->applyFilter($lookup, $object);
}

if ($nextObject instanceof MissingValue) {
continue 2;
}

$object = $nextObject;
if ($object instanceof IsContextAware) {
$object->setContext($context);
}
Expand Down
17 changes: 12 additions & 5 deletions src/Render/RenderContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
use Keepsuit\Liquid\Contracts\IsContextAware;
use Keepsuit\Liquid\Contracts\LiquidFileSystem;
use Keepsuit\Liquid\Contracts\MapsToLiquid;
use Keepsuit\Liquid\Drop;
use Keepsuit\Liquid\Exceptions\ArithmeticException;
use Keepsuit\Liquid\Exceptions\InternalException;
use Keepsuit\Liquid\Exceptions\LiquidException;
use Keepsuit\Liquid\Exceptions\ResourceLimitException;
use Keepsuit\Liquid\Exceptions\StackLevelException;
use Keepsuit\Liquid\Exceptions\StandardException;
use Keepsuit\Liquid\Exceptions\UndefinedDropMethodException;
use Keepsuit\Liquid\FileSystems\BlankFileSystem;
use Keepsuit\Liquid\Interrupts\Interrupt;
use Keepsuit\Liquid\Nodes\VariableLookup;
Expand Down Expand Up @@ -190,11 +192,16 @@ public function findVariables(string $key): array

public function internalContextLookup(mixed $scope, int|string $key): mixed
{
$value = match (true) {
is_array($scope) && array_key_exists($key, $scope) => $scope[$key],
is_object($scope) => $scope->$key,
default => new MissingValue(),
};
try {
$value = match (true) {
is_array($scope) && array_key_exists($key, $scope) => $scope[$key],
$scope instanceof Drop => $scope->{$key},
is_object($scope) && property_exists($scope, (string) $key) => $scope->{$key},
default => new MissingValue(),
};
} catch (UndefinedDropMethodException) {
return new MissingValue();
}

return $this->normalizeValue($value);
}
Expand Down
8 changes: 8 additions & 0 deletions tests/Integration/Tags/StandardTagTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,14 @@
);
});

test('size of iterable', function () {
assertTemplateResult(
'array has 4 elements',
'array has {{ array.size }} elements',
assigns: ['array' => new \Keepsuit\Liquid\Tests\Stubs\Collection([1, 2, 3, 4])],
);
});

test('illegal symbols', function () {
assertTemplateResult('', '{% if true == empty %}?{% endif %}');
assertTemplateResult('', '{% if true == null %}?{% endif %}');
Expand Down
5 changes: 2 additions & 3 deletions tests/Integration/TemplateTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?php

use Keepsuit\Liquid\Exceptions\ResourceLimitException;
use Keepsuit\Liquid\Exceptions\UndefinedDropMethodException;
use Keepsuit\Liquid\Exceptions\UndefinedFilterException;
use Keepsuit\Liquid\Exceptions\UndefinedVariableException;
use Keepsuit\Liquid\Render\RenderContext;
Expand Down Expand Up @@ -196,7 +195,7 @@
if ($strict) {
expect($template->getErrors())
->toHaveCount(1)
->{0}->toBeInstanceOf(UndefinedDropMethodException::class);
->{0}->toBeInstanceOf(UndefinedVariableException::class);
} else {
expect($template->getErrors())->toBeEmpty();
}
Expand All @@ -218,7 +217,7 @@
);

if ($strict) {
expect(fn () => $template->render($context))->toThrow(UndefinedDropMethodException::class);
expect(fn () => $template->render($context))->toThrow(UndefinedVariableException::class);
} else {
expect($template->render($context))->toBe('text1 ');
}
Expand Down
52 changes: 52 additions & 0 deletions tests/Stubs/Collection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace Keepsuit\Liquid\Tests\Stubs;

/**
* @implements \Iterator<int, mixed>
*/
class Collection implements \Countable, \Iterator
{
protected int $index = 0;

protected array $data;

public function __construct(?array $data = null)
{
$this->data = $data ?? [
['foo' => 1, 'bar' => 2],
['foo' => 2, 'bar' => 1],
['foo' => 3, 'bar' => 3],
];
}

public function current(): mixed
{
return $this->data[$this->index];
}

public function next(): void
{
$this->index++;
}

public function key(): mixed
{
return $this->index;
}

public function valid(): bool
{
return isset($this->data[$this->index]);
}

public function rewind(): void
{
$this->index = 0;
}

public function count(): int
{
return count($this->data);
}
}

0 comments on commit 47fb6b2

Please sign in to comment.