Skip to content

Commit

Permalink
Add default attributes for native lazy loading (#24)
Browse files Browse the repository at this point in the history
* Add default attributes for native lazy loading

* Fix phpstan

* Update doc for default attributes

* Fix attributes phpdocs
  • Loading branch information
alexander-schranz committed Mar 31, 2020
1 parent bbd9563 commit d4df8d3
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 36 deletions.
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.11",
"jangregor/phpstan-prophecy": "^0.4.1",
"phpstan/phpstan": "^0.11.12",
"phpstan/phpstan-phpunit": "^0.11.2",
"jangregor/phpstan-prophecy": "^0.6",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpunit/phpunit": "^5.0 || ^6.0 || ^7.0 || ^7.1",
"symfony/intl": "^2.8 || ^3.0 || ^4.0 || ^5.0",
"symfony/property-access": "^2.8 || ^3.0 || ^4.0 || ^5.0",
"thecodingmachine/phpstan-strict-rules": "^0.11.2"
"thecodingmachine/phpstan-strict-rules": "^0.12"
},
"suggest": {
"symfony/property-access": "The ImageExtension requires the symfony/property-access service.",
Expand Down
36 changes: 35 additions & 1 deletion docs/image.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,16 @@ This could be:

##### 5. Lazy images

> See also 6. Native lazy loading for a modern implementation.
The `get_lazy_image` twig function accepts the same parameters as the `get_image` function.
It will render the img attributes `src` and `srcset` as `data-src` and `data-srcset`.
Values of `src` and `srcset` are set to a placeholder svg image of the configured `placeholderPath`

Use for example [lazysizes](https://github.com/aFarkas/lazysizes) JS Library to load the images when they are visible.

```twig
<img alt="Test" title="Test" data-src="/images/placeholders/sulu-100x100.svg" src="/uploads/media/sulu-100x100/01/image.jpg?v=1-0">
<img alt="Test" title="Test" src="/images/placeholders/sulu-100x100.svg" data-src="/uploads/media/sulu-100x100/01/image.jpg?v=1-0">
```

This could be:
Expand All @@ -142,10 +144,42 @@ The placeholder svg should look like this:
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"/>
```

For this you need configure the placeholder path in the service definition:

```yaml
services:
Sulu\Twig\Extensions\ImageExtension:
arguments:
$placeholderPath: '/images/placeholders'
```

With the `has_lazy_image()` twig function you could check if the current rendered code includes one ore more lazy images.

```twig
{% if has_lazy_image() %}
<script src="lazy.js"></script>
{% endif %}
```

##### 6. Native lazy loading

Browsers today have native support for [lazy-loading](https://caniuse.com/#feat=loading-lazy-attr).

You can do the following:

```twig
{{ get_image(image, {
src: '800x',
loading: 'lazy',
}) }}
```

or when you want by default load all images lazy use the following service definition:

```yml
services:
Sulu\Twig\Extensions\ImageExtension:
arguments:
$defaultAttributes:
loading: 'lazy'
```
14 changes: 7 additions & 7 deletions src/ComponentExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@
class ComponentExtension extends AbstractExtension
{
/**
* @var array
* @var array<int, array{name: string, id: string, options: mixed}>
*/
protected $components = [];

/**
* @var array
* @var array<string, int>
*/
protected $instanceCounter = [];

/**
* @var array
* @var array<int, array{name: string, func: string, args: mixed}>
*/
protected $services = [];

Expand Down Expand Up @@ -102,7 +102,7 @@ public function registerComponent(string $name, ?array $options = null, ?string
* @param bool $jsonEncode
* @param bool $clear
*
* @return string|array|false
* @return array<int, array{name: string, id: string, options: mixed}>|string|false
*/
public function getComponents(bool $jsonEncode = true, bool $clear = true)
{
Expand All @@ -120,7 +120,7 @@ public function getComponents(bool $jsonEncode = true, bool $clear = true)
*
* @param bool $jsonEncode
*
* @return string|array|false
* @return string|string[]|false
*/
public function getComponentList(bool $jsonEncode = false)
{
Expand Down Expand Up @@ -158,7 +158,7 @@ public function callService(string $name, string $function, array $parameters =
* @param bool $jsonEncode
* @param bool $clear
*
* @return array|string|false
* @return array<int, array{name: string, func: string, args: mixed}>|string|false
*/
public function getServices(bool $jsonEncode = true, bool $clear = true)
{
Expand All @@ -176,7 +176,7 @@ public function getServices(bool $jsonEncode = true, bool $clear = true)
*
* @param bool $jsonEncode
*
* @return string|array|false
* @return string|string[]|false
*/
public function getServiceList(bool $jsonEncode = false)
{
Expand Down
36 changes: 30 additions & 6 deletions src/ImageExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,21 @@ class ImageExtension extends AbstractExtension
*/
private $placeholders = null;

public function __construct(?string $placeholderPath = null)
/**
* @var string[]
*/
private $defaultAttributes = null;

/**
* @param string[] $defaultAttributes
*/
public function __construct(?string $placeholderPath = null, array $defaultAttributes = [])
{
if (null !== $placeholderPath) {
$this->placeholderPath = rtrim($placeholderPath, '/') . '/';
}

$this->defaultAttributes = $defaultAttributes;
}

/**
Expand All @@ -60,7 +70,7 @@ public function getFunctions()
* Get an image or picture tag with given attributes for lazy loading.
*
* @param mixed $media
* @param string[]|string $attributes
* @param array<string, string|null>|string $attributes
* @param mixed[] $sources
*
* @return string
Expand Down Expand Up @@ -98,7 +108,7 @@ public function hasLazyImage(): bool
* Get an image or picture tag with given attributes.
*
* @param mixed $media
* @param string[]|string $attributes
* @param array<string, string|null>|string $attributes
* @param mixed[] $sources
*
* @return string
Expand All @@ -112,7 +122,7 @@ public function getImage($media, $attributes = [], array $sources = []): string
* Get an image or picture tag with given attributes.
*
* @param mixed $media
* @param string[]|string $attributes
* @param array<string, string|null>|string $attributes
* @param mixed[] $sources
* @param string[]|null $lazyThumbnails
*
Expand Down Expand Up @@ -145,6 +155,11 @@ private function createImage(
];
}

$attributes = array_merge(
$this->defaultAttributes,
$attributes
);

if ($lazyThumbnails) {
$attributes['class'] = trim((isset($attributes['class']) ? $attributes['class'] : '') . ' lazyload');
}
Expand All @@ -155,10 +170,13 @@ private function createImage(
// Get description from object to use as title attribute else fallback to alt attribute.
$title = $propertyAccessor->getValue($media, 'description') ?: $alt;

/** @var array<string, string|null> $attributes */
$attributes = array_merge(['alt' => $alt, 'title' => $title], $attributes);

// Get the image tag with all given attributes.
$imgTag = $this->createTag(
'img',
array_merge(['alt' => $alt, 'title' => $title], $attributes),
$attributes,
$thumbnails,
$lazyThumbnails
);
Expand Down Expand Up @@ -191,7 +209,7 @@ private function createImage(
* Create html tag.
*
* @param string $tag
* @param string[] $attributes
* @param array<string, string|null> $attributes
* @param string[] $thumbnails
* @param string[]|null $lazyThumbnails
*
Expand All @@ -202,6 +220,12 @@ private function createTag(string $tag, array $attributes, array $thumbnails, ?a
$output = '';

foreach ($attributes as $key => $value) {
// Ignore properties which are set to null e.g.: { loading: null }
// This is used to remove default attributes from
if (null === $value) {
continue;
}

if ('src' === $key) {
if ($lazyThumbnails) {
$output .= sprintf(' %s="%s"', $key, $lazyThumbnails[$value]);
Expand Down
20 changes: 10 additions & 10 deletions src/UrlExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function getFilters()
*
* @param string $url
*
* @return array|null
* @return array<string, int|string>
*/
private static function parseUrl(string $url): ?array
{
Expand All @@ -82,7 +82,7 @@ private static function parseUrl(string $url): ?array
/**
* Returns true if all the required flags would return a valid value. Otherwise returns false.
*
* @param array|null $parsedUrl
* @param array<string, int|string>|null $parsedUrl
* @param bool[] $flags
* @param string[] $requiredFlags
*
Expand Down Expand Up @@ -217,7 +217,7 @@ public function getScheme(string $url): ?string
{
$parsedUrl = self::parseUrl($url);

return null !== $parsedUrl && isset($parsedUrl[self::SCHEME]) ? $parsedUrl[self::SCHEME] : null;
return null !== $parsedUrl && isset($parsedUrl[self::SCHEME]) ? (string) $parsedUrl[self::SCHEME] : null;
}

/**
Expand All @@ -231,7 +231,7 @@ public function getUser(string $url): ?string
{
$parsedUrl = self::parseUrl($url);

return null !== $parsedUrl && isset($parsedUrl[self::USER]) ? $parsedUrl[self::USER] : null;
return null !== $parsedUrl && isset($parsedUrl[self::USER]) ? (string) $parsedUrl[self::USER] : null;
}

/**
Expand All @@ -245,7 +245,7 @@ public function getPass(string $url): ?string
{
$parsedUrl = self::parseUrl($url);

return null !== $parsedUrl && isset($parsedUrl[self::PASS]) ? $parsedUrl[self::PASS] : null;
return null !== $parsedUrl && isset($parsedUrl[self::PASS]) ? (string) $parsedUrl[self::PASS] : null;
}

/**
Expand All @@ -259,7 +259,7 @@ public function getHost(string $url): ?string
{
$parsedUrl = self::parseUrl($url);

return null !== $parsedUrl && isset($parsedUrl[self::HOST]) ? $parsedUrl[self::HOST] : null;
return null !== $parsedUrl && isset($parsedUrl[self::HOST]) ? (string) $parsedUrl[self::HOST] : null;
}

/**
Expand All @@ -273,7 +273,7 @@ public function getPort(string $url): ?int
{
$parsedUrl = self::parseUrl($url);

return null !== $parsedUrl && isset($parsedUrl[self::PORT]) ? $parsedUrl[self::PORT] : null;
return null !== $parsedUrl && isset($parsedUrl[self::PORT]) ? (int) $parsedUrl[self::PORT] : null;
}

/**
Expand All @@ -287,7 +287,7 @@ public function getPath(string $url): ?string
{
$parsedUrl = self::parseUrl($url);

return null !== $parsedUrl && isset($parsedUrl[self::PATH]) ? $parsedUrl[self::PATH] : null;
return null !== $parsedUrl && isset($parsedUrl[self::PATH]) ? (string) $parsedUrl[self::PATH] : null;
}

/**
Expand All @@ -301,7 +301,7 @@ public function getQuery(string $url): ?string
{
$parsedUrl = self::parseUrl($url);

return null !== $parsedUrl && isset($parsedUrl[self::QUERY]) ? $parsedUrl[self::QUERY] : null;
return null !== $parsedUrl && isset($parsedUrl[self::QUERY]) ? (string) $parsedUrl[self::QUERY] : null;
}

/**
Expand All @@ -315,6 +315,6 @@ public function getFragment(string $url): ?string
{
$parsedUrl = self::parseUrl($url);

return null !== $parsedUrl && isset($parsedUrl[self::FRAGMENT]) ? $parsedUrl[self::FRAGMENT] : null;
return null !== $parsedUrl && isset($parsedUrl[self::FRAGMENT]) ? (string) $parsedUrl[self::FRAGMENT] : null;
}
}
5 changes: 3 additions & 2 deletions tests/ComponentExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ComponentExtensionTest extends TestCase
*/
private $componentExtension;

public function setup()
public function setUp(): void
{
$this->componentExtension = new ComponentExtension();
}
Expand Down Expand Up @@ -137,16 +137,17 @@ public function testRegisterComponentArray(): void
{
$this->assertSame('test-1', $this->componentExtension->registerComponent('test'));

/** @var array<int, array{name: string, id: string, options: mixed}> $components */
$components = $this->componentExtension->getComponents(false);
$this->assertNotFalse($components);
$this->assertIsObject($components[0]['options']);
unset($components[0]['options']);

$this->assertSame(
[
[
'name' => 'test',
'id' => 'test-1',
'options' => $components[0]['options'],
],
],
$components
Expand Down
2 changes: 1 addition & 1 deletion tests/CountExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class CountExtensionTest extends TestCase
*/
private $countExtension;

public function setup()
public function setUp(): void
{
$this->countExtension = new CountExtension();
}
Expand Down
2 changes: 1 addition & 1 deletion tests/EditorExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class EditorExtensionTest extends TestCase
*/
private $editorExtension;

public function setup()
public function setUp(): void
{
$this->editorExtension = new EditorExtension();
}
Expand Down
Loading

0 comments on commit d4df8d3

Please sign in to comment.