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

tito10047 / progressive-image-bundle / 21089614089

17 Jan 2026 06:02AM UTC coverage: 90.997% (+0.6%) from 90.395%
21089614089

Pull #3

github

web-flow
Merge c4e3ccba0 into 74af92668
Pull Request #3: add modifiers capability

49 of 50 new or added lines in 10 files covered. (98.0%)

13 existing lines in 2 files now uncovered.

849 of 933 relevant lines covered (91.0%)

291.63 hits per line

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

83.83
/src/DependencyInjection/ProgressiveImageExtension.php
1
<?php
2

3
/*
4
 * This file is part of the Progressive Image Bundle.
5
 *
6
 * (c) Jozef Môstka <https://github.com/tito10047/progressive-image-bundle>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
namespace Tito10047\ProgressiveImageBundle\DependencyInjection;
13

14
use Liip\ImagineBundle\LiipImagineBundle;
15
use Symfony\Component\Config\FileLocator;
16
use Symfony\Component\DependencyInjection\ContainerBuilder;
17
use Symfony\Component\DependencyInjection\Extension\Extension;
18
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
19
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
20
use Symfony\Component\DependencyInjection\Parameter;
21
use Symfony\Component\DependencyInjection\Reference;
22
use Tito10047\ProgressiveImageBundle\Command\GenerateCustomCssCommand;
23
use Tito10047\ProgressiveImageBundle\Event\TransparentImageCacheSubscriber;
24
use Tito10047\ProgressiveImageBundle\Modifier\BaseFilterModifier;
25
use Tito10047\ProgressiveImageBundle\Modifier\CoreFilterModifier;
26
use Tito10047\ProgressiveImageBundle\Modifier\FilterManager;
27
use Tito10047\ProgressiveImageBundle\Modifier\FilterModifierInterface;
28
use Tito10047\ProgressiveImageBundle\Modifier\ModifierInterface;
29
use Tito10047\ProgressiveImageBundle\Modifier\ModifierProvider;
30
use Tito10047\ProgressiveImageBundle\Resolver\AssetMapperResolver;
31
use Tito10047\ProgressiveImageBundle\Resolver\ChainResolver;
32
use Tito10047\ProgressiveImageBundle\Resolver\FileSystemResolver;
33
use Tito10047\ProgressiveImageBundle\Service\LiipImagineRuntimeConfigGenerator;
34
use Tito10047\ProgressiveImageBundle\Service\LiipImagineRuntimeConfigGeneratorInterface;
35
use Tito10047\ProgressiveImageBundle\Service\MetadataReader;
36
use Tito10047\ProgressiveImageBundle\Service\PreloadCollector;
37
use Tito10047\ProgressiveImageBundle\Service\ResponsiveAttributeGenerator;
38
use Tito10047\ProgressiveImageBundle\Twig\Components\Image;
39
use Tito10047\ProgressiveImageBundle\Twig\TransparentCacheExtension;
40
use Tito10047\ProgressiveImageBundle\UrlGenerator\LiipImagineResponsiveImageUrlGenerator;
41
use Tito10047\ProgressiveImageBundle\UrlGenerator\ResponsiveImageUrlGeneratorInterface;
42

43
final class ProgressiveImageExtension extends Extension implements PrependExtensionInterface
44
{
45
    public function getAlias(): string
46
    {
47
        return 'progressive_image';
602✔
48
    }
49

50
    public function prepend(ContainerBuilder $builder): void
51
    {
52
        $builder->prependExtensionConfig('framework', [
602✔
53
            'asset_mapper' => [
602✔
54
                'paths' => [
602✔
55
                    __DIR__.'/../../assets' => 'tito10047/progressive-image-bundle',
602✔
56
                ],
602✔
57
            ],
602✔
58
        ]);
602✔
59
        $builder->prependExtensionConfig('twig_component', [
602✔
60
            'defaults' => [
602✔
61
                'Tito10047\ProgressiveImageBundle\Twig\Components\\' => [
602✔
62
                    'template_directory' => '@ProgressiveImage/components/',
602✔
63
                    'name_prefix' => 'pgi',
602✔
64
                ],
602✔
65
            ],
602✔
66
        ]);
602✔
67

68
        $configs = $builder->getExtensionConfig($this->getAlias());
602✔
69
        $configs = $this->processConfiguration(new Configuration(), $configs);
602✔
70

71
        if (isset($configs['responsive_strategy']['breakpoints'])) {
602✔
UNCOV
72
            $breakpoints = $configs['responsive_strategy']['breakpoints'];
×
73
            $liipConfigs = $builder->getExtensionConfig('liip_imagine');
×
74

75
            $newFilterSets = [];
×
76
            foreach ($liipConfigs as $liipConfig) {
×
77
                if (isset($liipConfig['filter_sets'])) {
×
78
                    foreach ($liipConfig['filter_sets'] as $setName => $setConfig) {
×
79
                        foreach ($breakpoints as $breakpointName => $width) {
×
80
                            $newSetName = $setName.'_'.$breakpointName;
×
UNCOV
81
                            if (isset($newFilterSets[$newSetName])) {
×
82
                                continue;
×
83
                            }
84
                            $newSetConfig = $setConfig;
×
85

86
                            if (isset($newSetConfig['filters']['thumbnail']['size'])) {
×
87
                                [$origWidth, $origHeight] = $newSetConfig['filters']['thumbnail']['size'];
×
88
                                if ($origWidth > 0 && $origHeight > 0) {
×
89
                                    $ratio = $origHeight / $origWidth;
×
UNCOV
90
                                    $newHeight = (int) round($width * $ratio);
×
91
                                    $newSetConfig['filters']['thumbnail']['size'] = [$width, $newHeight];
×
92
                                } else {
UNCOV
93
                                    $newSetConfig['filters']['thumbnail']['size'] = [$width, $width];
×
94
                                }
95
                            }
96

UNCOV
97
                            $newFilterSets[$newSetName] = $newSetConfig;
×
98
                        }
99
                    }
100
                }
101
            }
102

103
            if (!empty($newFilterSets)) {
×
104
                $builder->prependExtensionConfig('liip_imagine', [
×
UNCOV
105
                    'filter_sets' => $newFilterSets,
×
UNCOV
106
                ]);
×
107
            }
108
        }
109
    }
110

111
    public function load(array $configs, ContainerBuilder $container): void
112
    {
113
        $configs = $this->processConfiguration(new Configuration(), $configs);
602✔
114

115
        if (!isset($container->getParameter('kernel.bundles')['TwigBundle'])) {
602✔
UNCOV
116
            throw new \LogicException('The TwigBundle is not registered in your application. Try running "composer require symfony/twig-bundle".');
×
117
        }
118

119
        $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../../config'));
602✔
120
        $loader->load('services.php');
602✔
121

122
        $this->configureResolvers($configs, $container);
602✔
123

124
        $driver = $configs['driver'] ?? 'gd';
602✔
125
        $analyzerId = match ($driver) {
602✔
126
            'imagick' => 'progressive_image.analyzer.imagick',
×
127
            'gd' => 'progressive_image.analyzer.gd',
602✔
UNCOV
128
            default => $driver,
×
129
        };
602✔
130

131
        $loaderId = $configs['loader'] ?? 'progressive_image.filesystem.loader';
602✔
132
        $cacheId = $configs['cache'] ?? 'cache.app';
602✔
133
        $imageCacheServiceId = $configs['image_cache_service'] ?? 'cache.app';
602✔
134
        $imageCacheEnabled = $configs['image_cache_enabled'] ?? false;
602✔
135
        $ttl = $configs['ttl'] ?? null;
602✔
136
                $retinaConfig = $configs['retina'] ?? ['enabled' => true, 'multipliers' => [1, 2]];
602✔
137
                $retina = $retinaConfig['enabled'] ?? true;
602✔
138
                $retinaMultipliers = $retinaConfig['multipliers'] ?? [1, 2];
602✔
139

140
        if (!$imageCacheEnabled) {
602✔
141
            $imageCacheServiceReference = null;
14✔
142
        } else {
143
            $imageCacheServiceReference = new Reference('progressive_image.image_cache_service');
588✔
144
        }
145

146
        $definition = $container->getDefinition(MetadataReader::class);
602✔
147
        $definition->setArgument('$analyzer', new Reference($analyzerId))
602✔
148
            ->setArgument('$loader', new Reference($loaderId))
602✔
149
                        ->setArgument('$pathResolver', new Reference('progressive_image.resolver.default'))
602✔
150
            ->setArgument('$cache', new Reference($cacheId))
602✔
151
            ->setArgument('$ttl', $configs['ttl'] ?? null)
602✔
152
            ->setArgument('$fallbackPath', $configs['fallback_image'] ?? null)
602✔
153
        ;
602✔
154
        $container->setParameter('progressive_image.image_cache_enabled', $imageCacheEnabled);
602✔
155
        $container->setParameter('progressive_image.ttl', $ttl);
602✔
156
                $container->setParameter('progressive_image.image_configs', $configs['image_configs'] ?? []);
602✔
157
        $container->setAlias('progressive_image.image_cache_service', $imageCacheServiceId);
602✔
158

159
        $container->register(TransparentCacheExtension::class)
602✔
160
            ->setArgument('$ttl', new Parameter('progressive_image.ttl'))
602✔
161
            ->setArgument('$cache', $imageCacheServiceReference)
602✔
162
            ->addTag('twig.extension')
602✔
163
        ;
602✔
164

165
        $container->register(TransparentImageCacheSubscriber::class)
602✔
166
            ->setArgument('$enabled', new Parameter('progressive_image.image_cache_enabled'))
602✔
167
            ->setArgument('$cache', $imageCacheServiceReference)
602✔
168
            ->setArgument('$ttl', new Parameter('progressive_image.ttl'))
602✔
169
            ->addTag('kernel.event_subscriber')
602✔
170
        ;
602✔
171

172
                $container->registerForAutoconfiguration(ModifierInterface::class)
602✔
173
                        ->addTag('progressive_image.modifier');
602✔
174

175
                $container->registerForAutoconfiguration(FilterModifierInterface::class)
602✔
176
                        ->addTag('pgi.filter_modifier');
602✔
177

178
                $container->register(CoreFilterModifier::class)
602✔
179
                        ->addTag('pgi.filter_modifier', ['priority' => -100]);
602✔
180

181
                $container->register(FilterManager::class)
602✔
182
                        ->setArgument('$modifiers', new \Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument('pgi.filter_modifier'));
602✔
183

184
                $container->register(BaseFilterModifier::class)
602✔
185
                        ->setArgument('$filterManager', new Reference(FilterManager::class))
602✔
186
                        ->addTag('progressive_image.modifier', ['priority' => -100]);
602✔
187

188
                $container->register(ModifierProvider::class)
602✔
189
                        ->setArgument('$modifiers', new \Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument('progressive_image.modifier'));
602✔
190

191
        if (class_exists(LiipImagineBundle::class)) {
602✔
192
                        $container->register(LiipImagineRuntimeConfigGenerator::class)
602✔
193
                                ->setArgument('$filterConfiguration', new Reference('liip_imagine.filter.configuration'))
602✔
194
                                ->setArgument('$imageConfigs', new Parameter('progressive_image.image_configs'));
602✔
195

196
            $container->register(LiipImagineResponsiveImageUrlGenerator::class)
602✔
197
                ->setArgument('$cacheManager', new Reference('liip_imagine.cache.manager'))
602✔
198
                ->setArgument('$router', new Reference('router'))
602✔
199
                ->setArgument('$uriSigner', new Reference('uri_signer'))
602✔
200
                ->setArgument('$runtimeConfigGenerator', new Reference(LiipImagineRuntimeConfigGenerator::class))
602✔
201
                ->setArgument('$filterConfiguration', new Reference('liip_imagine.filter.configuration'))
602✔
202
                                ->setArgument('$requestStack', new Reference('request_stack'))
602✔
203
                ->setArgument('$cache', $imageCacheServiceReference)
602✔
204
                                ->setArgument('$webpGenerate', new Parameter('liip_imagine.webp.generate'))
602✔
205
                ->setPublic(true);
602✔
206

207
                        $container->setAlias(ResponsiveImageUrlGeneratorInterface::class, LiipImagineResponsiveImageUrlGenerator::class)->setPublic(true);
602✔
208
                        $container->setAlias(LiipImagineRuntimeConfigGeneratorInterface::class, LiipImagineRuntimeConfigGenerator::class)->setPublic(true);
602✔
209
        }
210
        $responsiveConfig = $configs['responsive_strategy'] ?? [];
602✔
211
        $generatorId = $responsiveConfig['generator'] ?? null;
602✔
212

213
        if ($generatorId || class_exists(LiipImagineBundle::class)) {
602✔
214
            $container->register(ResponsiveAttributeGenerator::class, ResponsiveAttributeGenerator::class)
602✔
215
                ->setArgument('$gridConfig', $responsiveConfig['grid'] ?? [])
602✔
216
                ->setArgument('$ratioConfig', $responsiveConfig['ratios'] ?? [])
602✔
217
                                ->setArgument('$retinaMultipliers', $retinaMultipliers)
602✔
218
                ->setArgument('$preloadCollector', new Reference(PreloadCollector::class))
602✔
219
                ->setArgument('$urlGenerator', $generatorId ? new Reference($generatorId) : new Reference(ResponsiveImageUrlGeneratorInterface::class))
602✔
220
                                ->setArgument('$modifierProvider', new Reference(ModifierProvider::class))
602✔
221
            ;
602✔
222
        }
223

224
                $container->register(GenerateCustomCssCommand::class)
602✔
225
                        ->setArgument('$gridConfig', $responsiveConfig['grid'] ?? [])
602✔
226
                        ->setArgument('$projectDir', new Parameter('kernel.project_dir'))
602✔
227
                        ->addTag('console.command');
602✔
228

229
        $container->register(Image::class, Image::class)
602✔
230
            ->setArgument('$analyzer', new Reference(MetadataReader::class))
602✔
231
            ->setArgument('$pathDecorator', array_map(fn ($id) => new Reference($id), $configs['path_decorators'] ?? []))
602✔
232
            ->setArgument('$responsiveAttributeGenerator', $generatorId || class_exists(LiipImagineBundle::class) ? new Reference(ResponsiveAttributeGenerator::class) : null)
602✔
233
            ->setArgument('$preloadCollector', new Reference(PreloadCollector::class))
602✔
234
                        ->setArgument('$framework', $configs['responsive_strategy']['grid']['framework'] ?? 'custom')
602✔
235
                        ->setArgument('$defaultRetina', $retina)
602✔
236
            ->setShared(false)
602✔
237
            ->addTag('twig.component')
602✔
238
            ->setPublic(true);
602✔
239
    }
240

241
    /**
242
     * @param array<string, mixed> $config
243
     */
244
    private function configureResolvers(array $config, ContainerBuilder $container): void
245
    {
246
        $resolvers = $config['resolvers'] ?? [];
602✔
247
        foreach ($resolvers as $name => $resolverConfig) {
602✔
248
            $id = 'progressive_image.resolver.'.$name;
378✔
249

250
            if ('filesystem' === $resolverConfig['type']) {
378✔
251
                $container->register($id, FileSystemResolver::class)
378✔
252
                    ->setArgument('$roots', $resolverConfig['roots'] ?? ['%kernel.project_dir%/public'])
378✔
253
                                        ->setArgument('$allowUnresolvable', $resolverConfig['allowUnresolvable'] ?? true);
378✔
254
            } elseif ('asset_mapper' === $resolverConfig['type']) {
14✔
255
                                $container->register($id, AssetMapperResolver::class)
14✔
256
                                        ->setArgument('$assetMapper', new Reference('asset_mapper'));
14✔
257
                        } elseif ('chain' === $resolverConfig['type']) {
14✔
258
                                $childResolvers = array_map(fn($name) => new Reference('progressive_image.resolver.' . $name), $resolverConfig['resolvers'] ?? []);
14✔
259
                                $container->register($id, ChainResolver::class)
14✔
260
                                        ->setArgument('$resolvers', $childResolvers);
14✔
261
            }
262
        }
263

264
                $resolver = $config['resolver'] ?? 'default';
602✔
265

266
                if (isset($resolvers[$resolver])) {
602✔
267
                        $container->setAlias('progressive_image.resolver.default', 'progressive_image.resolver.' . $resolver);
364✔
268
                } elseif (in_array($resolver, ['filesystem', 'asset_mapper'])) {
238✔
NEW
269
                        $container->setAlias('progressive_image.resolver.default', 'progressive_image.resolver.' . $resolver);
×
270
                } elseif (!empty($resolvers) && 'default' === $resolver) {
238✔
271
            $firstResolver = array_key_first($resolvers);
14✔
272
            $container->setAlias('progressive_image.resolver.default', 'progressive_image.resolver.'.$firstResolver);
14✔
273
        } else {
274
            $container->register('progressive_image.resolver.default', FileSystemResolver::class)
224✔
275
                ->setArgument('$roots', ['%kernel.project_dir%/public'])
224✔
276
                ->setArgument('$allowUnresolvable', true);
224✔
277
        }
278
    }
279
}
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