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

tito10047 / progressive-image-bundle / 20614906728

31 Dec 2025 08:04AM UTC coverage: 92.84% (-0.04%) from 92.875%
20614906728

push

github

tito10047
add LiipImagine integration: implement automatic filter generation for responsive breakpoints, add corresponding tests and update documentation

24 of 26 new or added lines in 1 file covered. (92.31%)

389 of 419 relevant lines covered (92.84%)

94.93 hits per line

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

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

3
namespace Tito10047\ProgressiveImageBundle\DependencyInjection;
4

5
use Symfony\Component\Config\FileLocator;
6
use Symfony\Component\DependencyInjection\ContainerBuilder;
7
use Symfony\Component\DependencyInjection\Extension\Extension;
8
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
9
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
10
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
11
use Symfony\Component\DependencyInjection\Reference;
12
use Symfony\Contracts\Cache\CacheInterface;
13
use Tito10047\ProgressiveImageBundle\Resolver\AssetMapperResolver;
14
use Tito10047\ProgressiveImageBundle\Resolver\FileSystemResolver;
15
use Tito10047\ProgressiveImageBundle\Service\MetadataReader;
16
use Tito10047\ProgressiveImageBundle\Service\PreloadCollector;
17
use Tito10047\ProgressiveImageBundle\Twig\Components\Image;
18
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
19

20
class ProgressiveImageExtension extends Extension implements PrependExtensionInterface {
21

22
        public function prepend(ContainerBuilder $builder): void
23
        {
24
                $builder->prependExtensionConfig('framework', [
168✔
25
                        'asset_mapper' => [
168✔
26
                                'paths' => [
168✔
27
                                        __DIR__.'/../../assets' => 'tito10047/progressive-image-bundle',
168✔
28
                                ],
168✔
29
                        ],
168✔
30
                ]);
168✔
31
                $builder->prependExtensionConfig('twig_component', [
168✔
32
                        'defaults' => [
168✔
33
                                'Tito10047\ProgressiveImageBundle\Twig\Components\\' => [
168✔
34
                                        'template_directory' => '@ProgressiveImage/components/',
168✔
35
                                        'name_prefix' => 'pgi',
168✔
36
                                ],
168✔
37
                        ],
168✔
38
                ]);
168✔
39

40
                $configs = $builder->getExtensionConfig($this->getAlias());
168✔
41
                $configs = $this->processConfiguration(new Configuration(), $configs);
168✔
42

43
                if (isset($configs['responsive_strategy']['breakpoints'])) {
168✔
44
                        $breakpoints = $configs['responsive_strategy']['breakpoints'];
42✔
45
                        $liipConfigs = $builder->getExtensionConfig('liip_imagine');
42✔
46

47
                        $newFilterSets = [];
42✔
48
                        foreach ($liipConfigs as $liipConfig) {
42✔
49
                                if (isset($liipConfig['filter_sets'])) {
42✔
50
                                        foreach ($liipConfig['filter_sets'] as $setName => $setConfig) {
42✔
51
                                                foreach ($breakpoints as $breakpointName => $width) {
42✔
52
                                                        $newSetName = $setName . '_' . $breakpointName;
42✔
53
                                                        if (isset($newFilterSets[$newSetName])) {
42✔
NEW
54
                                                                continue;
×
55
                                                        }
56
                                                        $newSetConfig = $setConfig;
42✔
57

58
                                                        if (isset($newSetConfig['filters']['thumbnail']['size'])) {
42✔
59
                                                                [$origWidth, $origHeight] = $newSetConfig['filters']['thumbnail']['size'];
42✔
60
                                                                if ($origWidth > 0 && $origHeight > 0) {
42✔
61
                                                                        $ratio = $origHeight / $origWidth;
42✔
62
                                                                        $newHeight = (int) round($width * $ratio);
42✔
63
                                                                        $newSetConfig['filters']['thumbnail']['size'] = [$width, $newHeight];
42✔
64
                                                                } else {
NEW
65
                                                                        $newSetConfig['filters']['thumbnail']['size'] = [$width, $width];
×
66
                                                                }
67
                                                        }
68

69
                                                        $newFilterSets[$newSetName] = $newSetConfig;
42✔
70
                                                }
71
                                        }
72
                                }
73
                        }
74

75
                        if (!empty($newFilterSets)) {
42✔
76
                                $builder->prependExtensionConfig('liip_imagine', [
42✔
77
                                        'filter_sets' => $newFilterSets,
42✔
78
                                ]);
42✔
79
                        }
80
                }
81
        }
82

83

84

85
        public function load(array $configs, ContainerBuilder $container): void {
86

87
                $configs = $this->processConfiguration(new Configuration(), $configs);
168✔
88

89
                if (!isset($container->getParameter('kernel.bundles')['TwigBundle'])) {
168✔
90
                        throw new \LogicException('The TwigBundle is not registered in your application. Try running "composer require symfony/twig-bundle".');
×
91
                }
92

93
                $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../../config'));
168✔
94
                $loader->load('services.php');
168✔
95

96
                $this->configureResolvers($configs, $container);
168✔
97

98
                $driver = $configs['driver'] ?? 'gd';
168✔
99
                $analyzerId = match ($driver) {
168✔
100
                        "imagick" => "progressive_image.analyzer.imagick",
×
101
                        "gd" => "progressive_image.analyzer.gd",
168✔
102
                        default => $driver,
×
103
                };
168✔
104

105
                $resolver   = $configs['resolver']??'default';
168✔
106
                $maybeService = 'progressive_image.resolver.' . $resolver;
168✔
107
                if ($container->hasDefinition($maybeService)) {
168✔
108
                        $resolverId = $maybeService;
168✔
109
                }else{
110
                        $resolverId = $resolver;
×
111
                }
112
                $loaderId = $configs['loader']??'progressive_image.filesystem.loader';
168✔
113
                $cacheId = $configs['cache']??CacheInterface::class;
168✔
114

115
                $definition = $container->getDefinition(MetadataReader::class);
168✔
116
                $definition->setArgument('$analyzer', new Reference($analyzerId))
168✔
117
                        ->setArgument('$loader', new Reference($loaderId))
168✔
118
                        ->setArgument('$pathResolver', new Reference($resolverId))
168✔
119
                        ->setArgument('$cache', new Reference($cacheId))
168✔
120
                ;
168✔
121

122
                if (isset($configs['ttl'])) {
168✔
123
                        $definition->setArgument('$ttl', $configs['ttl']);
×
124
                }
125

126
                if (isset($configs['fallback_image'])) {
168✔
127
                        $definition->setArgument('$fallbackPath', $configs['fallback_image']);
14✔
128
                }
129

130
                $responsiveConfig = $configs['responsive_strategy'] ?? [];
168✔
131
                $breakpoints = $responsiveConfig['breakpoints'] ?? [];
168✔
132
                $defaultPreset = [
168✔
133
                        'widths' => $responsiveConfig['fallback_widths'] ?? [],
168✔
134
                        'sizes' => $responsiveConfig['fallback_sizes'] ?? '100vw',
168✔
135
                ];
168✔
136
                $presets = $responsiveConfig['presets'] ?? [];
168✔
137
                $generatorId = $responsiveConfig['generator'] ?? null;
168✔
138

139
                $container->register(Image::class, Image::class)
168✔
140
                        ->setArgument(0, new Reference(MetadataReader::class))
168✔
141
                        ->setArgument(1, array_map(fn($id) => new Reference($id), $configs['path_decorators'] ?? []))
168✔
142
                        ->setArgument(2, new Reference(PreloadCollector::class))
168✔
143
                        ->setArgument(3, $generatorId ? new Reference($generatorId) : null)
168✔
144
                        ->setArgument(4, $breakpoints)
168✔
145
                        ->setArgument(5, $defaultPreset)
168✔
146
                        ->setArgument(6, $presets)
168✔
147
                        ->addTag('twig.component')
168✔
148
                        ->setPublic(true);
168✔
149

150

151
        }
152

153

154
        private function configureResolvers(array $config, ContainerBuilder $container): void
155
        {
156
                $resolvers = $config['resolvers'] ?? [];
168✔
157
                foreach ($resolvers as $name => $resolverConfig) {
168✔
158
                        $id = 'progressive_image.resolver.' . $name;
70✔
159

160
                        if ('filesystem' === $resolverConfig['type']) {
70✔
161
                                $container->register($id, FileSystemResolver::class)
70✔
162
                                        ->setArgument('$roots', $resolverConfig['roots'] ?? ["%kernel.project_dir%/public"])
70✔
163
                                        ->setArgument('$allowUnresolvable', $resolverConfig['allowUnresolvable'] ?? false);
70✔
164
                        } elseif ('asset_mapper' === $resolverConfig['type']) {
×
165
                                $container->register($id, AssetMapperResolver::class);
×
166
                        }
167
                        // Chain resolver logic can be added here if needed
168
                }
169

170
                if (isset($config['resolver']) && !isset($resolvers[$config['resolver']])) {
168✔
171
                        // If a default resolver type is used but not defined in resolvers array
172
                        if (in_array($config['resolver'], ['filesystem', 'asset_mapper'])) {
×
173
                                // handle basic types if they are used as string directly
174
                        }
175
                }
176

177
                // Register a default alias if possible
178
                if (isset($config['resolver']) && isset($resolvers[$config['resolver']])) {
168✔
179
                        $container->setAlias('progressive_image.resolver.default', 'progressive_image.resolver.' . $config['resolver']);
70✔
180
                } elseif (!empty($resolvers)) {
98✔
181
                        $firstResolver = array_key_first($resolvers);
×
182
                        $container->setAlias('progressive_image.resolver.default', 'progressive_image.resolver.' . $firstResolver);
×
183
                } else {
184
                        // Fallback if no resolvers defined, register a basic one to avoid ServiceNotFoundException
185
                        $container->register('progressive_image.resolver.default', FileSystemResolver::class)
98✔
186
                                ->setArgument('$roots', ['%kernel.project_dir%/public'])
98✔
187
                                ->setArgument('$allowUnresolvable', true);
98✔
188
                }
189
        }
190
}
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

© 2025 Coveralls, Inc