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

tito10047 / progressive-image-bundle / 21092918970

17 Jan 2026 10:35AM UTC coverage: 90.436% (-0.5%) from 90.889%
21092918970

Pull #4

github

web-flow
Merge 5e68d9ba1 into 0bfbae60a
Pull Request #4: Switch `ResponsiveAttributeGenerator::generate` to return `Responsive…

268 of 332 new or added lines in 19 files covered. (80.72%)

5 existing lines in 2 files now uncovered.

851 of 941 relevant lines covered (90.44%)

318.39 hits per line

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

82.05
/src/Twig/Components/Image.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\Twig\Components;
13

14
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
15
use Symfony\UX\TwigComponent\Attribute\PostMount;
16
use Tito10047\ProgressiveImageBundle\Decorators\PathDecoratorInterface;
17
use Tito10047\ProgressiveImageBundle\DTO\BreakpointAssignment;
18
use Tito10047\ProgressiveImageBundle\DTO\ImageMetadata;
19
use Tito10047\ProgressiveImageBundle\DTO\ResponsiveAttributesInterface;
20
use Tito10047\ProgressiveImageBundle\Exception\PathResolutionException;
21
use Tito10047\ProgressiveImageBundle\ProgressiveImageBundle;
22
use Tito10047\ProgressiveImageBundle\Service\MetadataReaderInterface;
23
use Tito10047\ProgressiveImageBundle\Service\PreloadCollector;
24
use Tito10047\ProgressiveImageBundle\Service\ResponsiveAttributeGenerator;
25

26
#[AsTwigComponent]
27
final class Image
28
{
29
    public ?string $src = null;
30
    public ?string $filter = null;
31
    public ?string $alt = null;
32
    public ?string $pointInterest = null;
33
    /**
34
     * @var array<string, mixed>
35
     */
36
    public array $context = [];
37
    private ?ImageMetadata $metadata = null;
38
    private string $decoratedSrc;
39
    private ?int $decoratedWidth = null;
40
    private ?int $decoratedHeight = null;
41
    public bool $preload = false;
42
    public ?int $ttl = null;
43
    public string $priority = 'high';
44
    public ?string $sizes = null;
45
    public ?string $ratio = null;
46
    public ?bool $retina = null;
47
    /**
48
     * @var BreakpointAssignment[]
49
     */
50
    private array $breakpoinsts = [];
51

52
    private ?ResponsiveAttributesInterface $responsiveAttributes = null;
53

54
    /**
55
     * @param iterable<PathDecoratorInterface> $pathDecorator
56
     */
57
    public function __construct(
58
        private readonly MetadataReaderInterface $analyzer,
59
        private readonly iterable $pathDecorator,
60
        private readonly ?ResponsiveAttributeGenerator $responsiveAttributeGenerator,
61
        private readonly PreloadCollector $preloadCollector,
62
        private readonly string $framework = 'custom',
63
        private readonly bool $defaultRetina = true,
64
    ) {
65
    }
390✔
66

67
    #[PostMount]
68
    public function postMount(): void
69
    {
70
        if (null === $this->retina) {
390✔
71
            $this->retina = $this->defaultRetina;
362✔
72
        }
73

74
        $this->decoratedSrc = $this->src;
390✔
75
        foreach ($this->pathDecorator as $decorator) {
390✔
76
            $this->decoratedSrc = $decorator->decorate($this->decoratedSrc, $this->context);
57✔
77
        }
78

79
        try {
80
            $this->metadata = $this->analyzer->getMetadata($this->src);
390✔
81
        } catch (PathResolutionException) {
134✔
82
            $this->metadata = null;
134✔
83
        }
84
        $this->breakpoinsts = $this->sizes ? BreakpointAssignment::parseSegments($this->sizes, $this->ratio) : [];
390✔
85
        if ($this->breakpoinsts && $this->responsiveAttributeGenerator) {
390✔
86
            $context = $this->context;
197✔
87
            if ($this->filter) {
197✔
NEW
88
                $context['filter'] = $this->filter;
×
89
            }
90
            $this->responsiveAttributes = $this->responsiveAttributeGenerator->generate($this->src, $this->breakpoinsts, $this->metadata->width ?? 0, $this->preload, $this->pointInterest, $context, $this->retina);
197✔
91
        } else {
92
            $this->decoratedWidth = $this->metadata?->width;
193✔
93
            $this->decoratedHeight = $this->metadata?->height;
193✔
94
            foreach ($this->pathDecorator as $decorator) {
193✔
95
                $size = $decorator->getSize($this->decoratedSrc, $this->context);
43✔
96
                if ($size) {
43✔
NEW
97
                    $this->decoratedWidth = $size['width'];
28✔
NEW
98
                    $this->decoratedHeight = $size['height'];
28✔
99
                }
100
            }
101

102
            if ($this->preload) {
193✔
NEW
103
                $this->preloadCollector->add($this->decoratedSrc, 'image', $this->priority);
14✔
104
            }
105
        }
106
    }
107

108
    public function getSrcset(): string
109
    {
UNCOV
110
        if (!$this->responsiveAttributes) {
×
UNCOV
111
            return '';
×
112
        }
113

NEW
114
        return "srcset=\"{$this->responsiveAttributes->getDefaultSource()->getSrcset()}\"";
×
115
    }
116

117
    public function getResponsiveSizes(): string
118
    {
UNCOV
119
        if (!$this->responsiveAttributes) {
×
UNCOV
120
            return '';
×
121
        }
122

NEW
123
        return "sizes=\"{$this->responsiveAttributes->getDefaultSource()->getSizes()}\"";
×
124
    }
125

126
    public function getResponsiveAttributes(): ?ResponsiveAttributesInterface
127
    {
128
        return $this->responsiveAttributes;
316✔
129
    }
130

131
    public function hasResponsiveAttributes(): bool
132
    {
133
        return null !== $this->responsiveAttributes;
316✔
134
    }
135

136
    public function getHash(): ?string
137
    {
138
        return $this->metadata?->originalHash;
361✔
139
    }
140

141
    public function getWidth(): ?int
142
    {
143
        return $this->decoratedWidth;
88✔
144
    }
145

146
    public function getHeight(): ?int
147
    {
148
        return $this->decoratedHeight;
178✔
149
    }
150

151
    public function getVariables(): array
152
    {
153
        return $this->responsiveAttributes?->getVariables() ?? [];
316✔
154
    }
155

156
    public function getDecoratedSrc(): string
157
    {
158
        return $this->decoratedSrc ?? $this->src;
360✔
159
    }
160

161
    public function getController(): string
162
    {
163
        return ProgressiveImageBundle::STIMULUS_CONTROLLER;
316✔
164
    }
165

166
    public function getFramework(): string
167
    {
168
        return $this->framework;
316✔
169
    }
170
}
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