diff --git a/CHANGELOG.md b/CHANGELOG.md index 67b46fc..06f7ac4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Added support for new banner option `fetchpriority`. +- Added support for banner options defined in the AMP administration. + +### Changed +- The option `loading` is now processed as an expression. The option `loading-offset` is ignored. +- Updated docs. + ## [1.2.0] - 2024-04-04 ### Added - Added integration of the new AMP API fields `mode` and `dimensions`. diff --git a/docs/integration-without-framework.md b/docs/integration-without-framework.md index d42c828..ae3f0bb 100644 --- a/docs/integration-without-framework.md +++ b/docs/integration-without-framework.md @@ -261,24 +261,53 @@ echo $renderer->renderClientSide(new Position('homepage.top'), [], [ ``` A special case is a position of type `multiple`, where it may be desirable to lazily load all banners except the first. -This can be achieved by adding the option `loading-offset`, whose value specifies from which banner the attribute `loading` should be rendered. +his can be achieved with the following expression: ```php # server-side rendering: echo $renderer->render($response->getPosition('homepage.top'), [], [ - 'loading' => 'lazy', - 'loading-offset' => 1, + 'loading' => '>=1:lazy', ]); # client-side rendering: echo $renderer->renderClientSide(new Position('homepage.top'), [], [ - 'loading' => 'lazy', - 'loading-offset' => 1, + 'loading' => '>=1:lazy', ]); ``` If you prefer a different implementation of lazy loading, it is possible to use own templates instead of the default ones and integrate a different solution in these templates. +#### Fetch priority of image banners + +The [fetchpriority](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/fetchPriority) attribute can be set for image and embed banners using the `fetchpriority` option. + +```php +# server-side rendering: +echo $renderer->render($response->getPosition('homepage.top'), [], [ + 'fetchpriority' => 'high', +]); + +# client-side rendering: +echo $renderer->renderClientSide(new Position('homepage.top'), [], [ + 'fetchpriority' => 'high', +]); +``` + +In the case of a `multiple` position, it may be required that the first banner have a fetch priority of `high` and the others `low`. +This can be achieved with the following expression: + +```php +# server-side rendering: +echo $renderer->render($response->getPosition('homepage.top'), [], [ + 'fetchpriority' => '0:high,low', +]); + +# client-side rendering: +echo $renderer->renderClientSide(new Position('homepage.top'), [], [ + 'fetchpriority' => '0:high,low', +]); +``` + ### Templates overwriting The default templates are written as `.phtml` templates and can be found [here](../src/Renderer/Phtml/Templates). Templates can be also overwritten: diff --git a/src/Bridge/Nette/DI/AmpClientExtension.php b/src/Bridge/Nette/DI/AmpClientExtension.php index 024451e..f7f3501 100644 --- a/src/Bridge/Nette/DI/AmpClientExtension.php +++ b/src/Bridge/Nette/DI/AmpClientExtension.php @@ -23,6 +23,8 @@ use SixtyEightPublishers\AmpClient\Bridge\Nette\DI\Config\RendererConfig; use SixtyEightPublishers\AmpClient\Bridge\Nette\NetteCacheStorage; use SixtyEightPublishers\AmpClient\ClientConfig; +use SixtyEightPublishers\AmpClient\Expression\ExpressionParser; +use SixtyEightPublishers\AmpClient\Expression\ExpressionParserInterface; use SixtyEightPublishers\AmpClient\Http\Cache\CacheStorageInterface; use SixtyEightPublishers\AmpClient\Http\Cache\NoCacheStorage; use SixtyEightPublishers\AmpClient\Http\HttpClientFactory; @@ -153,6 +155,10 @@ public function loadConfiguration(): void 'cacheStorage' => new Reference($this->prefix('cacheStorage')), ]); + $builder->addDefinition($this->prefix('expressionParser')) + ->setType(ExpressionParserInterface::class) + ->setFactory(ExpressionParser::class); + $builder->addDefinition($this->prefix('renderer.rendererBridge')) ->setAutowired(false) ->setType(RendererBridgeInterface::class) diff --git a/src/Expression/ExpressionParser.php b/src/Expression/ExpressionParser.php new file mode 100644 index 0000000..06179ba --- /dev/null +++ b/src/Expression/ExpressionParser.php @@ -0,0 +1,98 @@ +\d+)-(?\d+):|(?\d+):|<(?\d+):|<=(?\d+):|>(?\d+):|>=(?\d+):)?(?[^:\s]+)$/'; + + /** @var array */ + private array $cache = []; + + public function parseExpression(string $expression): ParsedExpression + { + if (isset($this->cache[$expression])) { + return $this->cache[$expression]; + } + + $parts = array_map( + static fn (string $part) => trim($part), + explode(',', $expression), + ); + $rules = []; + + foreach ($parts as $part) { + if (!preg_match(self::ExpressionRegex, $part, $matches)) { + continue; + } + + $value = $matches['VALUE'] ?? null; + + if (null === $value) { + continue; + } + + switch (true) { + case '' !== ($matches['INTERVAL_FROM'] ?? '') && '' !== ($matches['INTERVAL_TO'] ?? ''): + $rules[] = ExpressionRule::range( + (int) $matches['INTERVAL_FROM'], + (int) $matches['INTERVAL_TO'], + $value, + ); + + break; + case '' !== ($matches['EQ'] ?? ''): + $rules[] = ExpressionRule::eq( + (int) $matches['EQ'], + $value, + ); + + break; + case '' !== ($matches['LT'] ?? ''): + $rules[] = ExpressionRule::lt( + (int) $matches['LT'], + $value, + ); + + break; + case '' !== ($matches['LTE'] ?? ''): + $rules[] = ExpressionRule::lte( + (int) $matches['LTE'], + $value, + ); + + break; + case '' !== ($matches['GT'] ?? ''): + $rules[] = ExpressionRule::gt( + (int) $matches['GT'], + $value, + ); + + break; + case '' !== ($matches['GTE'] ?? ''): + $rules[] = ExpressionRule::gte( + (int) $matches['GTE'], + $value, + ); + + break; + default: + $rules[] = ExpressionRule::positive($value); + } + } + + return $this->cache[$expression] = new ParsedExpression($rules); + } + + public function evaluateExpression(string $expression, int $index): ?string + { + return $this->parseExpression($expression)->evaluate($index); + } +} diff --git a/src/Expression/ExpressionParserInterface.php b/src/Expression/ExpressionParserInterface.php new file mode 100644 index 0000000..2815d00 --- /dev/null +++ b/src/Expression/ExpressionParserInterface.php @@ -0,0 +1,12 @@ +value = $value; + $this->matcher = $matcher; + } + + public static function eq(int $eq, string $value): self + { + return new self( + $value, + function (int $index) use ($eq): bool { + return $index === $eq; + }, + ); + } + + public static function range(int $from, int $to, string $value): self + { + return new self( + $value, + function (int $index) use ($from, $to): bool { + return $index >= $from && $index <= $to; + }, + ); + } + + public static function lt(int $lt, string $value): self + { + return new self( + $value, + function (int $index) use ($lt): bool { + return $index < $lt; + }, + ); + } + + public static function lte(int $lte, string $value): self + { + return new self( + $value, + function (int $index) use ($lte): bool { + return $index <= $lte; + }, + ); + } + + public static function gt(int $gt, string $value): self + { + return new self( + $value, + function (int $index) use ($gt): bool { + return $index > $gt; + }, + ); + } + + public static function gte(int $gte, string $value): self + { + return new self( + $value, + function (int $index) use ($gte): bool { + return $index >= $gte; + }, + ); + } + + public static function positive(string $value): self + { + return new self( + $value, + function (): bool { + return true; + }, + ); + } + + public function matches(int $index): bool + { + return ($this->matcher)($index); + } +} diff --git a/src/Expression/ParsedExpression.php b/src/Expression/ParsedExpression.php new file mode 100644 index 0000000..ee0edb0 --- /dev/null +++ b/src/Expression/ParsedExpression.php @@ -0,0 +1,42 @@ + */ + private array $rules; + + /** @var array */ + private array $cache = []; + + /** + * @param list $rules + */ + public function __construct( + array $rules + ) { + $this->rules = $rules; + } + + public function evaluate(int $index): ?string + { + $cacheKey = 'i_' . $index; + + if (array_key_exists($cacheKey, $this->cache)) { + return $this->cache[$cacheKey]; + } + + foreach ($this->rules as $rule) { + if ($rule->matches($index)) { + return $this->cache[$cacheKey] = $rule->value; + } + } + + return $this->cache[$cacheKey] = null; + } +} diff --git a/src/Renderer/Latte/Helpers.php b/src/Renderer/Latte/Helpers.php index a46379b..72bc3ab 100644 --- a/src/Renderer/Latte/Helpers.php +++ b/src/Renderer/Latte/Helpers.php @@ -4,6 +4,7 @@ namespace SixtyEightPublishers\AmpClient\Renderer\Latte; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Request\ValueObject\Position as RequestPosition; final class Helpers @@ -25,15 +26,13 @@ public static function createResourceAttributes(RequestPosition $position): arra } /** - * @param array $options - * * @return array */ - public static function createOptionAttributes(array $options): array + public static function createOptionAttributes(Options $options): array { $attributes = []; - foreach ($options as $name => $value) { + foreach ($options->toArray() as $name => $value) { $attributes['data-amp-option-' . $name] = $value; } diff --git a/src/Renderer/Latte/LatteRendererBridge.php b/src/Renderer/Latte/LatteRendererBridge.php index f5028ef..65cedec 100644 --- a/src/Renderer/Latte/LatteRendererBridge.php +++ b/src/Renderer/Latte/LatteRendererBridge.php @@ -11,6 +11,7 @@ use SixtyEightPublishers\AmpClient\Renderer\Latte\Templates\NotFoundTemplate; use SixtyEightPublishers\AmpClient\Renderer\Latte\Templates\RandomTemplate; use SixtyEightPublishers\AmpClient\Renderer\Latte\Templates\SingleTemplate; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Renderer\RendererBridgeInterface; use SixtyEightPublishers\AmpClient\Renderer\Templates; use SixtyEightPublishers\AmpClient\Request\ValueObject\Position as RequestPosition; @@ -52,7 +53,7 @@ public function overrideTemplates(Templates $templates): self return $renderer; } - public function renderNotFound(ResponsePosition $position, array $elementAttributes = [], array $options = []): string + public function renderNotFound(ResponsePosition $position, array $elementAttributes, Options $options): string { return $this->getLatte()->renderToString( $this->templates->getTemplateFile(Templates::NotFound), @@ -60,7 +61,7 @@ public function renderNotFound(ResponsePosition $position, array $elementAttribu ); } - public function renderSingle(ResponsePosition $position, ?Banner $banner, array $elementAttributes = [], array $options = []): string + public function renderSingle(ResponsePosition $position, ?Banner $banner, array $elementAttributes, Options $options): string { return $this->getLatte()->renderToString( $this->templates->getTemplateFile(Templates::Single), @@ -68,7 +69,7 @@ public function renderSingle(ResponsePosition $position, ?Banner $banner, array ); } - public function renderRandom(ResponsePosition $position, ?Banner $banner, array $elementAttributes = [], array $options = []): string + public function renderRandom(ResponsePosition $position, ?Banner $banner, array $elementAttributes, Options $options): string { return $this->getLatte()->renderToString( $this->templates->getTemplateFile(Templates::Random), @@ -76,7 +77,7 @@ public function renderRandom(ResponsePosition $position, ?Banner $banner, array ); } - public function renderMultiple(ResponsePosition $position, array $banners, array $elementAttributes = [], array $options = []): string + public function renderMultiple(ResponsePosition $position, array $banners, array $elementAttributes, Options $options): string { return $this->getLatte()->renderToString( $this->templates->getTemplateFile(Templates::Multiple), @@ -84,7 +85,7 @@ public function renderMultiple(ResponsePosition $position, array $banners, array ); } - public function renderClientSide(RequestPosition $position, ClientSideMode $mode, array $elementAttributes = [], array $options = []): string + public function renderClientSide(RequestPosition $position, ClientSideMode $mode, array $elementAttributes, Options $options): string { return $this->getLatte()->renderToString( $this->templates->getTemplateFile(Templates::ClientSide), diff --git a/src/Renderer/Latte/Templates/ClientSideTemplate.php b/src/Renderer/Latte/Templates/ClientSideTemplate.php index 1c65041..41871c2 100644 --- a/src/Renderer/Latte/Templates/ClientSideTemplate.php +++ b/src/Renderer/Latte/Templates/ClientSideTemplate.php @@ -5,6 +5,7 @@ namespace SixtyEightPublishers\AmpClient\Renderer\Latte\Templates; use SixtyEightPublishers\AmpClient\Renderer\ClientSideMode; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Request\ValueObject\Position; final class ClientSideTemplate @@ -16,18 +17,16 @@ final class ClientSideTemplate /** @var array */ public array $elementAttributes; - /** @var array */ - public array $options; + public Options $options; /** * @param array $elementAttributes - * @param array $options */ public function __construct( Position $position, ClientSideMode $mode, array $elementAttributes, - array $options + Options $options ) { $this->position = $position; $this->mode = $mode; diff --git a/src/Renderer/Latte/Templates/MultipleTemplate.php b/src/Renderer/Latte/Templates/MultipleTemplate.php index 58184d3..0689fd2 100644 --- a/src/Renderer/Latte/Templates/MultipleTemplate.php +++ b/src/Renderer/Latte/Templates/MultipleTemplate.php @@ -4,6 +4,7 @@ namespace SixtyEightPublishers\AmpClient\Renderer\Latte\Templates; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Response\ValueObject\Banner; use SixtyEightPublishers\AmpClient\Response\ValueObject\Position; @@ -17,19 +18,17 @@ final class MultipleTemplate /** @var array */ public array $elementAttributes; - /** @var array */ - public array $options; + public Options $options; /** * @param array $banners * @param array $elementAttributes - * @param array $options */ public function __construct( Position $position, array $banners, array $elementAttributes, - array $options + Options $options ) { $this->position = $position; $this->banners = $banners; diff --git a/src/Renderer/Latte/Templates/NotFoundTemplate.php b/src/Renderer/Latte/Templates/NotFoundTemplate.php index 55bf4a7..971cfe2 100644 --- a/src/Renderer/Latte/Templates/NotFoundTemplate.php +++ b/src/Renderer/Latte/Templates/NotFoundTemplate.php @@ -4,6 +4,7 @@ namespace SixtyEightPublishers\AmpClient\Renderer\Latte\Templates; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Response\ValueObject\Position; final class NotFoundTemplate @@ -13,17 +14,15 @@ final class NotFoundTemplate /** @var array */ public array $elementAttributes; - /** @var array */ - public array $options; + public Options $options; /** * @param array $elementAttributes - * @param array $options */ public function __construct( Position $position, array $elementAttributes, - array $options + Options $options ) { $this->position = $position; $this->elementAttributes = $elementAttributes; diff --git a/src/Renderer/Latte/Templates/RandomTemplate.php b/src/Renderer/Latte/Templates/RandomTemplate.php index 4e1d5ca..a57f8e8 100644 --- a/src/Renderer/Latte/Templates/RandomTemplate.php +++ b/src/Renderer/Latte/Templates/RandomTemplate.php @@ -4,6 +4,7 @@ namespace SixtyEightPublishers\AmpClient\Renderer\Latte\Templates; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Response\ValueObject\Banner; use SixtyEightPublishers\AmpClient\Response\ValueObject\Position; @@ -16,18 +17,16 @@ final class RandomTemplate /** @var array */ public array $elementAttributes; - /** @var array */ - public array $options; + public Options $options; /** * @param array $elementAttributes - * @param array $options */ public function __construct( Position $position, ?Banner $banner, array $elementAttributes, - array $options + Options $options ) { $this->position = $position; $this->banner = $banner; diff --git a/src/Renderer/Latte/Templates/SingleTemplate.php b/src/Renderer/Latte/Templates/SingleTemplate.php index 3dc91e0..dbfa252 100644 --- a/src/Renderer/Latte/Templates/SingleTemplate.php +++ b/src/Renderer/Latte/Templates/SingleTemplate.php @@ -4,6 +4,7 @@ namespace SixtyEightPublishers\AmpClient\Renderer\Latte\Templates; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Response\ValueObject\Banner; use SixtyEightPublishers\AmpClient\Response\ValueObject\Position; @@ -16,18 +17,16 @@ final class SingleTemplate /** @var array */ public array $elementAttributes; - /** @var array */ - public array $options; + public Options $options; /** * @param array $elementAttributes - * @param array $options */ public function __construct( Position $position, ?Banner $banner, array $elementAttributes, - array $options + Options $options ) { $this->position = $position; $this->banner = $banner; diff --git a/src/Renderer/Latte/Templates/contents.fragment.latte b/src/Renderer/Latte/Templates/contents.fragment.latte index a849788..b079924 100644 --- a/src/Renderer/Latte/Templates/contents.fragment.latte +++ b/src/Renderer/Latte/Templates/contents.fragment.latte @@ -1,6 +1,7 @@ {varType SixtyEightPublishers\AmpClient\Response\ValueObject\Position $position} {varType SixtyEightPublishers\AmpClient\Response\ValueObject\Banner|null $banner} -{varType array $options} +{varType SixtyEightPublishers\AmpClient\Renderer\Options $options} +{varType int $index} {foreach $banner->getContents() as $content} {if $content instanceof SixtyEightPublishers\AmpClient\Response\ValueObject\ImageContent} @@ -24,7 +25,8 @@ width => null !== $position->getDimensions()->getWidth() ? $position->getDimensions()->getWidth() : null, height => null !== $position->getDimensions()->getHeight() ? $position->getDimensions()->getHeight() : null, title => '' !== $content->getTitle() ? $content->getTitle() : null, - loading => $options['loading'] ?? null + loading => $options->evaluate('loading', $index), + fetchpriority => $options->evaluate('fetchpriority', $index), "> diff --git a/src/Renderer/Latte/Templates/multiple.latte b/src/Renderer/Latte/Templates/multiple.latte index 0fe9961..9bc51c9 100644 --- a/src/Renderer/Latte/Templates/multiple.latte +++ b/src/Renderer/Latte/Templates/multiple.latte @@ -13,13 +13,7 @@ data-amp-banner-fingerprint="{SixtyEightPublishers\AmpClient\Renderer\Fingerprint::create($position, $banner)}" data-amp-banner-id="{$banner->getId()}"> - {var $optionsCopy = $options} - - {if isset($optionsCopy['loading']) && $index < ((int) ($optionsCopy['loading-offset'] ?? 0))} - {do $optionsCopy['loading'] = null} - {/if} - - {include 'contents.fragment.latte', position: $position, banner: $banner, options: $optionsCopy} + {include 'contents.fragment.latte', position: $position, banner: $banner, options: $options, index: $index} diff --git a/src/Renderer/Latte/Templates/random.latte b/src/Renderer/Latte/Templates/random.latte index 514dd9d..4b07c11 100644 --- a/src/Renderer/Latte/Templates/random.latte +++ b/src/Renderer/Latte/Templates/random.latte @@ -9,6 +9,6 @@ class="amp-banner amp-banner--random" data-amp-banner-fingerprint="{SixtyEightPublishers\AmpClient\Renderer\Fingerprint::create($position, $banner)}" data-amp-banner-id="{$banner->getId()}"> - {include 'contents.fragment.latte', position: $position, banner: $banner, options: $options} + {include 'contents.fragment.latte', position: $position, banner: $banner, options: $options, index: 0} diff --git a/src/Renderer/Latte/Templates/single.latte b/src/Renderer/Latte/Templates/single.latte index 7591821..7912402 100644 --- a/src/Renderer/Latte/Templates/single.latte +++ b/src/Renderer/Latte/Templates/single.latte @@ -9,6 +9,6 @@ class="amp-banner amp-banner--single" data-amp-banner-fingerprint="{SixtyEightPublishers\AmpClient\Renderer\Fingerprint::create($position, $banner)}" data-amp-banner-id="{$banner->getId()}"> - {include 'contents.fragment.latte', position: $position, banner: $banner, options: $options} + {include 'contents.fragment.latte', position: $position, banner: $banner, options: $options, index: 0} diff --git a/src/Renderer/Options.php b/src/Renderer/Options.php new file mode 100644 index 0000000..93234e1 --- /dev/null +++ b/src/Renderer/Options.php @@ -0,0 +1,69 @@ + */ + private array $options; + + private ExpressionParserInterface $expressionParser; + + /** + * @param array $options + */ + public function __construct( + array $options, + ExpressionParserInterface $expressionParser + ) { + $this->options = $options; + $this->expressionParser = $expressionParser; + } + + public function has(string $name): bool + { + return array_key_exists($name, $this->options); + } + + public function get(string $name, $defaultValue = null) + { + return array_key_exists($name, $this->options) ? $this->options[$name] : $defaultValue; + } + + public function evaluate(string $name, int $index): ?string + { + if (!array_key_exists($name, $this->options)) { + return null; + } + + return $this->expressionParser->evaluateExpression( + (string) $this->options[$name], + $index, + ); + } + + /** + * @param array $options + */ + public function override(array $options): void + { + $this->options = array_merge( + $this->options, + $options, + ); + } + + /** + * @return array + */ + public function toArray(): array + { + return $this->options; + } +} diff --git a/src/Renderer/Phtml/PhtmlRendererBridge.php b/src/Renderer/Phtml/PhtmlRendererBridge.php index b27d540..4936d03 100644 --- a/src/Renderer/Phtml/PhtmlRendererBridge.php +++ b/src/Renderer/Phtml/PhtmlRendererBridge.php @@ -5,6 +5,7 @@ namespace SixtyEightPublishers\AmpClient\Renderer\Phtml; use SixtyEightPublishers\AmpClient\Renderer\ClientSideMode; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Renderer\OutputBuffer; use SixtyEightPublishers\AmpClient\Renderer\RendererBridgeInterface; use SixtyEightPublishers\AmpClient\Renderer\Templates; @@ -39,7 +40,7 @@ public function overrideTemplates(Templates $templates): self /** * @throws Throwable */ - public function renderNotFound(ResponsePosition $position, array $elementAttributes = [], array $options = []): string + public function renderNotFound(ResponsePosition $position, array $elementAttributes, Options $options): string { $filename = $this->templates->getTemplateFile(Templates::NotFound); @@ -51,7 +52,7 @@ public function renderNotFound(ResponsePosition $position, array $elementAttribu /** * @throws Throwable */ - public function renderSingle(ResponsePosition $position, ?Banner $banner, array $elementAttributes = [], array $options = []): string + public function renderSingle(ResponsePosition $position, ?Banner $banner, array $elementAttributes, Options $options): string { $filename = $this->templates->getTemplateFile(Templates::Single); @@ -63,7 +64,7 @@ public function renderSingle(ResponsePosition $position, ?Banner $banner, array /** * @throws Throwable */ - public function renderRandom(ResponsePosition $position, ?Banner $banner, array $elementAttributes = [], array $options = []): string + public function renderRandom(ResponsePosition $position, ?Banner $banner, array $elementAttributes, Options $options): string { $filename = $this->templates->getTemplateFile(Templates::Random); @@ -75,7 +76,7 @@ public function renderRandom(ResponsePosition $position, ?Banner $banner, array /** * @throws Throwable */ - public function renderMultiple(ResponsePosition $position, array $banners, array $elementAttributes = [], array $options = []): string + public function renderMultiple(ResponsePosition $position, array $banners, array $elementAttributes, Options $options): string { $filename = $this->templates->getTemplateFile(Templates::Multiple); @@ -87,7 +88,7 @@ public function renderMultiple(ResponsePosition $position, array $banners, array /** * @throws Throwable */ - public function renderClientSide(RequestPosition $position, ClientSideMode $mode, array $elementAttributes = [], array $options = []): string + public function renderClientSide(RequestPosition $position, ClientSideMode $mode, array $elementAttributes, Options $options): string { $filename = $this->templates->getTemplateFile(Templates::ClientSide); diff --git a/src/Renderer/Phtml/Templates/clientSide.phtml b/src/Renderer/Phtml/Templates/clientSide.phtml index a994749..688c44d 100644 --- a/src/Renderer/Phtml/Templates/clientSide.phtml +++ b/src/Renderer/Phtml/Templates/clientSide.phtml @@ -4,20 +4,21 @@ declare(strict_types=1); namespace SixtyEightPublishers\AmpClient\Renderer\Phtml; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Renderer\ClientSideMode; use SixtyEightPublishers\AmpClient\Request\ValueObject\Position; use SixtyEightPublishers\AmpClient\Request\ValueObject\BannerResource; /** @var Position $position */ /** @var array $elementAttributes */ -/** @var array $options */ +/** @var Options $options */ /** @var ClientSideMode $mode */ ?>
$mode->isEmbed() ? $mode->getValue() : null], array_map(static fn (BannerResource $resource): string => implode(',', $resource->getValues()), Helpers::prefixKeys($position->getResources(), 'data-amp-resource-')), - Helpers::prefixKeys($options, 'data-amp-option-'), + Helpers::prefixKeys($options->toArray(), 'data-amp-option-'), $elementAttributes )) ?>>
diff --git a/src/Renderer/Phtml/Templates/contents.fragment.phtml b/src/Renderer/Phtml/Templates/contents.fragment.phtml index ff87cd6..b4328b2 100644 --- a/src/Renderer/Phtml/Templates/contents.fragment.phtml +++ b/src/Renderer/Phtml/Templates/contents.fragment.phtml @@ -4,6 +4,7 @@ declare(strict_types=1); namespace SixtyEightPublishers\AmpClient\Renderer\Phtml; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Response\ValueObject\Position; use SixtyEightPublishers\AmpClient\Response\ValueObject\Banner; use SixtyEightPublishers\AmpClient\Response\ValueObject\HtmlContent; @@ -12,7 +13,7 @@ use SixtyEightPublishers\AmpClient\Renderer\BreakpointStyle\BreakpointStyle; /** @var Position $position */ /** @var ?Banner $banner */ -/** @var array $options */ +/** @var Options $options */ ?> getContents() as $content) : ?> @@ -33,7 +34,8 @@ use SixtyEightPublishers\AmpClient\Renderer\BreakpointStyle\BreakpointStyle; getDimensions()->getWidth()) : ?>width="getDimensions()->getWidth()) ?>" getDimensions()->getHeight()) : ?>height="getDimensions()->getHeight()) ?>" getTitle()) : ?>title="getTitle()) ?>" - loading=""> + evaluate('loading', $index ?? 0))) : ?>loading="" + evaluate('fetchpriority', $index ?? 0))) : ?>fetchpriority=""> diff --git a/src/Renderer/Phtml/Templates/multiple.phtml b/src/Renderer/Phtml/Templates/multiple.phtml index 09d1bb5..cc0506f 100644 --- a/src/Renderer/Phtml/Templates/multiple.phtml +++ b/src/Renderer/Phtml/Templates/multiple.phtml @@ -4,6 +4,7 @@ declare(strict_types=1); namespace SixtyEightPublishers\AmpClient\Renderer\Phtml; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Response\ValueObject\Position; use SixtyEightPublishers\AmpClient\Response\ValueObject\Banner; use SixtyEightPublishers\AmpClient\Renderer\Fingerprint; @@ -12,22 +13,19 @@ use SixtyEightPublishers\AmpClient\Renderer\AmpBannerExternalAttribute; /** @var Position $position */ /** @var array $banners */ /** @var array $elementAttributes */ -/** @var array $options */ +/** @var Options $options */ ?>
> + toArray(), 'data-amp-option-'), $elementAttributes)) ?>> 0 < count($banner->getContents())) ?>
- - $banner) : ?>
-
diff --git a/src/Renderer/Phtml/Templates/notFound.phtml b/src/Renderer/Phtml/Templates/notFound.phtml index 56d8cb3..80b862d 100644 --- a/src/Renderer/Phtml/Templates/notFound.phtml +++ b/src/Renderer/Phtml/Templates/notFound.phtml @@ -4,14 +4,15 @@ declare(strict_types=1); namespace SixtyEightPublishers\AmpClient\Renderer\Phtml; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Response\ValueObject\Position; use SixtyEightPublishers\AmpClient\Renderer\AmpBannerExternalAttribute; /** @var Position $position */ /** @var array $elementAttributes */ -/** @var array $options */ +/** @var Options $options */ ?>
> + toArray(), 'data-amp-option-'), $elementAttributes)) ?>>
diff --git a/src/Renderer/Phtml/Templates/random.phtml b/src/Renderer/Phtml/Templates/random.phtml index 559f792..3d5a12a 100644 --- a/src/Renderer/Phtml/Templates/random.phtml +++ b/src/Renderer/Phtml/Templates/random.phtml @@ -4,6 +4,7 @@ declare(strict_types=1); namespace SixtyEightPublishers\AmpClient\Renderer\Phtml; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Response\ValueObject\Position; use SixtyEightPublishers\AmpClient\Response\ValueObject\Banner; use SixtyEightPublishers\AmpClient\Renderer\Fingerprint; @@ -12,11 +13,11 @@ use SixtyEightPublishers\AmpClient\Renderer\AmpBannerExternalAttribute; /** @var Position $position */ /** @var ?Banner $banner */ /** @var array $elementAttributes */ -/** @var array $options */ +/** @var Options $options */ ?>
> + toArray(), 'data-amp-option-'), $elementAttributes)) ?>> getContents())): ?>
$elementAttributes */ -/** @var array $options */ +/** @var Options $options */ ?>
> + toArray(), 'data-amp-option-'), $elementAttributes)) ?>> getContents())): ?>
bannersResolver = $bannersResolver; $this->rendererBridge = $rendererBridge; + $this->expressionParser = $expressionParser; } public static function create(?RendererBridgeInterface $rendererBridge = null): self @@ -30,12 +36,16 @@ public static function create(?RendererBridgeInterface $rendererBridge = null): return new self( new BannersResolver(), $rendererBridge ?? new PhtmlRendererBridge(), + new ExpressionParser(), ); } public function render(ResponsePosition $position, array $elementAttributes = [], array $options = []): string { try { + $options = new Options($options, $this->expressionParser); + $options->override($position->getOptions()); + switch ($position->getDisplayType()) { case null: return $this->rendererBridge->renderNotFound( @@ -92,7 +102,7 @@ public function renderClientSide(RequestPosition $position, array $elementAttrib $position, $mode, $elementAttributes, - $options, + new Options($options, $this->expressionParser), ); } catch (Throwable $e) { if ($e instanceof RendererException) { diff --git a/src/Renderer/RendererBridgeInterface.php b/src/Renderer/RendererBridgeInterface.php index 8febf94..47f547f 100644 --- a/src/Renderer/RendererBridgeInterface.php +++ b/src/Renderer/RendererBridgeInterface.php @@ -14,32 +14,27 @@ public function overrideTemplates(Templates $templates): self; /** * @param array $elementAttributes - * @param array $options */ - public function renderNotFound(ResponsePosition $position, array $elementAttributes = [], array $options = []): string; + public function renderNotFound(ResponsePosition $position, array $elementAttributes, Options $options): string; /** * @param array $elementAttributes - * @param array $options */ - public function renderSingle(ResponsePosition $position, ?Banner $banner, array $elementAttributes = [], array $options = []): string; + public function renderSingle(ResponsePosition $position, ?Banner $banner, array $elementAttributes, Options $options): string; /** * @param array $elementAttributes - * @param array $options */ - public function renderRandom(ResponsePosition $position, ?Banner $banner, array $elementAttributes = [], array $options = []): string; + public function renderRandom(ResponsePosition $position, ?Banner $banner, array $elementAttributes, Options $options): string; /** * @param array $banners * @param array $elementAttributes - * @param array $options */ - public function renderMultiple(ResponsePosition $position, array $banners, array $elementAttributes = [], array $options = []): string; + public function renderMultiple(ResponsePosition $position, array $banners, array $elementAttributes, Options $options): string; /** * @param array $elementAttributes - * @param array $options */ - public function renderClientSide(RequestPosition $position, ClientSideMode $mode, array $elementAttributes = [], array $options = []): string; + public function renderClientSide(RequestPosition $position, ClientSideMode $mode, array $elementAttributes, Options $options): string; } diff --git a/src/Response/Hydrator/BannersResponseHydratorHandler.php b/src/Response/Hydrator/BannersResponseHydratorHandler.php index ae3cda1..7c781ff 100644 --- a/src/Response/Hydrator/BannersResponseHydratorHandler.php +++ b/src/Response/Hydrator/BannersResponseHydratorHandler.php @@ -92,6 +92,7 @@ public function hydrate($responseBody): BannersResponse $positionData['display_type'] ?? null, $positionData['breakpoint_type'], $positionData['mode'] ?? Position::ModeManaged, + $positionData['options'] ?? [], $this->hydrateDimensions($positionData['dimensions'] ?? null), $this->hydrateBanners($positionData['banners']), ); diff --git a/src/Response/ValueObject/Position.php b/src/Response/ValueObject/Position.php index 5fd7dd7..f001687 100644 --- a/src/Response/ValueObject/Position.php +++ b/src/Response/ValueObject/Position.php @@ -30,6 +30,9 @@ final class Position private string $mode; + /** @var array */ + private array $options; + private Dimensions $dimensions; /** @var array */ @@ -46,6 +49,7 @@ public function __construct( ?string $displayType, string $breakpointType, string $mode, + array $options, Dimensions $dimensions, array $banners ) { @@ -56,6 +60,7 @@ public function __construct( $this->displayType = $displayType; $this->breakpointType = $breakpointType; $this->mode = $mode; + $this->options = $options; $this->dimensions = $dimensions; $this->banners = $banners; } @@ -95,6 +100,14 @@ public function getMode(): string return $this->mode; } + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + public function getDimensions(): Dimensions { return $this->dimensions; diff --git a/tests/Bridge/Latte/RendererProviderTest.php b/tests/Bridge/Latte/RendererProviderTest.php index 91ec645..39acd80 100644 --- a/tests/Bridge/Latte/RendererProviderTest.php +++ b/tests/Bridge/Latte/RendererProviderTest.php @@ -46,6 +46,7 @@ public function testInvokingDefaultInstanceWithoutResources(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -91,6 +92,7 @@ public function testInvokingDefaultInstanceWithSameModeAsDefault(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -136,6 +138,7 @@ public function testInvokingDefaultInstanceWithAttributes(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -181,6 +184,7 @@ public function testInvokingDefaultInstanceWithOptions(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -226,6 +230,7 @@ public function testInvokingDefaultInstanceWithResources(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -287,6 +292,7 @@ public function testInvokingDefaultInstanceWithPositionInEmbedMode(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, $positionMode, + [], new Dimensions(null, null), [], ); @@ -333,6 +339,7 @@ public function testClientConfigurationEventsShouldBeInvokedBeforeFirstFetch(): ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -453,6 +460,7 @@ public function testExceptionShouldBeThrownWhenRendererThrowsExceptionInDebugMod ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -492,6 +500,7 @@ public function testEmptyStringShouldBeReturnedWhenRendererThrowsExceptionInNonD ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -529,6 +538,7 @@ public function testExceptionShouldBeLoggedWhenRendererThrowsExceptionInNonDebug ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -616,6 +626,7 @@ public function testPositionsShouldBeQueuedAndReplacedInStringOutput(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -627,6 +638,7 @@ public function testPositionsShouldBeQueuedAndReplacedInStringOutput(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -728,6 +740,7 @@ public function testPositionsShouldBeQueuedAndReplacedInArrayOutput(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -739,6 +752,7 @@ public function testPositionsShouldBeQueuedAndReplacedInArrayOutput(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -830,6 +844,7 @@ public function testEmbedPositionReturnedFromApiShouldBeQueuedAndReplacedInStrin ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeEmbed, + [], new Dimensions(null, null), [], ); diff --git a/tests/Expression/ExpressionParserTest.php b/tests/Expression/ExpressionParserTest.php new file mode 100644 index 0000000..243f660 --- /dev/null +++ b/tests/Expression/ExpressionParserTest.php @@ -0,0 +1,98 @@ +parser->evaluateExpression($expression, 0)); + Assert::same('foo', $this->parser->evaluateExpression($expression, 1)); + } + + public function testEvaluateEq(): void + { + $expression = '1:foo'; + + Assert::null($this->parser->evaluateExpression($expression, 0)); + Assert::same('foo', $this->parser->evaluateExpression($expression, 1)); + Assert::null($this->parser->evaluateExpression($expression, 2)); + } + + public function testEvaluateRange(): void + { + $expression = '1-2:foo'; + + Assert::null($this->parser->evaluateExpression($expression, 0)); + Assert::same('foo', $this->parser->evaluateExpression($expression, 1)); + Assert::same('foo', $this->parser->evaluateExpression($expression, 2)); + Assert::null($this->parser->evaluateExpression($expression, 3)); + } + + public function testEvaluateLt(): void + { + $expression = '>1:foo'; + + Assert::null($this->parser->evaluateExpression($expression, 0)); + Assert::null($this->parser->evaluateExpression($expression, 1)); + Assert::same('foo', $this->parser->evaluateExpression($expression, 2)); + } + + public function testEvaluateLte(): void + { + $expression = '>=1:foo'; + + Assert::null($this->parser->evaluateExpression($expression, 0)); + Assert::same('foo', $this->parser->evaluateExpression($expression, 1)); + Assert::same('foo', $this->parser->evaluateExpression($expression, 2)); + } + + public function testEvaluateGt(): void + { + $expression = '<1:foo'; + + Assert::same('foo', $this->parser->evaluateExpression($expression, 0)); + Assert::null($this->parser->evaluateExpression($expression, 1)); + } + + public function testEvaluateGte(): void + { + $expression = '<=1:foo'; + + Assert::same('foo', $this->parser->evaluateExpression($expression, 0)); + Assert::same('foo', $this->parser->evaluateExpression($expression, 1)); + Assert::null($this->parser->evaluateExpression($expression, 2)); + } + + public function evaluateMultipleRules(): void + { + $expression = '0:large,1-3:medium,<5:normal,thin'; + + Assert::same('large', $this->parser->evaluateExpression($expression, 0)); + Assert::same('medium', $this->parser->evaluateExpression($expression, 1)); + Assert::same('medium', $this->parser->evaluateExpression($expression, 2)); + Assert::same('medium', $this->parser->evaluateExpression($expression, 3)); + Assert::same('normal', $this->parser->evaluateExpression($expression, 4)); + Assert::same('thin', $this->parser->evaluateExpression($expression, 5)); + Assert::same('thin', $this->parser->evaluateExpression($expression, 6)); + } + + protected function setUp(): void + { + $this->parser = new ExpressionParser(); + } +} + +(new ExpressionParserTest())->run(); diff --git a/tests/Renderer/BannersResolverTest.php b/tests/Renderer/BannersResolverTest.php index 3f26f7d..7c9c0ca 100644 --- a/tests/Renderer/BannersResolverTest.php +++ b/tests/Renderer/BannersResolverTest.php @@ -25,6 +25,7 @@ public function testNullShouldBeReturnedWhenResolvingSinglePositionWithoutBanner Position::DisplayTypeSingle, Position::BreakpointTypeMin, Position::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -47,6 +48,7 @@ public function testFirstBannerWithHighestScoreShouldBeReturnedWhenResolvingSing Position::DisplayTypeSingle, Position::BreakpointTypeMin, Position::ModeManaged, + [], new Dimensions(null, null), [$banner1, $banner2, $banner3, $banner4], ); @@ -65,6 +67,7 @@ public function testEmptyArrayShouldBeReturnedWhenResolvingMultiplePositionWitho Position::DisplayTypeMultiple, Position::BreakpointTypeMin, Position::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -87,6 +90,7 @@ public function testSortedBannersShouldBeReturnedWhenResolvingMultiplePosition() Position::DisplayTypeMultiple, Position::BreakpointTypeMin, Position::ModeManaged, + [], new Dimensions(null, null), [$banner1, $banner2, $banner3, $banner4], ); @@ -110,6 +114,7 @@ public function testNullShouldBeReturnedWhenResolvingRandomPositionWithoutBanner Position::DisplayTypeRandom, Position::BreakpointTypeMin, Position::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -132,6 +137,7 @@ public function testRandomBannerShouldBeReturnedWhenResolvingRandomPosition(): v Position::DisplayTypeRandom, Position::BreakpointTypeMin, Position::ModeManaged, + [], new Dimensions(null, null), [$banner1, $banner2, $banner3, $banner4], ); diff --git a/tests/Renderer/BreakpointStyle/BreakpointStyleTest.php b/tests/Renderer/BreakpointStyle/BreakpointStyleTest.php index e430c0a..cef2631 100644 --- a/tests/Renderer/BreakpointStyle/BreakpointStyleTest.php +++ b/tests/Renderer/BreakpointStyle/BreakpointStyleTest.php @@ -133,6 +133,7 @@ private function createBreakpointStyle(string $breakpointType, array $contents): Position::DisplayTypeSingle, $breakpointType, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [$banner], ); diff --git a/tests/Renderer/Latte/LatteRendererBridgeTest.php b/tests/Renderer/Latte/LatteRendererBridgeTest.php index 5fdc4be..a005ee4 100644 --- a/tests/Renderer/Latte/LatteRendererBridgeTest.php +++ b/tests/Renderer/Latte/LatteRendererBridgeTest.php @@ -6,8 +6,10 @@ use Closure; use Latte\Engine; +use SixtyEightPublishers\AmpClient\Expression\ExpressionParser; use SixtyEightPublishers\AmpClient\Renderer\ClientSideMode; use SixtyEightPublishers\AmpClient\Renderer\Latte\LatteRendererBridge; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Renderer\Templates; use SixtyEightPublishers\AmpClient\Request\ValueObject\Position as RequestPosition; use SixtyEightPublishers\AmpClient\Response\ValueObject\Banner; @@ -46,7 +48,7 @@ public function testNotFoundTemplateRendering( ): void { $renderer = $this->createRendererBridge(); - AssertHtml::assert($expectationFile, $renderer->renderNotFound($position, $elementAttributes, $options)); + AssertHtml::assert($expectationFile, $renderer->renderNotFound($position, $elementAttributes, $this->createOptions($options))); } /** @@ -61,7 +63,7 @@ public function testSingleTemplateRendering( ): void { $renderer = $this->createRendererBridge(); - AssertHtml::assert($expectationFile, $renderer->renderSingle($position, $banner, $elementAttributes, $options)); + AssertHtml::assert($expectationFile, $renderer->renderSingle($position, $banner, $elementAttributes, $this->createOptions($options))); } /** @@ -76,7 +78,7 @@ public function testRandomTemplateRendering( ): void { $renderer = $this->createRendererBridge(); - AssertHtml::assert($expectationFile, $renderer->renderRandom($position, $banner, $elementAttributes, $options)); + AssertHtml::assert($expectationFile, $renderer->renderRandom($position, $banner, $elementAttributes, $this->createOptions($options))); } /** @@ -91,7 +93,7 @@ public function testMultipleTemplateRendering( ): void { $renderer = $this->createRendererBridge(); - AssertHtml::assert($expectationFile, $renderer->renderMultiple($position, $banners, $elementAttributes, $options)); + AssertHtml::assert($expectationFile, $renderer->renderMultiple($position, $banners, $elementAttributes, $this->createOptions($options))); } /** @@ -106,7 +108,7 @@ public function testClientSideTemplateRendering( ): void { $renderer = $this->createRendererBridge(); - AssertHtml::assert($expectationFile, $renderer->renderClientSide($position, $mode, $elementAttributes, $options)); + AssertHtml::assert($expectationFile, $renderer->renderClientSide($position, $mode, $elementAttributes, $this->createOptions($options))); } public function notFoundTemplateDataProvider(): array @@ -138,6 +140,11 @@ private function createRendererBridge(): LatteRendererBridge { return LatteRendererBridge::fromEngine(new Engine()); } + + private function createOptions(array $options): Options + { + return new Options($options, new ExpressionParser()); + } } (new LatteRendererBridgeTest())->run(); diff --git a/tests/Renderer/Phtml/PhtmlRendererBridgeTest.php b/tests/Renderer/Phtml/PhtmlRendererBridgeTest.php index 73100f1..a9534ae 100644 --- a/tests/Renderer/Phtml/PhtmlRendererBridgeTest.php +++ b/tests/Renderer/Phtml/PhtmlRendererBridgeTest.php @@ -5,7 +5,9 @@ namespace SixtyEightPublishers\AmpClient\Tests\Renderer\Phtml; use Closure; +use SixtyEightPublishers\AmpClient\Expression\ExpressionParser; use SixtyEightPublishers\AmpClient\Renderer\ClientSideMode; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Renderer\Phtml\PhtmlRendererBridge; use SixtyEightPublishers\AmpClient\Renderer\Templates; use SixtyEightPublishers\AmpClient\Request\ValueObject\Position as RequestPosition; @@ -45,7 +47,7 @@ public function testNotFoundTemplateRendering( ): void { $renderer = new PhtmlRendererBridge(); - AssertHtml::assert($expectationFile, $renderer->renderNotFound($position, $elementAttributes, $options)); + AssertHtml::assert($expectationFile, $renderer->renderNotFound($position, $elementAttributes, $this->createOptions($options))); } /** @@ -60,7 +62,7 @@ public function testSingleTemplateRendering( ): void { $renderer = new PhtmlRendererBridge(); - AssertHtml::assert($expectationFile, $renderer->renderSingle($position, $banner, $elementAttributes, $options)); + AssertHtml::assert($expectationFile, $renderer->renderSingle($position, $banner, $elementAttributes, $this->createOptions($options))); } /** @@ -75,7 +77,7 @@ public function testRandomTemplateRendering( ): void { $renderer = new PhtmlRendererBridge(); - AssertHtml::assert($expectationFile, $renderer->renderRandom($position, $banner, $elementAttributes, $options)); + AssertHtml::assert($expectationFile, $renderer->renderRandom($position, $banner, $elementAttributes, $this->createOptions($options))); } /** @@ -90,7 +92,7 @@ public function testMultipleTemplateRendering( ): void { $renderer = new PhtmlRendererBridge(); - AssertHtml::assert($expectationFile, $renderer->renderMultiple($position, $banners, $elementAttributes, $options)); + AssertHtml::assert($expectationFile, $renderer->renderMultiple($position, $banners, $elementAttributes, $this->createOptions($options))); } /** @@ -105,7 +107,7 @@ public function testClientSideTemplateRendering( ): void { $renderer = new PhtmlRendererBridge(); - AssertHtml::assert($expectationFile, $renderer->renderClientSide($position, $mode, $elementAttributes, $options)); + AssertHtml::assert($expectationFile, $renderer->renderClientSide($position, $mode, $elementAttributes, $this->createOptions($options))); } public function notFoundTemplateDataProvider(): array @@ -132,6 +134,11 @@ public function clientSideTemplateDataProvider(): array { return require __DIR__ . '/../../resources/renderer/client-side/data-provider.php'; } + + private function createOptions(array $options): Options + { + return new Options($options, new ExpressionParser()); + } } (new PhtmlRendererBridgeTest())->run(); diff --git a/tests/Renderer/RendererTest.php b/tests/Renderer/RendererTest.php index 33a092d..8e7bdb8 100644 --- a/tests/Renderer/RendererTest.php +++ b/tests/Renderer/RendererTest.php @@ -9,9 +9,12 @@ use Hamcrest\Matchers; use Mockery; use SixtyEightPublishers\AmpClient\Exception\RendererException; +use SixtyEightPublishers\AmpClient\Expression\ExpressionParser; +use SixtyEightPublishers\AmpClient\Expression\ExpressionParserInterface; use SixtyEightPublishers\AmpClient\Renderer\BannersResolver; use SixtyEightPublishers\AmpClient\Renderer\BannersResolverInterface; use SixtyEightPublishers\AmpClient\Renderer\ClientSideMode; +use SixtyEightPublishers\AmpClient\Renderer\Options; use SixtyEightPublishers\AmpClient\Renderer\Phtml\PhtmlRendererBridge; use SixtyEightPublishers\AmpClient\Renderer\Renderer; use SixtyEightPublishers\AmpClient\Renderer\RendererBridgeInterface; @@ -32,22 +35,25 @@ public function testDefaultRendererShouldBeCreated(): void { $renderer = Renderer::create(); - [$bannersResolver, $rendererBridge] = call_user_func(Closure::bind(static function () use ($renderer): array { + [$bannersResolver, $rendererBridge, $expressionParser] = call_user_func(Closure::bind(static function () use ($renderer): array { return [ $renderer->bannersResolver, $renderer->rendererBridge, + $renderer->expressionParser, ]; }, null, Renderer::class)); Assert::equal(new BannersResolver(), $bannersResolver); Assert::equal(new PhtmlRendererBridge(), $rendererBridge); + Assert::equal(new ExpressionParser(), $expressionParser); } public function testNotFoundTemplateShouldBeRendered(): void { $bannersResolver = Mockery::mock(BannersResolverInterface::class); $rendererBridge = Mockery::mock(RendererBridgeInterface::class); - $renderer = new Renderer($bannersResolver, $rendererBridge); + $expressionParser = Mockery::mock(ExpressionParserInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge, $expressionParser); $position = new ResponsePosition( null, @@ -57,6 +63,7 @@ public function testNotFoundTemplateShouldBeRendered(): void null, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -64,7 +71,9 @@ public function testNotFoundTemplateShouldBeRendered(): void $rendererBridge ->shouldReceive('renderNotFound') ->once() - ->with($position, [], []) + ->with($position, [], Mockery::on(function ($options): bool { + return $options instanceof Options && [] === $options->toArray(); + })) ->andReturn('not found'); Assert::same('not found', $renderer->render($position)); @@ -74,7 +83,8 @@ public function testSingleTemplateShouldBeRendered(): void { $bannersResolver = Mockery::mock(BannersResolverInterface::class); $rendererBridge = Mockery::mock(RendererBridgeInterface::class); - $renderer = new Renderer($bannersResolver, $rendererBridge); + $expressionParser = Mockery::mock(ExpressionParserInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge, $expressionParser); $banner = new Banner('1234', 'Main', 0, null, null, null, []); $position = new ResponsePosition( @@ -85,6 +95,7 @@ public function testSingleTemplateShouldBeRendered(): void ResponsePosition::DisplayTypeSingle, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [$banner], ); @@ -98,7 +109,9 @@ public function testSingleTemplateShouldBeRendered(): void $rendererBridge ->shouldReceive('renderSingle') ->once() - ->with($position, $banner, [], []) + ->with($position, $banner, [], Mockery::on(function ($options): bool { + return $options instanceof Options && [] === $options->toArray(); + })) ->andReturn('single'); Assert::same('single', $renderer->render($position)); @@ -108,7 +121,8 @@ public function testRandomTemplateShouldBeRendered(): void { $bannersResolver = Mockery::mock(BannersResolverInterface::class); $rendererBridge = Mockery::mock(RendererBridgeInterface::class); - $renderer = new Renderer($bannersResolver, $rendererBridge); + $expressionParser = Mockery::mock(ExpressionParserInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge, $expressionParser); $banner = new Banner('1234', 'Main', 0, null, null, null, []); $position = new ResponsePosition( @@ -119,6 +133,7 @@ public function testRandomTemplateShouldBeRendered(): void ResponsePosition::DisplayTypeRandom, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [$banner], ); @@ -132,7 +147,9 @@ public function testRandomTemplateShouldBeRendered(): void $rendererBridge ->shouldReceive('renderRandom') ->once() - ->with($position, $banner, [], []) + ->with($position, $banner, [], Mockery::on(function ($options): bool { + return $options instanceof Options && [] === $options->toArray(); + })) ->andReturn('random'); Assert::same('random', $renderer->render($position)); @@ -142,7 +159,8 @@ public function testMultipleTemplateShouldBeRendered(): void { $bannersResolver = Mockery::mock(BannersResolverInterface::class); $rendererBridge = Mockery::mock(RendererBridgeInterface::class); - $renderer = new Renderer($bannersResolver, $rendererBridge); + $expressionParser = Mockery::mock(ExpressionParserInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge, $expressionParser); $banners = [ new Banner('1234', 'Main', 0, null, null, null, []), @@ -156,6 +174,9 @@ public function testMultipleTemplateShouldBeRendered(): void ResponsePosition::DisplayTypeMultiple, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [ + 'fetchpriority' => '0:high,low', + ], new Dimensions(null, null), $banners, ); @@ -169,17 +190,20 @@ public function testMultipleTemplateShouldBeRendered(): void $rendererBridge ->shouldReceive('renderMultiple') ->once() - ->with($position, $banners, [], []) + ->with($position, $banners, [], Mockery::on(function ($options): bool { + return $options instanceof Options && ['fetchpriority' => '0:high,low', 'loading' => 'lazy'] === $options->toArray(); + })) ->andReturn('multiple'); - Assert::same('multiple', $renderer->render($position)); + Assert::same('multiple', $renderer->render($position, [], ['fetchpriority' => 'high', 'loading' => 'lazy'])); } public function testClientSideTemplateShouldBeRendered(): void { $bannersResolver = Mockery::mock(BannersResolverInterface::class); $rendererBridge = Mockery::mock(RendererBridgeInterface::class); - $renderer = new Renderer($bannersResolver, $rendererBridge); + $expressionParser = Mockery::mock(ExpressionParserInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge, $expressionParser); $position = new RequestPosition('homepage.top', [ new BannerResource('role', 'vip'), @@ -188,7 +212,9 @@ public function testClientSideTemplateShouldBeRendered(): void $rendererBridge ->shouldReceive('renderClientSide') ->once() - ->with($position, Matchers::equalTo(ClientSideMode::managed()), [], []) + ->with($position, Matchers::equalTo(ClientSideMode::managed()), [], Mockery::on(function ($options): bool { + return $options instanceof Options && [] === $options->toArray(); + })) ->andReturn('client-side'); Assert::same('client-side', $renderer->renderClientSide($position)); @@ -198,7 +224,8 @@ public function testClientSideTemplateWithEmbedModeShouldBeRendered(): void { $bannersResolver = Mockery::mock(BannersResolverInterface::class); $rendererBridge = Mockery::mock(RendererBridgeInterface::class); - $renderer = new Renderer($bannersResolver, $rendererBridge); + $expressionParser = Mockery::mock(ExpressionParserInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge, $expressionParser); $position = new RequestPosition('homepage.top', [ new BannerResource('role', 'vip'), @@ -207,7 +234,9 @@ public function testClientSideTemplateWithEmbedModeShouldBeRendered(): void $rendererBridge ->shouldReceive('renderClientSide') ->once() - ->with($position, Matchers::equalTo(ClientSideMode::embed()), [], ['omit-default-resources' => '1']) + ->with($position, Matchers::equalTo(ClientSideMode::embed()), [], Mockery::on(function ($options): bool { + return $options instanceof Options && ['omit-default-resources' => '1'] === $options->toArray(); + })) ->andReturn('client-side'); Assert::same('client-side', $renderer->renderClientSide($position, [], [], ClientSideMode::embed())); @@ -217,7 +246,8 @@ public function testRendererExceptionShouldBeThrownOnRenderingWhenBridgeThrowsTh { $bannersResolver = Mockery::mock(BannersResolverInterface::class); $rendererBridge = Mockery::mock(RendererBridgeInterface::class); - $renderer = new Renderer($bannersResolver, $rendererBridge); + $expressionParser = Mockery::mock(ExpressionParserInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge, $expressionParser); $position = new ResponsePosition( null, @@ -227,6 +257,7 @@ public function testRendererExceptionShouldBeThrownOnRenderingWhenBridgeThrowsTh null, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -234,7 +265,9 @@ public function testRendererExceptionShouldBeThrownOnRenderingWhenBridgeThrowsTh $rendererBridge ->shouldReceive('renderNotFound') ->once() - ->with($position, [], []) + ->with($position, [], Mockery::on(function ($options): bool { + return $options instanceof Options && [] === $options->toArray(); + })) ->andThrow(new RendererException('Test exception')); Assert::exception( @@ -248,14 +281,17 @@ public function testRendererExceptionShouldBeThrownOnClientSideRenderingWhenBrid { $bannersResolver = Mockery::mock(BannersResolverInterface::class); $rendererBridge = Mockery::mock(RendererBridgeInterface::class); - $renderer = new Renderer($bannersResolver, $rendererBridge); + $expressionParser = Mockery::mock(ExpressionParserInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge, $expressionParser); $position = new RequestPosition('homepage.top'); $rendererBridge ->shouldReceive('renderClientSide') ->once() - ->with($position, Matchers::equalTo(ClientSideMode::managed()), [], []) + ->with($position, Matchers::equalTo(ClientSideMode::managed()), [], Mockery::on(function ($options): bool { + return $options instanceof Options && [] === $options->toArray(); + })) ->andThrow(new RendererException('Test exception')); Assert::exception( @@ -269,7 +305,8 @@ public function testRendererExceptionShouldBeThrownOnRenderingWhenBridgeThrowsAn { $bannersResolver = Mockery::mock(BannersResolverInterface::class); $rendererBridge = Mockery::mock(RendererBridgeInterface::class); - $renderer = new Renderer($bannersResolver, $rendererBridge); + $expressionParser = Mockery::mock(ExpressionParserInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge, $expressionParser); $position = new ResponsePosition( null, @@ -279,6 +316,7 @@ public function testRendererExceptionShouldBeThrownOnRenderingWhenBridgeThrowsAn null, ResponsePosition::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -286,7 +324,9 @@ public function testRendererExceptionShouldBeThrownOnRenderingWhenBridgeThrowsAn $rendererBridge ->shouldReceive('renderNotFound') ->once() - ->with($position, [], []) + ->with($position, [], Mockery::on(function ($options): bool { + return $options instanceof Options && [] === $options->toArray(); + })) ->andThrow(new Exception('Test exception')); Assert::exception( @@ -300,14 +340,17 @@ public function testRendererExceptionShouldBeThrownOnClientSideRenderingWhenBrid { $bannersResolver = Mockery::mock(BannersResolverInterface::class); $rendererBridge = Mockery::mock(RendererBridgeInterface::class); - $renderer = new Renderer($bannersResolver, $rendererBridge); + $expressionParser = Mockery::mock(ExpressionParserInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge, $expressionParser); $position = new RequestPosition('homepage.top'); $rendererBridge ->shouldReceive('renderClientSide') ->once() - ->with($position, Matchers::equalTo(ClientSideMode::managed()), [], []) + ->with($position, Matchers::equalTo(ClientSideMode::managed()), [], Mockery::on(function ($options): bool { + return $options instanceof Options && [] === $options->toArray(); + })) ->andThrow(new Exception('Test exception')); Assert::exception( diff --git a/tests/resources/renderer/multiple/data-provider.php b/tests/resources/renderer/multiple/data-provider.php index d80cec4..08d569b 100644 --- a/tests/resources/renderer/multiple/data-provider.php +++ b/tests/resources/renderer/multiple/data-provider.php @@ -18,6 +18,7 @@ Position::DisplayTypeMultiple, Position::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(800, 300), [], ); @@ -30,6 +31,7 @@ Position::DisplayTypeMultiple, Position::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); @@ -145,8 +147,7 @@ $fullFeaturedBanners, [], [ - 'loading' => 'lazy', - 'loading-offset' => 1, + 'loading' => '>=1:lazy', ], __DIR__ . '/multipleBannersFullFeatured.withLazyLoadingFromOffset1.html', ], diff --git a/tests/resources/renderer/multiple/multipleBannersFullFeatured.withLazyLoadingFromOffset1.html b/tests/resources/renderer/multiple/multipleBannersFullFeatured.withLazyLoadingFromOffset1.html index eb76863..d13835a 100644 --- a/tests/resources/renderer/multiple/multipleBannersFullFeatured.withLazyLoadingFromOffset1.html +++ b/tests/resources/renderer/multiple/multipleBannersFullFeatured.withLazyLoadingFromOffset1.html @@ -1,4 +1,4 @@ -
+
diff --git a/tests/resources/renderer/not-found/data-provider.php b/tests/resources/renderer/not-found/data-provider.php index 297e780..10ca525 100644 --- a/tests/resources/renderer/not-found/data-provider.php +++ b/tests/resources/renderer/not-found/data-provider.php @@ -14,6 +14,7 @@ null, Position::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(800, 300), [], ); diff --git a/tests/resources/renderer/random/data-provider.php b/tests/resources/renderer/random/data-provider.php index 36e64ce..c47f087 100644 --- a/tests/resources/renderer/random/data-provider.php +++ b/tests/resources/renderer/random/data-provider.php @@ -18,6 +18,7 @@ Position::DisplayTypeRandom, Position::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(800, 300), [], ); @@ -30,6 +31,7 @@ Position::DisplayTypeRandom, Position::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); diff --git a/tests/resources/renderer/single/data-provider.php b/tests/resources/renderer/single/data-provider.php index e95d6c5..96403ae 100644 --- a/tests/resources/renderer/single/data-provider.php +++ b/tests/resources/renderer/single/data-provider.php @@ -18,6 +18,7 @@ Position::DisplayTypeSingle, Position::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(800, 300), [], ); @@ -30,6 +31,7 @@ Position::DisplayTypeSingle, Position::BreakpointTypeMin, ResponsePosition::ModeManaged, + [], new Dimensions(null, null), [], ); diff --git a/tests/resources/response-body/fetch-banners.full.php b/tests/resources/response-body/fetch-banners.full.php index d3fa981..00ebba9 100644 --- a/tests/resources/response-body/fetch-banners.full.php +++ b/tests/resources/response-body/fetch-banners.full.php @@ -19,6 +19,7 @@ Position::DisplayTypeMultiple, Position::BreakpointTypeMin, Position::ModeManaged, + [], new Dimensions( 1320, 400, @@ -86,6 +87,7 @@ Position::DisplayTypeRandom, Position::BreakpointTypeMin, Position::ModeManaged, + [], new Dimensions( 800, 300, @@ -100,6 +102,7 @@ null, Position::BreakpointTypeMin, Position::ModeManaged, + [], new Dimensions( null, null, @@ -114,6 +117,7 @@ Position::DisplayTypeSingle, Position::BreakpointTypeMin, Position::ModeManaged, + [], new Dimensions( 800, 300,