• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

liip / LiipImagineBundle / 22199968624

19 Feb 2026 09:02PM UTC coverage: 81.111% (+1.0%) from 80.126%
22199968624

Pull #1651

github

web-flow
Merge 07e5821bd into 69d2df3c6
Pull Request #1651: Add support for alternatiwe image formats like webp and avif

233 of 250 new or added lines in 8 files covered. (93.2%)

7 existing lines in 2 files now uncovered.

2379 of 2933 relevant lines covered (81.11%)

99.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.62
/DependencyInjection/Configuration.php
1
<?php
2

3
/*
4
 * This file is part of the `liip/LiipImagineBundle` project.
5
 *
6
 * (c) https://github.com/liip/LiipImagineBundle/graphs/contributors
7
 *
8
 * For the full copyright and license information, please view the LICENSE.md
9
 * file that was distributed with this source code.
10
 */
11

12
namespace Liip\ImagineBundle\DependencyInjection;
13

14
use Liip\ImagineBundle\Config\Controller\ControllerConfig;
15
use Liip\ImagineBundle\Controller\ImagineController;
16
use Liip\ImagineBundle\DependencyInjection\Factory\FactoryInterface;
17
use Liip\ImagineBundle\DependencyInjection\Factory\Loader\LoaderFactoryInterface;
18
use Liip\ImagineBundle\DependencyInjection\Factory\Resolver\ResolverFactoryInterface;
19
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
20
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
21
use Symfony\Component\Config\Definition\ConfigurationInterface;
22
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
23

24
class Configuration implements ConfigurationInterface
25
{
26
    /**
27
     * @var ResolverFactoryInterface[]
28
     */
29
    protected $resolversFactories;
30

31
    /**
32
     * @var LoaderFactoryInterface[]
33
     */
34
    protected $loadersFactories;
35

36
    /**
37
     * @param ResolverFactoryInterface[] $resolversFactories
38
     * @param LoaderFactoryInterface[]   $loadersFactories
39
     */
40
    public function __construct(array $resolversFactories, array $loadersFactories)
41
    {
42
        $this->resolversFactories = $resolversFactories;
385✔
43
        $this->loadersFactories = $loadersFactories;
385✔
44
    }
45

46
    public function getConfigTreeBuilder(): TreeBuilder
47
    {
48
        $treeBuilder = new TreeBuilder('liip_imagine');
374✔
49
        $rootNode = $treeBuilder->getRootNode();
374✔
50

51
        $rootNode
374✔
52
            ->beforeNormalization()
374✔
53
                ->always(function ($v) {
374✔
54
                    if (\is_array($v) && \array_key_exists('webp', $v)) {
352✔
55
                        if (!\array_key_exists('alternative_formats', $v)) {
33✔
56
                            $v['alternative_formats'] = [];
33✔
57
                        }
58
                        if (!\array_key_exists('webp', $v['alternative_formats'])) {
33✔
59
                            $v['alternative_formats']['webp'] = $v['webp'];
33✔
60
                        }
61
                        unset($v['webp']);
33✔
62
                    }
63

64
                    if (\is_array($v) && \array_key_exists('alternative_formats', $v)) {
352✔
65
                        $defaults = [
66✔
66
                            'webp' => ['image/webp'],
66✔
67
                            'avif' => ['image/avif'],
66✔
68
                        ];
66✔
69
                        foreach ($v['alternative_formats'] as $format => &$config) {
66✔
70
                            if (isset($defaults[$format]) && (empty($config['mime_types']) || !\is_array($config['mime_types']))) {
66✔
71
                                $config['mime_types'] = $defaults[$format];
55✔
72
                            }
73
                        }
74
                    }
75

76
                    return $v;
352✔
77
                })
374✔
78
            ->end();
374✔
79

80
        $resolversPrototypeNode = $rootNode
374✔
81
            ->children()
374✔
82
                ->arrayNode('resolvers')
374✔
83
                    ->useAttributeAsKey('name')
374✔
84
                    ->prototype('array')
374✔
85
                        ->performNoDeepMerging();
374✔
86
        $this->addResolversSections($resolversPrototypeNode);
374✔
87

88
        $loadersPrototypeNode = $rootNode
374✔
89
            ->children()
374✔
90
                ->arrayNode('loaders')
374✔
91
                    ->useAttributeAsKey('name')
374✔
92
                    ->prototype('array');
374✔
93
        $this->addLoadersSections($loadersPrototypeNode);
374✔
94

95
        $rootNode
374✔
96
            ->beforeNormalization()
374✔
97
                ->ifTrue(function ($v) {
374✔
98
                    return
352✔
99
                        empty($v['loaders'])
352✔
100
                        || empty($v['loaders']['default'])
352✔
101
                        || empty($v['resolvers'])
352✔
102
                        || empty($v['resolvers']['default']);
352✔
103
                })
374✔
104
                ->then(function ($v) {
374✔
105
                    if (empty($v['loaders'])) {
352✔
106
                        $v['loaders'] = [];
308✔
107
                    }
108

109
                    if (false === \is_array($v['loaders'])) {
352✔
110
                        throw new \LogicException('Loaders has to be array');
11✔
111
                    }
112

113
                    if (false === \array_key_exists('default', $v['loaders'])) {
341✔
114
                        $v['loaders']['default'] = ['filesystem' => null];
330✔
115
                    }
116

117
                    if (empty($v['resolvers'])) {
341✔
118
                        $v['resolvers'] = [];
297✔
119
                    }
120

121
                    if (false === \is_array($v['resolvers'])) {
341✔
122
                        throw new \LogicException('Resolvers has to be array');
11✔
123
                    }
124

125
                    if (false === \array_key_exists('default', $v['resolvers'])) {
330✔
126
                        $v['resolvers']['default'] = ['web_path' => null];
319✔
127
                    }
128

129
                    return $v;
330✔
130
                })
374✔
131
            ->end();
374✔
132

133
        $rootNode
374✔
134
            ->fixXmlConfig('filter_set', 'filter_sets')
374✔
135
            ->children()
374✔
136
                ->scalarNode('driver')->defaultValue('gd')
374✔
137
                    ->validate()
374✔
138
                        ->ifTrue(function ($v) {
374✔
139
                            return !\in_array($v, ['gd', 'imagick', 'gmagick', 'vips'], true);
×
140
                        })
374✔
141
                        ->thenInvalid('Invalid imagine driver specified: %s')
374✔
142
                    ->end()
374✔
143
                ->end()
374✔
144
                ->scalarNode('cache')->defaultValue('default')->end()
374✔
145
                ->scalarNode('cache_base_path')->defaultValue('')->end()
374✔
146
                ->scalarNode('data_loader')->defaultValue('default')->end()
374✔
147
                ->scalarNode('default_image')->defaultNull()->end()
374✔
148
                ->arrayNode('default_filter_set_settings')
374✔
149
                    ->addDefaultsIfNotSet()
374✔
150
                    ->children()
374✔
151
                        ->scalarNode('quality')->defaultValue(100)->end()
374✔
152
                        ->scalarNode('jpeg_quality')->defaultNull()->end()
374✔
153
                        ->scalarNode('png_compression_level')->defaultNull()->end()
374✔
154
                        ->scalarNode('png_compression_filter')->defaultNull()->end()
374✔
155
                        ->scalarNode('format')->defaultNull()->end()
374✔
156
                        ->booleanNode('animated')->defaultFalse()->end()
374✔
157
                        ->scalarNode('cache')->defaultNull()->end()
374✔
158
                        ->scalarNode('data_loader')->defaultNull()->end()
374✔
159
                        ->scalarNode('default_image')->defaultNull()->end()
374✔
160
                        ->arrayNode('filters')
374✔
161
                            ->useAttributeAsKey('name')
374✔
162
                            ->prototype('array')
374✔
163
                                ->useAttributeAsKey('name')
374✔
164
                                ->ignoreExtraKeys(false)
374✔
165
                                ->prototype('variable')->end()
374✔
166
                            ->end()
374✔
167
                        ->end()
374✔
168
                        ->arrayNode('post_processors')
374✔
169
                            ->defaultValue([])
374✔
170
                            ->useAttributeAsKey('name')
374✔
171
                            ->prototype('array')
374✔
172
                                ->useAttributeAsKey('name')
374✔
173
                                ->ignoreExtraKeys(false)
374✔
174
                                ->prototype('variable')->end()
374✔
175
                            ->end()
374✔
176
                        ->end()
374✔
177
                    ->end()
374✔
178
                ->end()
374✔
179
                ->arrayNode('controller')
374✔
180
                    ->addDefaultsIfNotSet()
374✔
181
                    ->children()
374✔
182
                        ->scalarNode('filter_action')->defaultValue(\sprintf('%s::filterAction', ImagineController::class))->end()
374✔
183
                        ->scalarNode('filter_runtime_action')->defaultValue(\sprintf('%s::filterRuntimeAction', ImagineController::class))->end()
374✔
184
                        ->integerNode('redirect_response_code')->defaultValue(302)
374✔
185
                            ->validate()
374✔
186
                                ->ifTrue(function ($redirectResponseCode) {
374✔
187
                                    return !\in_array($redirectResponseCode, ControllerConfig::REDIRECT_RESPONSE_CODES, true);
×
188
                                })
374✔
189
                                ->thenInvalid('Invalid redirect response code "%s" (must be 201, 301, 302, 303, 307, or 308).')
374✔
190
                            ->end()
374✔
191
                        ->end()
374✔
192
                    ->end()
374✔
193
                ->end()
374✔
194
                ->arrayNode('filter_sets')
374✔
195
                    ->useAttributeAsKey('name')
374✔
196
                    ->prototype('array')
374✔
197
                        ->fixXmlConfig('filter', 'filters')
374✔
198
                        ->children()
374✔
199
                            ->scalarNode('quality')->end()
374✔
200
                            ->scalarNode('jpeg_quality')->end()
374✔
201
                            ->scalarNode('png_compression_level')->end()
374✔
202
                            ->scalarNode('png_compression_filter')->end()
374✔
203
                            ->scalarNode('format')->end()
374✔
204
                            ->booleanNode('animated')->end()
374✔
205
                            ->scalarNode('cache')->end()
374✔
206
                            ->scalarNode('data_loader')->end()
374✔
207
                            ->scalarNode('default_image')->end()
374✔
208
                            ->arrayNode('filters')
374✔
209
                                ->useAttributeAsKey('name')
374✔
210
                                ->prototype('array')
374✔
211
                                    ->useAttributeAsKey('name')
374✔
212
                                    ->ignoreExtraKeys(false)
374✔
213
                                    ->prototype('variable')->end()
374✔
214
                                ->end()
374✔
215
                            ->end()
374✔
216
                            ->arrayNode('post_processors')
374✔
217
                                ->defaultValue([])
374✔
218
                                ->useAttributeAsKey('name')
374✔
219
                                ->prototype('array')
374✔
220
                                    ->useAttributeAsKey('name')
374✔
221
                                    ->ignoreExtraKeys(false)
374✔
222
                                    ->prototype('variable')->end()
374✔
223
                                ->end()
374✔
224
                        ->end()
374✔
225
                    ->end()
374✔
226
                ->end()
374✔
227
                ->end()
374✔
228
                ->arrayNode('alternative_formats')
374✔
229
                    ->useAttributeAsKey('format')
374✔
230
                    ->prototype('array')
374✔
231
                        ->children()
374✔
232
                            ->booleanNode('generate')->defaultFalse()->end()
374✔
233
                            ->integerNode('quality')->defaultValue(100)->end()
374✔
234
                            ->scalarNode('cache')->defaultNull()->end()
374✔
235
                            ->scalarNode('data_loader')->defaultNull()->end()
374✔
236
                            ->arrayNode('post_processors')
374✔
237
                                ->defaultValue([])
374✔
238
                                ->useAttributeAsKey('name')
374✔
239
                                ->prototype('variable')->end()
374✔
240
                            ->end()
374✔
241
                            ->arrayNode('mime_types')
374✔
242
                                ->prototype('scalar')->end()
374✔
243
                            ->end()
374✔
244
                            ->integerNode('priority')->defaultNull()->end()
374✔
245
                        ->end()
374✔
246
                    ->end()
374✔
247
                ->end()
374✔
248
        ->arrayNode('twig')
374✔
249
                ->addDefaultsIfNotSet()
374✔
250
                ->children()
374✔
251
                    ->enumNode('mode')
374✔
252
                        ->defaultValue('legacy')
374✔
253
                        ->info('Twig mode: none/lazy/legacy (default)')
374✔
254
                        ->values(['none', 'lazy', 'legacy'])
374✔
255
                        ->validate()
374✔
256
                            ->ifTrue(function ($v) {
374✔
UNCOV
257
                                return 'legacy' === $v;
×
258
                            })
374✔
259
                            ->then(function ($v) {
374✔
UNCOV
260
                                @trigger_error('Twig "legacy" mode has been deprecated and will be removed in 3.0. Use "none" or "lazy".', E_USER_DEPRECATED);
×
261

UNCOV
262
                                return $v;
×
263
                            })
374✔
264
                        ->end()
374✔
265
                    ->end()
374✔
266
                    ->scalarNode('assets_version')->defaultNull()->end()
374✔
267
                ->end()
374✔
268
            ->end()
374✔
269
            ->booleanNode('enqueue')
374✔
270
                ->defaultFalse()
374✔
271
                ->info('Enables integration with enqueue if set true. Allows resolve image caches in background by sending messages to MQ.')
374✔
272
            ->end()
374✔
273
            ->arrayNode('messenger')
374✔
274
                ->canBeEnabled()
374✔
275
                ->info('Enables integration with symfony/messenger if set true. Warmup image caches in background by sending messages to MQ.')
374✔
276
            ->end()
374✔
277
            ->booleanNode('templating')
374✔
278
                ->defaultTrue()
374✔
279
                ->info('Enables integration with symfony/templating component')
374✔
280
                ->validate()
374✔
281
                    ->ifTrue()
374✔
282
                    ->then(function ($v) {
374✔
283
                        @trigger_error('Symfony templating integration has been deprecated since LiipImagineBundle 2.2 and will be removed in 3.0. Use Twig and use "false" as "liip_imagine.templating" value instead.', E_USER_DEPRECATED);
11✔
284

285
                        return $v;
11✔
286
                    })
374✔
287
                ->end()
374✔
288
            ->end()
374✔
289
        ->end();
374✔
290

291
        $rootNode
374✔
292
            ->children()
374✔
293
                ->arrayNode('webp')
374✔
294
                    ->setDeprecated('liip/imagine-bundle', '2.x', 'The "webp" option is deprecated and will be removed in 3.0. Use "alternative_formats" instead.')
374✔
295
                    ->children()
374✔
296
                        ->booleanNode('generate')->defaultFalse()->end()
374✔
297
                        ->integerNode('quality')->defaultValue(100)->end()
374✔
298
                        ->scalarNode('cache')->defaultNull()->end()
374✔
299
                        ->scalarNode('data_loader')->defaultNull()->end()
374✔
300
                        ->arrayNode('post_processors')
374✔
301
                            ->defaultValue([])
374✔
302
                            ->useAttributeAsKey('name')
374✔
303
                            ->prototype('array')
374✔
304
                                ->useAttributeAsKey('name')
374✔
305
                                ->ignoreExtraKeys(false)
374✔
306
                                ->prototype('variable')->end()
374✔
307
                            ->end()
374✔
308
                    ->end()
374✔
309
                ->end()
374✔
310
            ->end();
374✔
311

312
        return $treeBuilder;
374✔
313
    }
314

315
    private function addResolversSections(ArrayNodeDefinition $resolversPrototypeNode)
316
    {
317
        $this->addConfigurationSections($this->resolversFactories, $resolversPrototypeNode, 'resolver');
374✔
318
    }
319

320
    private function addLoadersSections(ArrayNodeDefinition $resolversPrototypeNode)
321
    {
322
        $this->addConfigurationSections($this->loadersFactories, $resolversPrototypeNode, 'loader');
374✔
323
    }
324

325
    /**
326
     * @param FactoryInterface[] $factories
327
     */
328
    private function addConfigurationSections(array $factories, ArrayNodeDefinition $definition, $type)
329
    {
330
        foreach ($factories as $f) {
374✔
331
            $f->addConfiguration($definition->children()->arrayNode($f->getName()));
352✔
332
        }
333

334
        $definition->end()
374✔
335
            ->validate()
374✔
336
            ->ifTrue(function ($array) use ($type) {
374✔
337
                foreach ($array as $name => $element) {
319✔
338
                    if (!$element) {
319✔
UNCOV
339
                        throw new InvalidConfigurationException(ucfirst($type).' "'.$name.'" must have a factory configured');
×
340
                    }
341
                }
342

343
                return false;
319✔
344
            })
374✔
345
            ->thenInvalid('Each '.$type.' must have a factory configured')
374✔
346
            ->end();
374✔
347
    }
348
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc