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

liip / LiipImagineBundle / 22202859951

19 Feb 2026 10:31PM UTC coverage: 81.146% (+1.0%) from 80.126%
22202859951

Pull #1651

github

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

249 of 268 new or added lines in 8 files covered. (92.91%)

3 existing lines in 1 file now uncovered.

2393 of 2949 relevant lines covered (81.15%)

99.86 hits per line

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

97.33
/Imagine/Filter/PostProcessor/AvifPostProcessor.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\Imagine\Filter\PostProcessor;
13

14
use Liip\ImagineBundle\Binary\BinaryInterface;
15
use Liip\ImagineBundle\Model\Binary;
16
use Symfony\Component\OptionsResolver\OptionsResolver;
17
use Symfony\Component\Process\Exception\ProcessFailedException;
18

19
class AvifPostProcessor extends AbstractPostProcessor
20
{
21
    /**
22
     * @var int|null
23
     */
24
    protected $quality;
25

26
    /**
27
     * @var int|null
28
     */
29
    protected $speed;
30

31
    /**
32
     * @var int|null
33
     */
34
    protected $jobs;
35

36
    /**
37
     * @var OptionsResolver
38
     */
39
    private $resolver;
40

41
    public function __construct(
42
        string $executablePath = '/usr/bin/avifenc',
43
        ?string $temporaryRootPath = null,
44
        ?int $quality = null,
45
        ?int $speed = null,
46
        ?int $jobs = null
47
    ) {
48
        parent::__construct($executablePath, $temporaryRootPath);
209✔
49

50
        $this->quality = $quality;
209✔
51
        $this->speed = $speed;
209✔
52
        $this->jobs = $jobs;
209✔
53
        $this->resolver = new OptionsResolver();
209✔
54

55
        $this->configureOptions($this->resolver);
209✔
56
    }
57

58
    public function process(BinaryInterface $binary, array $options = []): BinaryInterface
59
    {
60
        if (!$this->isBinaryTypeSupported($binary)) {
110✔
61
            return $binary;
11✔
62
        }
63

64
        $input = $this->writeTemporaryFile($binary, $options, 'imagine-post-processor-avif-input');
99✔
65
        if (false === mb_strpos(basename($input), '.')) {
99✔
66
            $inputWithExtension = $input.$this->getExtensionFromMimeType($binary->getMimeType());
99✔
67
            if (rename($input, $inputWithExtension)) {
99✔
68
                $input = $inputWithExtension;
99✔
69
            }
70
        }
71

72
        $output = $this->acquireTemporaryFilePath($options, 'imagine-post-processor-avif-output').'.avif';
99✔
73

74
        $arguments = $this->getProcessArguments($options);
99✔
75
        $arguments[] = $input;
99✔
76
        $arguments[] = $output;
99✔
77
        $process = $this->createProcess($arguments, $options);
99✔
78

79
        $process->run();
99✔
80

81
        if (!$this->isSuccessfulProcess($process)) {
99✔
82
            unlink($input);
44✔
83
            @unlink($output);
44✔
84

85
            throw new ProcessFailedException($process);
44✔
86
        }
87

88
        $result = new Binary(file_get_contents($output), 'image/avif', 'avif');
55✔
89

90
        unlink($input);
55✔
91
        unlink($output);
55✔
92

93
        return $result;
55✔
94
    }
95

96
    private function getExtensionFromMimeType(string $mimeType): string
97
    {
98
        switch ($mimeType) {
99
            case 'image/jpeg':
99✔
100
            case 'image/jpg':
88✔
101
                return '.jpg';
11✔
102
            case 'image/png':
88✔
NEW
103
                return '.png';
×
104
            case 'image/avif':
88✔
105
                return '.avif';
88✔
106
            default:
NEW
107
                return '';
×
108
        }
109
    }
110

111
    protected function isBinaryTypeSupported(BinaryInterface $binary): bool
112
    {
113
        return $this->isBinaryTypeMatch($binary, ['image/avif', 'image/jpeg', 'image/jpg', 'image/png']);
110✔
114
    }
115

116
    protected function configureOptions(OptionsResolver $resolver): void
117
    {
118
        $resolver
209✔
119
            ->setDefault('quality', $this->quality)
209✔
120
            ->setAllowedTypes('quality', ['null', 'int'])
209✔
121
            ->setAllowedValues('quality', static function ($value) {
209✔
122
                if (null === $value) {
198✔
123
                    return true;
132✔
124
                }
125

126
                return $value >= 0 && $value <= 100;
66✔
127
            });
209✔
128

129
        $resolver
209✔
130
            ->setDefault('speed', $this->speed)
209✔
131
            ->setAllowedTypes('speed', ['null', 'int'])
209✔
132
            ->setAllowedValues('speed', static function ($value) {
209✔
133
                if (null === $value) {
187✔
134
                    return true;
143✔
135
                }
136

137
                return $value >= 0 && $value <= 10;
44✔
138
            });
209✔
139

140
        $resolver
209✔
141
            ->setDefault('jobs', $this->jobs)
209✔
142
            ->setAllowedTypes('jobs', ['null', 'int'])
209✔
143
            ->setAllowedValues('jobs', static function ($value) {
209✔
144
                if (null === $value) {
176✔
145
                    return true;
132✔
146
                }
147

148
                return $value >= 0;
44✔
149
            });
209✔
150
    }
151

152
    /**
153
     * @param array<mixed> $options
154
     *
155
     * @return string[]
156
     */
157
    protected function getProcessArguments(array $options = []): array
158
    {
159
        $options = $this->resolver->resolve($options);
198✔
160
        $arguments = [$this->executablePath];
165✔
161

162
        if (null !== $options['quality']) {
165✔
163
            $quantizer = (int) round(63 * (1 - $options['quality'] / 100));
55✔
164
            $arguments[] = '--min';
55✔
165
            $arguments[] = $quantizer;
55✔
166
            $arguments[] = '--max';
55✔
167
            $arguments[] = $quantizer;
55✔
168
        }
169

170
        if (null !== $options['speed']) {
165✔
171
            $arguments[] = '--speed';
33✔
172
            $arguments[] = $options['speed'];
33✔
173
        }
174

175
        if (null !== $options['jobs']) {
165✔
176
            $arguments[] = '--jobs';
33✔
177
            $arguments[] = $options['jobs'];
33✔
178
        }
179

180
        return $arguments;
165✔
181
    }
182
}
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