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

liip / LiipImagineBundle / 22222994515

20 Feb 2026 11:51AM UTC coverage: 81.099% (+1.0%) from 80.126%
22222994515

Pull #1651

github

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

250 of 270 new or added lines in 9 files covered. (92.59%)

5 existing lines in 1 file now uncovered.

2390 of 2947 relevant lines covered (81.1%)

99.75 hits per line

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

96.0
/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->isBinaryTypeAvifImage($binary)) {
110✔
61
                        return $binary;
22✔
62
                }
63

64

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

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

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

80
        $process->run();
88✔
81

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

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

89
                $result = new Binary(file_get_contents($output), $binary->getMimeType(), $binary->getFormat());
44✔
90

91
        unlink($input);
44✔
92
        unlink($output);
44✔
93

94
        return $result;
44✔
95
    }
96

97
        protected function isBinaryTypeAvifImage(BinaryInterface $binary): bool
98
        {
99
                return $this->isBinaryTypeMatch($binary, ['image/avif']);
110✔
100
        }
101

102
        private function getExtensionFromMimeType(string $mimeType): string
103
        {
104
                switch ($mimeType) {
105
                        case 'image/jpeg':
88✔
106
                        case 'image/jpg':
88✔
NEW
107
                                return '.jpg';
×
108
                        case 'image/png':
88✔
NEW
109
                                return '.png';
×
110
                        case 'image/avif':
88✔
111
                                return '.avif';
88✔
112
                        default:
NEW
113
                                return '';
×
114
                }
115
        }
116

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

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

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

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

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

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

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

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

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

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

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