Repository: spatie/image-optimizer (language: php)

Issue:
OptimizerChainFactory refactoring broke "quality" config
CC @0xb4lint 

Hi, sorry if I misunderstand things. I'm looking at the "OptimizerChainFactory refactor with config processor" change from 1.7.3:
https://github.com/spatie/image-optimizer/commit/e74cdf113dc1f2ac5511415d2094bb69e99cb260

The problem occurs when the config in `OptimizerChainFactory::create($config)` contains a 'quality' entry, which it can, according to the code in `getOptimizers()` below. Previously, such a "generic" quality would be applied to all optimizers that support it.

The call to the new `self::configHasOptimizer($config)` results in config being returned verbatim, including the 'quality' entry:
https://github.com/spatie/image-optimizer/commit/e74cdf113dc1f2ac5511415d2094bb69e99cb260#diff-4f7d0501c10934549ecacfae146962d03a7dcacf1ffab4cadef7984a68409cdfR32

Then when this is instantiated, we get an attempt to call `new quality(...)` which fails with a ClassNotFoundErrror.
https://github.com/spatie/image-optimizer/commit/e74cdf113dc1f2ac5511415d2094bb69e99cb260#diff-4f7d0501c10934549ecacfae146962d03a7dcacf1ffab4cadef7984a68409cdfR21

In short: Since 1.7.3 it is no longer possible to specify `$config['quality']`, doing so results in a crash.
Thanks for reading.


Discussion / hints:

Fix patch:
diff --git a/src/OptimizerChainFactory.php b/src/OptimizerChainFactory.php
index 6315501..fe34bba 100644
--- a/src/OptimizerChainFactory.php
+++ b/src/OptimizerChainFactory.php
@@ -75,14 +75,14 @@ private static function getOptimizers(array $config): array
 
     private static function configHasOptimizer(array $config): bool
     {
-        return (bool)array_diff_key($config, [
-            Jpegoptim::class,
-            Pngquant::class,
-            Optipng::class,
-            Svgo::class,
-            Gifsicle::class,
-            Cwebp::class,
-            Avifenc::class,
+        return (bool)array_intersect_key($config, [
+            Jpegoptim::class => null,
+            Pngquant::class => null,
+            Optipng::class => null,
+            Svgo::class => null,
+            Gifsicle::class => null,
+            Cwebp::class => null,
+            Avifenc::class => null,
         ]);
     }
 }


Test patch:
diff --git a/tests/OptimizerChainFactoryTest.php b/tests/OptimizerChainFactoryTest.php
index 9dade53..d816b33 100644
--- a/tests/OptimizerChainFactoryTest.php
+++ b/tests/OptimizerChainFactoryTest.php
@@ -64,6 +64,53 @@ function (Optimizer $optimizer) {
     );
 });
 
+it('can use quality parameter with default config', function () {
+    $this->optimizerChain = OptimizerChainFactory::create(['quality' => 50])
+            ->useLogger($this->log);
+
+    assertEquals(
+        [
+            new Jpegoptim([
+                '-m50',
+                '--force',
+                '--strip-all',
+                '--all-progressive',
+            ]),
+            new Pngquant([
+                '--quality=50',
+                '--force',
+            ]),
+            new Optipng([
+                '-i0',
+                '-o2',
+                '-quiet',
+            ]),
+            new Svgo([]),
+            new Gifsicle([
+                '-b',
+                '-O3',
+            ]),
+            new Cwebp([
+                '-m 6',
+                '-pass 10',
+                '-mt',
+                '-q 50',
+            ]),
+            new Avifenc([
+                '-a cq-level=32',
+                '-j all',
+                '--min 0',
+                '--max 63',
+                '--minalpha 0',
+                '--maxalpha 63',
+                '-a end-usage=q',
+                '-a tune=ssim',
+            ]),
+        ],
+        $this->optimizerChain->getOptimizers()
+    );
+});
+
 it('can optimize a jpg', function () {
     $tempFilePath = getTempFilePath('image.jpg');