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

AMP options + fetchpriority #6

Merged
merged 3 commits into from
Sep 19, 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
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.3.0] - 2024-09-19
### 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`.
Expand All @@ -30,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Initial release.

[Unreleased]: https://github.com/68publishers/amp-client-js/compare/v1.2.0...HEAD
[Unreleased]: https://github.com/68publishers/amp-client-js/compare/v1.3.0...HEAD
[1.2.0]: https://github.com/68publishers/amp-client-php/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/68publishers/amp-client-php/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/68publishers/amp-client-php/compare/v1.0.0...v1.1.0
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ $ composer require 68publishers/amp-client
| `~1.0.0` | `~1.4.0` | `2.12.0` | `1` |
| `~1.1.0` | `~1.4.0` | `2.12.0` | `1` |
| `~1.2.0` | `~1.5.0` | `>=2.13.0` | `1` |
| `~1.3.0` | `~1.6.0` | `>=2.16.0` | `1` |

## Integration without a framework

Expand Down
39 changes: 34 additions & 5 deletions docs/integration-without-framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 6 additions & 0 deletions src/Bridge/Nette/DI/AmpClientExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
94 changes: 94 additions & 0 deletions src/Expression/ExpressionParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\AmpClient\Expression;

use function array_map;
use function explode;
use function preg_match;
use function trim;

final class ExpressionParser implements ExpressionParserInterface
{
private const ExpressionRegex = '/^(?:(?<INTERVAL_FROM>\d+)-(?<INTERVAL_TO>\d+):|(?<EQ>\d+):|<(?<LT>\d+):|<=(?<LTE>\d+):|>(?<GT>\d+):|>=(?<GTE>\d+):)?(?<VALUE>[^:\s]+)$/';

/** @var array<string, ParsedExpression> */
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'];

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);
}
}
12 changes: 12 additions & 0 deletions src/Expression/ExpressionParserInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\AmpClient\Expression;

interface ExpressionParserInterface
{
public function parseExpression(string $expression): ParsedExpression;

public function evaluateExpression(string $expression, int $index): ?string;
}
101 changes: 101 additions & 0 deletions src/Expression/ExpressionRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\AmpClient\Expression;

use Closure;

final class ExpressionRule
{
public string $value;

/** @var Closure(int $index): bool */
private Closure $matcher;

/**
* @param Closure(int $index): bool $matcher
*/
private function __construct(
string $value,
Closure $matcher
) {
$this->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);
}
}
42 changes: 42 additions & 0 deletions src/Expression/ParsedExpression.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\AmpClient\Expression;

use function array_key_exists;

final class ParsedExpression
{
/** @var list<ExpressionRule> */
private array $rules;

/** @var array<string, string|null> */
private array $cache = [];

/**
* @param list<ExpressionRule> $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;
}
}
4 changes: 2 additions & 2 deletions src/Http/Cache/CacheControlHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ public function __construct(array $values)
foreach ($matches as $match) {
$val = '';
if (count($match) == 3) {
$val = $match[2];
$val = $match[2]; # @phpstan-ignore-line
} elseif (count($match) > 3) {
$val = $match[3];
$val = $match[3]; # @phpstan-ignore-line
}

$this->values[$match[1]] = $val;
Expand Down
Loading
Loading