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

tito10047 / progressive-image-bundle / 20675431244

03 Jan 2026 09:29AM UTC coverage: 88.617% (+0.3%) from 88.338%
20675431244

push

github

tito10047
add TTL support for image caching: implement configurable TTL handling in cache services, adapt Twig components, and update tests to validate TTL functionality

12 of 12 new or added lines in 3 files covered. (100.0%)

120 existing lines in 9 files now uncovered.

615 of 694 relevant lines covered (88.62%)

184.23 hits per line

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

100.0
/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\Exception\PathResolutionException;
20
use Tito10047\ProgressiveImageBundle\ProgressiveImageBundle;
21
use Tito10047\ProgressiveImageBundle\Service\MetadataReaderInterface;
22
use Tito10047\ProgressiveImageBundle\Service\PreloadCollector;
23
use Tito10047\ProgressiveImageBundle\Service\ResponsiveAttributeGenerator;
24

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

65
    #[PostMount]
66
    public function postMount(): void
67
    {
68
        try {
69
            $this->metadata = $this->analyzer->getMetadata($this->src);
262✔
70
        } catch (PathResolutionException) {
134✔
71
            $this->metadata = null;
134✔
72
        }
73
        $this->decoratedSrc = $this->src;
262✔
74
        $this->decoratedWidth = $this->metadata?->width;
262✔
75
        $this->decoratedHeight = $this->metadata?->height;
262✔
76
        foreach ($this->pathDecorator as $decorator) {
262✔
77
            $this->decoratedSrc = $decorator->decorate($this->decoratedSrc, $this->context);
57✔
78
            $size = $decorator->getSize($this->decoratedSrc, $this->context);
57✔
79
            if ($size) {
57✔
UNCOV
80
                $this->decoratedWidth = $size['width'];
28✔
UNCOV
81
                $this->decoratedHeight = $size['height'];
28✔
82
            }
83
        }
84
        $this->breakpoinsts = $this->sizes ? BreakpointAssignment::parseSegments($this->sizes, $this->ratio) : [];
262✔
85
        if ($this->breakpoinsts) {
262✔
UNCOV
86
            $this->responsiveAttributes = $this->responsiveAttributeGenerator?->generate($this->src, $this->breakpoinsts, $this->decoratedWidth ?? 0, $this->preload, $this->pointInterest);
84✔
87
        } elseif ($this->preload) {
178✔
UNCOV
88
            $this->preloadCollector->add($this->decoratedSrc, 'image', $this->priority);
14✔
89
        }
90
    }
91

92
    public function getSrcset(): string
93
    {
94
        if (!$this->responsiveAttributes) {
188✔
95
            return '';
104✔
96
        }
97

UNCOV
98
        return "srcset=\"{$this->responsiveAttributes['srcset']}\"";
84✔
99
    }
100

101
    public function getResponsiveSizes(): string
102
    {
103
        if (!$this->responsiveAttributes) {
188✔
104
            return '';
104✔
105
        }
106

UNCOV
107
        return "sizes=\"{$this->responsiveAttributes['sizes']}\"";
84✔
108
    }
109

110
    public function getHash(): ?string
111
    {
112
        return $this->metadata?->originalHash;
233✔
113
    }
114

115
    public function getWidth(): ?int
116
    {
117
        return $this->decoratedWidth ?? $this->metadata?->width;
157✔
118
    }
119

120
    public function getHeight(): ?int
121
    {
122
        return $this->decoratedHeight ?? $this->metadata?->height;
247✔
123
    }
124

125
    public function getDecoratedSrc(): string
126
    {
127
        return $this->decoratedSrc ?? $this->src;
232✔
128
    }
129

130
    public function getController(): string
131
    {
132
        return ProgressiveImageBundle::STIMULUS_CONTROLLER;
188✔
133
    }
134
}
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