diff --git a/.github/workflows/test-application.yaml b/.github/workflows/test-application.yaml
index 1493235..08b3c6d 100644
--- a/.github/workflows/test-application.yaml
+++ b/.github/workflows/test-application.yaml
@@ -34,9 +34,15 @@ jobs:
env:
SYMFONY_DEPRECATIONS_HELPER: weak
+ - php-version: '8.3'
+ dependency-versions: 'highest'
+ tools: 'composer:v2'
+ env:
+ SYMFONY_DEPRECATIONS_HELPER: weak
+
steps:
- name: Checkout project
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Install and configure PHP
uses: shivammathur/setup-php@v2
@@ -44,8 +50,13 @@ jobs:
php-version: ${{ matrix.php-version }}
tools: ${{ matrix.tools }}
+ - name: Remove Lint Tools
+ # These tools are not required to run tests, so we are removing them to improve dependency resolving and
+ # testing lowest versions.
+ run: composer remove "*php-cs-fixer*" "*phpstan*" "*rector*" --dev --no-update
+
- name: Install composer dependencies
- uses: ramsey/composer-install@v1
+ uses: ramsey/composer-install@v2
with:
dependency-versions: ${{ matrix.dependency-versions }}
@@ -59,16 +70,16 @@ jobs:
steps:
- name: Checkout project
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Install and configure PHP
uses: shivammathur/setup-php@v2
with:
- php-version: 8.0
+ php-version: 8.3
tools: composer
- name: Install composer dependencies
- uses: ramsey/composer-install@v1
+ uses: ramsey/composer-install@v2
- name: Lint code
run: composer lint
diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php
similarity index 54%
rename from .php_cs.dist
rename to .php-cs-fixer.dist.php
index d7b95fb..bf00999 100644
--- a/.php_cs.dist
+++ b/.php-cs-fixer.dist.php
@@ -9,8 +9,12 @@
with this source code in the file LICENSE.
EOF;
-return PhpCsFixer\Config::create()
- ->setRiskyAllowed(true)
+$finder = PhpCsFixer\Finder::create()
+ ->exclude(['vendor'])
+ ->in(__DIR__);
+
+$config = new PhpCsFixer\Config();
+$config->setRiskyAllowed(true)
->setRules([
'@Symfony' => true,
'@Symfony:risky' => true,
@@ -18,9 +22,7 @@
'concat_space' => ['spacing' => 'one'],
'array_syntax' => ['syntax' => 'short'],
'phpdoc_align' => false,
- 'class_definition' => [
- 'multiLineExtendsEachSingleLine' => true,
- ],
+ 'class_definition' => false,
'linebreak_after_opening_tag' => true,
'declare_strict_types' => true,
'mb_str_functions' => false,
@@ -35,9 +37,17 @@
'strict_comparison' => true,
'strict_param' => true,
'header_comment' => ['header' => $header],
+ 'native_constant_invocation' => true,
+ 'native_function_casing' => true,
+ 'native_function_invocation' => false,
+ 'get_class_to_class_keyword' => false, // should be enabled as soon as support for php < 8 is dropped
+ 'nullable_type_declaration_for_default_null_value' => true,
+ 'no_null_property_initialization' => false,
+ 'fully_qualified_strict_types' => false,
+ 'new_with_parentheses' => true,
+ 'modernize_strpos' => false,
+ 'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['array_destructuring', 'arrays']],
])
- ->setFinder(
- PhpCsFixer\Finder::create()
- ->exclude('vendor')
- ->in(__DIR__)
- );
+ ->setFinder($finder);
+
+return $config;
diff --git a/composer.json b/composer.json
index 7666dce..3ae51b8 100644
--- a/composer.json
+++ b/composer.json
@@ -14,18 +14,18 @@
"twig/twig": "^1.38 || ^2.7 || ^3.0"
},
"require-dev": {
- "friendsofphp/php-cs-fixer": "^2.11",
- "jangregor/phpstan-prophecy": "^0.8",
- "phpstan/phpstan": "^0.12",
- "phpstan/phpstan-phpunit": "^0.12",
+ "php-cs-fixer/shim": "^3.64",
+ "jangregor/phpstan-prophecy": "^1.0",
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^7.5 || ^8.5",
"symfony/intl": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0",
- "symfony/property-access": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0",
- "thecodingmachine/phpstan-strict-rules": "^0.12"
+ "symfony/property-access": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0",
+ "thecodingmachine/phpstan-strict-rules": "^1.0"
},
"conflict": {
- "symfony/intl": ">=7.0",
- "symfony/property-access": ">=7.0"
+ "symfony/intl": ">=8.0",
+ "symfony/property-access": ">=8.0"
},
"suggest": {
"symfony/property-access": "The ImageExtension requires the symfony/property-access service.",
diff --git a/docs/image.md b/docs/image.md
index 30a5a8d..3a8b4b6 100644
--- a/docs/image.md
+++ b/docs/image.md
@@ -130,6 +130,26 @@ This could be:
) }}
```
+Retina example:
+
+```twig
+
+```
+
+This could be:
+
+```twig
+{{ get_image(image,
+ {
+ src: '452x452',
+ srcset: '452x452 1x, 452x452@2x 2x',
+ }
+) }}
+```
+
##### 5. Lazy images
> See also 6. Native lazy loading for a modern implementation.
@@ -197,6 +217,20 @@ services:
loading: 'lazy'
```
+If you are setting the default to lazy which is recommended you maybe want the
+first image of hero slider be loaded immediately this can be achieved via
+setting `loading` to `null` for the first element:
+
+```twig
+{% for image in images %}
+ {{ get_image(image, {
+ src: '452x452',
+ srcset: '452x452 1x, 452x452@2x 2x',
+ loading: loop.first ? null : 'lazy',
+ }) }}
+{% endfor %}
+```
+
##### 7. Webp Support
If your server supports converting images to webp you can automatically enable webp
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
new file mode 100644
index 0000000..56204f5
--- /dev/null
+++ b/phpstan-baseline.neon
@@ -0,0 +1,151 @@
+parameters:
+ ignoreErrors:
+ -
+ message: "#^Cannot access offset 'className' on mixed\\.$#"
+ count: 2
+ path: src/IconExtension.php
+
+ -
+ message: "#^Cannot access offset 'classPrefix' on mixed\\.$#"
+ count: 2
+ path: src/IconExtension.php
+
+ -
+ message: "#^Cannot access offset 'classSuffix' on mixed\\.$#"
+ count: 2
+ path: src/IconExtension.php
+
+ -
+ message: "#^Cannot access offset 'path' on mixed\\.$#"
+ count: 1
+ path: src/IconExtension.php
+
+ -
+ message: "#^Cannot access offset 'type' on mixed\\.$#"
+ count: 1
+ path: src/IconExtension.php
+
+ -
+ message: "#^Method Sulu\\\\Twig\\\\Extensions\\\\IconExtension\\:\\:getIconSet\\(\\) should return array but returns mixed\\.$#"
+ count: 1
+ path: src/IconExtension.php
+
+ -
+ message: "#^Parameter \\#3 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#"
+ count: 1
+ path: src/IconExtension.php
+
+ -
+ message: "#^Parameter \\#4 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#"
+ count: 1
+ path: src/IconExtension.php
+
+ -
+ message: "#^Parameter \\#6 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#"
+ count: 1
+ path: src/IconExtension.php
+
+ -
+ message: "#^Cannot access offset 'height' on mixed\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Cannot access offset 'media' on mixed\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Cannot access offset 'srcset' on mixed\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Cannot access offset 'type' on mixed\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Cannot access offset 'width' on mixed\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Method Sulu\\\\Twig\\\\Extensions\\\\ImageExtension\\:\\:getMimeType\\(\\) should return string but returns mixed\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Offset 'filename' on array\\{dirname\\?\\: string, basename\\: string, extension\\?\\: string, filename\\: string\\} on left side of \\?\\? always exists and is not nullable\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#1 \\$array of function reset expects array\\|object, mixed given\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#1 \\$objectOrArray of method Symfony\\\\Component\\\\PropertyAccess\\\\PropertyAccessor\\:\\:getValue\\(\\) expects array\\|object, mixed given\\.$#"
+ count: 6
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#1 \\$objectOrArray of method Symfony\\\\Component\\\\PropertyAccess\\\\PropertyAccessor\\:\\:isReadable\\(\\) expects array\\|object, mixed given\\.$#"
+ count: 3
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#1 \\$path of function pathinfo expects string, mixed given\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#1 \\$srcsets of method Sulu\\\\Twig\\\\Extensions\\\\ImageExtension\\:\\:addExtension\\(\\) expects string, mixed given\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#1 \\$thumbnails of method Sulu\\\\Twig\\\\Extensions\\\\ImageExtension\\:\\:getLazyThumbnails\\(\\) expects array\\, mixed given\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, mixed given\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#2 \\$attributes of method Sulu\\\\Twig\\\\Extensions\\\\ImageExtension\\:\\:createTag\\(\\) expects array\\, array given\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#2 \\$attributes of method Sulu\\\\Twig\\\\Extensions\\\\ImageExtension\\:\\:createTag\\(\\) expects array\\, mixed given\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#2 \\.\\.\\.\\$arrays of function array_merge expects array, mixed given\\.$#"
+ count: 1
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#3 \\$thumbnails of method Sulu\\\\Twig\\\\Extensions\\\\ImageExtension\\:\\:createTag\\(\\) expects array\\, mixed given\\.$#"
+ count: 3
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Parameter \\#5 \\$additionalTypes of method Sulu\\\\Twig\\\\Extensions\\\\ImageExtension\\:\\:createImage\\(\\) expects array\\, array given\\.$#"
+ count: 2
+ path: src/ImageExtension.php
+
+ -
+ message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
+ count: 1
+ path: src/PortalExtension.php
+
+ -
+ message: "#^Cannot access an offset on mixed\\.$#"
+ count: 1
+ path: src/PortalExtension.php
diff --git a/phpstan.neon b/phpstan.neon
index 7b1fe07..68b5cd0 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,4 +1,5 @@
includes:
+ - phpstan-baseline.neon
- vendor/jangregor/phpstan-prophecy/extension.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
@@ -10,7 +11,7 @@ parameters:
- src
- tests
inferPrivatePropertyTypeFromConstructor: true
- excludes_analyse:
+ excludePaths:
- %currentWorkingDirectory%/vendor/*
ignoreErrors:
- '#Call to an undefined static method Symfony\\Component\\Intl\\Intl#'
diff --git a/src/IconExtension.php b/src/IconExtension.php
index a29b048..af88857 100644
--- a/src/IconExtension.php
+++ b/src/IconExtension.php
@@ -21,8 +21,8 @@
*/
class IconExtension extends AbstractExtension
{
- const ICON_SET_TYPE_SVG = 'svg';
- const ICON_SET_TYPE_FONT = 'font';
+ public const ICON_SET_TYPE_SVG = 'svg';
+ public const ICON_SET_TYPE_FONT = 'font';
/**
* @var mixed[]
diff --git a/src/ImageExtension.php b/src/ImageExtension.php
index 361267e..1346501 100644
--- a/src/ImageExtension.php
+++ b/src/ImageExtension.php
@@ -100,7 +100,7 @@ public function __construct(
array $defaultAttributes = [],
array $defaultAdditionalTypes = [],
bool $aspectRatio = false,
- array $imageFormatConfiguration = null
+ ?array $imageFormatConfiguration = null
) {
if (null !== $placeholderPath) {
$this->placeholderPath = rtrim($placeholderPath, '/') . '/';
diff --git a/src/Node/PortalNode.php b/src/Node/PortalNode.php
index d9a1dbf..3df41a5 100644
--- a/src/Node/PortalNode.php
+++ b/src/Node/PortalNode.php
@@ -24,7 +24,7 @@ final class PortalNode extends Node
/**
* @param Node $body
*/
- public function __construct(string $name, Node $body, int $lineno, string $tag = null)
+ public function __construct(string $name, Node $body, int $lineno, ?string $tag = null)
{
parent::__construct(['body' => $body], ['name' => $name], $lineno, $tag);
}
diff --git a/src/UrlExtension.php b/src/UrlExtension.php
index 0f4bb48..1e90b43 100644
--- a/src/UrlExtension.php
+++ b/src/UrlExtension.php
@@ -21,18 +21,18 @@
*/
class UrlExtension extends AbstractExtension
{
- const DEFAULT_SCHEME = 'http';
-
- const SCHEME = 'scheme';
- const USER = 'user';
- const PASS = 'pass';
- const HOST = 'host';
- const PORT = 'port';
- const PATH = 'path';
- const QUERY = 'query';
- const FRAGMENT = 'fragment';
-
- const DEFAULT_FLAGS = [
+ public const DEFAULT_SCHEME = 'http';
+
+ public const SCHEME = 'scheme';
+ public const USER = 'user';
+ public const PASS = 'pass';
+ public const HOST = 'host';
+ public const PORT = 'port';
+ public const PATH = 'path';
+ public const QUERY = 'query';
+ public const FRAGMENT = 'fragment';
+
+ public const DEFAULT_FLAGS = [
self::SCHEME => true,
self::USER => true,
self::PASS => true,
diff --git a/tests/Unit/ImageExtensionTest.php b/tests/Unit/ImageExtensionTest.php
index 3bebc9c..d76b475 100644
--- a/tests/Unit/ImageExtensionTest.php
+++ b/tests/Unit/ImageExtensionTest.php
@@ -509,9 +509,9 @@ public function testIgnoreAdditionalTypesForSvg(): void
$imageExtension = new ImageExtension(null, [], ['webp' => 'image/webp']);
$this->assertSame(
- '',
+ '',
$imageExtension->getImage($this->svgImage, [
'src' => 'sulu-100x100',
])
@@ -556,6 +556,26 @@ public function testAdditionalTypesWithSrcSet(): void
);
}
+ public function testAdditionalTypesWithSrcSetWith1x(): void
+ {
+ $imageExtension = new ImageExtension(null, [], []);
+
+ $this->assertSame(
+ '',
+ $imageExtension->getImage($this->image, [
+ 'src' => 'sulu-100x100',
+ 'srcset' => 'sulu-100x100 1x, sulu-100x100@2x 2x',
+ ], [], ['webp' => 'image/webp'])
+ );
+ }
+
public function testAdditionalLazyComplexPictureTag(): void
{
$imageExtension = new ImageExtension('/lazy', [], ['webp' => 'image/webp']);
@@ -645,7 +665,7 @@ public function testAspectRatioNoProperties(): void
[
'thumbnails' => [
'200x100-inset' => '/uploads/media/200x100-inset/01/image.jpg?v=1-0',
- '200x100-inset' . '.webp' => '/uploads/media/200x100-inset/01/image.webp?v=1-0',
+ '200x100-inset.webp' => '/uploads/media/200x100-inset/01/image.webp?v=1-0',
],
]
);
diff --git a/tests/Unit/UrlExtensionTest.php b/tests/Unit/UrlExtensionTest.php
index 0cf4c91..3a10cf3 100644
--- a/tests/Unit/UrlExtensionTest.php
+++ b/tests/Unit/UrlExtensionTest.php
@@ -18,7 +18,7 @@
class UrlExtensionTest extends TestCase
{
- const URL = 'https://john.doe:hidden@example.org:8080/admin?resource=pages&limit=20#1234';
+ private const URL = 'https://john.doe:hidden@example.org:8080/admin?resource=pages&limit=20#1234';
/**
* @var UrlExtension