diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 28ea058be..64324ab2d 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -26,30 +26,37 @@ jobs: symfony: '*' stability: 'stable' - - php: '8.1' + # Minimum supported dependencies with the latest supported PHP version + - php: '8.2' dependencies: lowest symfony: '*' stability: 'stable' + # Test each supported Symfony version with the lowest supported PHP version - php: '8.0' dependencies: highest - symfony: '5.3.*' + symfony: '5.4.*' stability: 'stable' - # Test Symfony 5.4 dev version - - php: '8.0' + - php: '8.1' dependencies: highest - symfony: '5.4.*' + symfony: '6.4.*' + stability: 'stable' + + # Test Symfony 6.4 dev version + - php: '8.2' + dependencies: highest + symfony: '6.4.*' stability: 'dev' - # Test Symfony 6.0 dev version - - php: '8.0' + # Test Symfony 7.0 dev version + - php: '8.2' dependencies: highest - symfony: '6.0.*' + symfony: '7.0.*' stability: 'dev' steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure coverage driver id: coverage @@ -66,9 +73,14 @@ jobs: if: matrix.symfony != '*' run: | composer global config --no-plugins allow-plugins.symfony/flex true - composer global require --no-interaction --no-progress symfony/flex:^1.11 + composer global require --no-interaction --no-progress symfony/flex composer config extra.symfony.require ${{ matrix.symfony }} + - name: Remove non-compatible dependencies with Symfony 7 + if: matrix.symfony == '7.0.*' + run: | + composer remove enqueue/enqueue-bundle symfony/templating --dev --no-update + - name: Set minimum-stability run: composer config minimum-stability ${{ matrix.stability }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c72e4259..ca9814f07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ for a given releases. Unreleased, upcoming changes will be updated here periodic - The response when the `filter` parameter in a resolve request is not an array is now 400 bad request, and no longer 404 not found. +# 2.x + +## [2.12.1](https://github.com/liip/LiipImagineBundle/tree/2.12.1) + +- Adjustments to install with Symfony 7 ([mbabker](https://github.com/liip/LiipImagineBundle/pull/1535)) + ## [2.12.0](https://github.com/liip/LiipImagineBundle/tree/2.12.0) - Fix documentation filter command parameter name ([rdavaillaud](https://github.com/liip/LiipImagineBundle/pull/1515)) diff --git a/composer.json b/composer.json index d339775a1..2b6c5c590 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "type": "symfony-bundle", "description": "This bundle provides an image manipulation abstraction toolkit for Symfony-based projects.", "keywords": [ "imagine", "image", "manipulation", "pictures", "photos", "transformation", "bundle", "symfony", "liip" ], - "homepage": "http://liip.ch", + "homepage": "https://www.liip.ch", "license": "MIT", "authors": [ { @@ -25,34 +25,34 @@ "php": "^8.0", "ext-mbstring": "*", "imagine/imagine": "^1.3.2", - "symfony/filesystem": "^5.3|^6.0", - "symfony/finder": "^5.3|^6.0", - "symfony/framework-bundle": "^5.3|^6.0", - "symfony/mime": "^5.3|^6.0", - "symfony/options-resolver": "^5.3|^6.0", - "symfony/process": "^5.3|^6.0", + "symfony/filesystem": "^5.3|^6.0|^7.0", + "symfony/finder": "^5.3|^6.0|^7.0", + "symfony/framework-bundle": "^5.3|^6.0|^7.0", + "symfony/mime": "^5.3|^6.0|^7.0", + "symfony/options-resolver": "^5.3|^6.0|^7.0", + "symfony/process": "^5.3|^6.0|^7.0", "twig/twig": "^2.9|^3.0" }, "require-dev": { "ext-gd": "*", "amazonwebservices/aws-sdk-for-php": "^1.0", - "aws/aws-sdk-php": "^2.4", + "aws/aws-sdk-php": "^2.4|^3.0", "doctrine/persistence": "^1.3|^2.0", "league/flysystem": "^1.0|^2.0|^3.0", "phpstan/phpstan": "^1.10", "phpstan/phpstan-symfony": "^1.0", "psr/cache": "^1.0|^2.0|^3.0", "psr/log": "^1.0", - "symfony/browser-kit": "^5.3|^6.0", - "symfony/cache": "^5.3|^6.0", - "symfony/console": "^5.3|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/form": "^5.3|^6.0", - "symfony/messenger": "^5.3|^6.0", - "symfony/phpunit-bridge": "^5.3|^6.0", - "symfony/templating": "^5.3|^6.0", - "symfony/validator": "^5.3|^6.0", - "symfony/yaml": "^5.3|^6.0" + "symfony/browser-kit": "^5.3|^6.0|^7.0", + "symfony/cache": "^5.3|^6.0|^7.0", + "symfony/console": "^5.3|^6.0|^7.0", + "symfony/dependency-injection": "^5.3|^6.0|^7.0", + "symfony/form": "^5.3|^6.0|^7.0", + "symfony/messenger": "^5.3|^6.0|^7.0", + "symfony/phpunit-bridge": "^5.3|^6.0|^7.0", + "symfony/templating": "^5.3|^6.0|^7.0", + "symfony/validator": "^5.3|^6.0|^7.0", + "symfony/yaml": "^5.3|^6.0|^7.0" }, "suggest": { "ext-exif": "required to read EXIF metadata from images", diff --git a/doc/configuration.rst b/doc/configuration.rst index 3b1d2577a..ae5132d51 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -40,8 +40,8 @@ The default configuration for the bundle looks like this: filters: [] post_processors: [] controller: - filter_action: liip_imagine.controller:filterAction - filter_runtime_action: liip_imagine.controller:filterRuntimeAction + filter_action: liip_imagine.controller::filterAction + filter_runtime_action: liip_imagine.controller::filterRuntimeAction redirect_response_code: 302 webp: generate: false diff --git a/doc/data-loader/flysystem.rst b/doc/data-loader/flysystem.rst index 4d0e47d02..78c762140 100644 --- a/doc/data-loader/flysystem.rst +++ b/doc/data-loader/flysystem.rst @@ -33,7 +33,7 @@ Using `OneupFlysystemBundle`_, a basic configuration might look as follows: .. code-block:: yaml - # app/config/config.yml + # /config/liip_imagine.yaml liip_imagine: loaders: @@ -42,6 +42,9 @@ Using `OneupFlysystemBundle`_, a basic configuration might look as follows: filesystem_service: oneup_flysystem.profile_photos_filesystem data_loader: profile_photos + + # /config/oneup_flysystem.yaml + oneup_flysystem: adapters: profile_photos: @@ -57,7 +60,7 @@ Using `The League FlysystemBundle`_: .. code-block:: yaml - # app/config/config.yml + # /config/liip_imagine.yaml liip_imagine: loaders: @@ -67,6 +70,9 @@ Using `The League FlysystemBundle`_: filesystem_service: 'profile_photos.storage' data_loader: profile_photos + + # /config/flysystem.yaml + flysystem: storages: profile_photos.storage: diff --git a/doc/filters/general.rst b/doc/filters/general.rst index b0b350474..048d2c2b9 100644 --- a/doc/filters/general.rst +++ b/doc/filters/general.rst @@ -130,6 +130,81 @@ Interlace Options ``plane``, and ``partition``. +.. _filter-resample: + +Resample +-------- + +The built-in ``resample`` filter provides a resampling transformation by allows you to +change the resolution of an image. This filter exposes a number of `resample options`_ which +may be used to configure its behavior. + +.. tip:: + + Resampling changes the image resolution (also known as "pixel density") of an image + and is useful when you need to present different versions of an image dependent on + the user's screen density. For example, you may need to provide a "normal" and a + "retina" variant. + + The use of "resolution" is not to be confused with "dimensions". This filter does not + affect the dimentions of an image, only the pixel density. + + +Example configuration: + +.. code-block:: yaml + + # app/config/config.yml + + liip_imagine: + filter_sets: + + # name our filter set "my_resample_filter" + my_resample_filter: + filters: + + # use and setup the "resample" filter + resample: + + # set the unit to use for pixel density + unit: ppi + + # set the horizontal pixel density + x: 72 + + # set the vertical pixel density + y: 72 + + # set the resampling filter + filter: lanczos + + # set the temporary path to use for resampling work + tmp_dir: /my/custom/temporary/directory/path + + +Resample Options +~~~~~~~~~~~~~~~~ + +**unit:** ``string`` + Sets the unit to use for pixel density, either "pixels per inch" or "pixels per centimeter". + Valid values: ``ppi`` and ``ppc``. + +**x:** ``int|float`` + Sets the horizontal (x) pixel density to resample the image to. + +**y:** ``int|float`` + Sets the vertical (y) pixel density to resample the image to. + +**filter:** ``string`` + Sets the optional filter to use during the resampling operation. It must be a string resolvable + as a constant from `Imagine\Image\ImageInterface`_ (you may omit the ``FILTER_`` prefix) + or a valid fully qualified constant. By default it is set to ``FILTER_UNDEFINED``. + +**tmp_dir:** ``string`` + Sets the optional temporary work directory. This filter requires a temporary location to save + out and read back in the image binary, as these operations are requires to resample an image. + By default, it is set to the value of the `sys_get_temp_dir()`_ function. + .. _filter-strip: Strip diff --git a/doc/filters/sizing.rst b/doc/filters/sizing.rst index 6b6f04f61..424b7b2c9 100644 --- a/doc/filters/sizing.rst +++ b/doc/filters/sizing.rst @@ -53,6 +53,10 @@ Thumbnail Options Sets the generated thumbnail size as an integer array containing the dimensions as width and height values. +**allow_upscale:** ``bool`` + Toggles allowing image up-scaling when the image is smaller than the desired + thumbnail size. + .. _filter-fixed: Fixed size diff --git a/doc/post-processors/png-opti.rst b/doc/post-processors/png-opti.rst index 0c3c91799..6f832d717 100644 --- a/doc/post-processors/png-opti.rst +++ b/doc/post-processors/png-opti.rst @@ -49,6 +49,12 @@ Options When multi-images are encountered (for example, an animated image), this causes one of the images to be kept and drops the other ones. Depending on the input format, this may be either the first or the most relevant (e.g. the largest) image. +**strip_all:** ``bool`` (deprecated) + Removes all comments, EXIF markers, and other image metadata. + +**level:** ``int`` + Sets the image optimization factor, from 0 to 7. + **strip:** ``bool|string`` When set to ``true``, all extra image headers, such as its comments, EXIF markers, and other metadata, will be removed. Equivalently, the string value ``all`` also removes all extra metadata. diff --git a/doc/post-processors/png-quant.rst b/doc/post-processors/png-quant.rst index f9f93b546..d8fa33410 100644 --- a/doc/post-processors/png-quant.rst +++ b/doc/post-processors/png-quant.rst @@ -59,4 +59,4 @@ Parameters ---------- **liip_imagine.pngquant.binary:** ``string`` - Sets the location of the ``pnquant`` executable. Default is ``/usr/bin/pngquant``. + Sets the location of the ``pngquant`` executable. Default is ``/usr/bin/pngquant``. diff --git a/phpstan.neon.dist b/phpstan.neon.dist index c3d0a6945..5c96010a6 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -12,3 +12,7 @@ parameters: - .phpstan/stubs/ReadInterface.stub dynamicConstantNames: - Symfony\Component\HttpKernel\Kernel::VERSION + ignoreErrors: + # Depending on aws sdk version, this method has 2 or 4 parameters. The extra parameters will just be ignored. + # https://github.com/liip/LiipImagineBundle/issues/1457 + - '#^Method Aws\\S3\\S3Client::getObjectUrl\(\) invoked with 4 parameters, 2 required\.$#' diff --git a/src/Imagine/Cache/Resolver/AwsS3Resolver.php b/src/Imagine/Cache/Resolver/AwsS3Resolver.php index 2b8aa919d..8db5cb78c 100644 --- a/src/Imagine/Cache/Resolver/AwsS3Resolver.php +++ b/src/Imagine/Cache/Resolver/AwsS3Resolver.php @@ -159,7 +159,7 @@ public function remove(array $paths, array $filters): void * * @return $this * - * @see \Aws\S3\S3Client::getObjectUrl() for available options + * @see S3Client::getObjectUrl() for available options */ public function setGetOption(string $key, $value): self { @@ -178,7 +178,7 @@ public function setGetOption(string $key, $value): self * * @return $this * - * @see \Aws\S3\S3Client::putObject() for available options + * @see S3Client::putObject() for available options */ public function setPutOption(string $key, $value): self { diff --git a/src/Imagine/Filter/Loader/PasteFilterLoader.php b/src/Imagine/Filter/Loader/PasteFilterLoader.php index 8776ea67c..cce35a028 100644 --- a/src/Imagine/Filter/Loader/PasteFilterLoader.php +++ b/src/Imagine/Filter/Loader/PasteFilterLoader.php @@ -28,7 +28,7 @@ public function __construct(ImagineInterface $imagine, string $projectDir) } /** - * @see \Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface::load() + * @see LoaderInterface::load */ public function load(ImageInterface $image, array $options = []): ImageInterface { diff --git a/src/Message/Handler/WarmupCacheHandler.php b/src/Message/Handler/WarmupCacheHandler.php index 7d1768003..ba1075b27 100644 --- a/src/Message/Handler/WarmupCacheHandler.php +++ b/src/Message/Handler/WarmupCacheHandler.php @@ -14,14 +14,13 @@ use Liip\ImagineBundle\Imagine\Filter\FilterManager; use Liip\ImagineBundle\Message\WarmupCache; use Liip\ImagineBundle\Service\FilterService; -use Symfony\Component\Messenger\Handler\MessageHandlerInterface; /** * Listen to WarmupCache messages and prepare the cache for those images. * * @experimental */ -class WarmupCacheHandler implements MessageHandlerInterface +class WarmupCacheHandler { private FilterManager $filterManager; diff --git a/tests/Functional/Controller/ImagineControllerTest.php b/tests/Functional/Controller/ImagineControllerTest.php index c92d8e269..0043b7b32 100644 --- a/tests/Functional/Controller/ImagineControllerTest.php +++ b/tests/Functional/Controller/ImagineControllerTest.php @@ -37,7 +37,7 @@ protected function setUp(): void // supported by the current PHP build or not. Enabling WebP in configurations will drop all tests if WebP is // not supported. if ($this->webp_generate) { - $filterService = self::getService('test.liip_imagine.service.filter'); + $filterService = $this->getService('test.liip_imagine.service.filter'); $webpGenerate = new \ReflectionProperty($filterService, 'webpGenerate'); $webpGenerate->setAccessible(true); $webpGenerate->setValue($filterService, true); @@ -162,7 +162,7 @@ public function testShouldResolveWebPFromCache(): void public function testThrowBadRequestIfSignInvalidWhileUsingCustomFilters(): void { - $this->expectException(\Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class); + $this->expectException(BadRequestHttpException::class); $this->expectExceptionMessage('Signed url does not pass the sign check for path "images/cats.jpeg" and filter "thumbnail_web_path" and runtime config {"thumbnail":{"size":["50","50"]}}'); $this->client->request('GET', '/media/cache/resolve/thumbnail_web_path/rc/invalidHash/images/cats.jpeg?'.http_build_query([ @@ -202,7 +202,7 @@ public function testInvalidFilterShouldThrowNotFoundHttpException(): void public function testShouldResolveWithCustomFiltersPopulatingCacheFirst(): void { /** @var Signer $signer */ - $signer = self::getService('liip_imagine.cache.signer'); + $signer = $this->getService('liip_imagine.cache.signer'); $params = [ 'filters' => [ @@ -240,7 +240,7 @@ public function testShouldResolveWithCustomFiltersPopulatingCacheFirst(): void public function testShouldResolveWithCustomFiltersPopulatingCacheFirstWebP(): void { /** @var Signer $signer */ - $signer = self::getService('liip_imagine.cache.signer'); + $signer = $this->getService('liip_imagine.cache.signer'); $params = [ 'filters' => [ @@ -283,7 +283,7 @@ public function testShouldResolveWithCustomFiltersPopulatingCacheFirstWebP(): vo public function testShouldResolveWithCustomFiltersFromCache(): void { /** @var Signer $signer */ - $signer = self::getService('liip_imagine.cache.signer'); + $signer = $this->getService('liip_imagine.cache.signer'); $params = [ 'filters' => [ diff --git a/tests/Message/Handler/WarmupCacheHandlerTest.php b/tests/Message/Handler/WarmupCacheHandlerTest.php index 78adfda9d..29779a2b7 100644 --- a/tests/Message/Handler/WarmupCacheHandlerTest.php +++ b/tests/Message/Handler/WarmupCacheHandlerTest.php @@ -19,7 +19,6 @@ use Liip\ImagineBundle\Message\WarmupCache; use Liip\ImagineBundle\Service\FilterService; use Liip\ImagineBundle\Tests\Functional\AbstractWebTestCase; -use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\MessageBusInterface; /** @@ -34,13 +33,6 @@ protected function setUp(): void } } - public function testShouldImplementMessageHandlerInterface(): void - { - $rc = new \ReflectionClass(WarmupCacheHandler::class); - - $this->assertTrue($rc->implementsInterface(MessageHandlerInterface::class)); - } - public function testCouldBeConstructedWithExpectedArguments(): void { static::createClient();