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

liip / LiipImagineBundle / 22198012024

19 Feb 2026 08:04PM UTC coverage: 81.034% (+0.9%) from 80.126%
22198012024

Pull #1651

github

web-flow
Merge 5909f3aa5 into 69d2df3c6
Pull Request #1651: Deprecate `webp` configuration in favor of `alternative_formats` and …

221 of 238 new or added lines in 8 files covered. (92.86%)

16 existing lines in 3 files now uncovered.

2367 of 2921 relevant lines covered (81.03%)

98.33 hits per line

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

96.67
/Service/FormatNegotiator.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\Service;
13

14
use Psr\Log\LoggerInterface;
15
use Symfony\Component\HttpFoundation\Request;
16

17
class FormatNegotiator
18
{
19
    /**
20
     * @var array
21
     */
22
    private $mimeMap;
23

24
    /**
25
     * @var LoggerInterface|null
26
     */
27
    private $logger;
28

29
    public function __construct(array $mimeMap = [], ?LoggerInterface $logger = null)
30
    {
31
        $this->mimeMap = $mimeMap;
44✔
32
        $this->logger = $logger;
44✔
33
    }
34

35
    /**
36
     * Negotiate the best format based on the Request's Accept header and configured alternative formats.
37
     *
38
     * @param array $configuredAlternativeFormats Configuration from alternative_formats
39
     *
40
     * @return string[] Sorted array of format names (e.g., ['avif', 'webp'])
41
     */
42
    public function negotiate(Request $request, array $configuredAlternativeFormats): array
43
    {
44
        $acceptedFormats = $this->getAcceptedFormats($request);
11✔
45

46
        if (empty($acceptedFormats)) {
11✔
47
            return [];
11✔
48
        }
49

50
        $negotiated = [];
11✔
51
        foreach ($configuredAlternativeFormats as $format => $config) {
11✔
52
            if (isset($config['generate']) && false === $config['generate']) {
11✔
53
                continue;
11✔
54
            }
55

56
            if ($this->isFormatAccepted($format, $request)) {
11✔
57
                $q = $this->getMaxQForFormat($format, $request);
11✔
58
                $priority = $config['priority'] ?? 0;
11✔
59
                $negotiated[] = [
11✔
60
                    'format' => $format,
11✔
61
                    'q' => $q,
11✔
62
                    'priority' => $priority,
11✔
63
                ];
11✔
64
            }
65
        }
66

67
        // Sort by q-factor (desc), then by priority (desc), then by original order
68
        usort($negotiated, function ($a, $b) {
11✔
69
            if ($a['q'] !== $b['q']) {
11✔
70
                return $b['q'] <=> $a['q'];
11✔
71
            }
72

73
            if ($a['priority'] !== $b['priority']) {
11✔
74
                return $b['priority'] <=> $a['priority'];
11✔
75
            }
76

NEW
77
            return 0;
×
78
        });
11✔
79

80
        return array_column($negotiated, 'format');
11✔
81
    }
82

83
    /**
84
     * Check if a specific format is accepted by the client.
85
     */
86
    public function isFormatAccepted(string $format, Request $request): bool
87
    {
88
        $mimeTypes = $this->getMimeTypesForFormat($format);
33✔
89
        $acceptHeader = $request->headers->get('Accept', '');
33✔
90

91
        foreach ($mimeTypes as $mimeType) {
33✔
92
            if (preg_match('#'.preg_quote($mimeType, '#').'(;q=([0-9\.]+))?#', $acceptHeader, $matches)) {
33✔
93
                $q = isset($matches[2]) ? (float) $matches[2] : 1.0;
33✔
94
                if ($q > 0) {
33✔
95
                    return true;
33✔
96
                }
97
            }
98
        }
99

100
        return false;
11✔
101
    }
102

103
    /**
104
     * Returns an array of accepted formats with their q-factors.
105
     */
106
    public function getAcceptedFormats(Request $request): array
107
    {
108
        $acceptHeader = $request->headers->get('Accept', '');
22✔
109
        if (!$acceptHeader) {
22✔
NEW
110
            return [];
×
111
        }
112

113
        $accepted = [];
22✔
114
        // Very basic parsing of Accept header
115
        $parts = explode(',', $acceptHeader);
22✔
116
        foreach ($parts as $part) {
22✔
117
            $subParts = explode(';', trim($part));
22✔
118
            $mimeType = trim($subParts[0]);
22✔
119
            $q = 1.0;
22✔
120
            if (isset($subParts[1]) && str_starts_with(trim($subParts[1]), 'q=')) {
22✔
121
                $q = (float) mb_substr(trim($subParts[1]), 2);
22✔
122
            }
123

124
            foreach ($this->mimeMap as $format => $mimes) {
22✔
125
                if (\in_array($mimeType, (array) $mimes, true)) {
22✔
126
                    $accepted[$format] = max($accepted[$format] ?? 0, $q);
22✔
127
                }
128
            }
129
        }
130

131
        arsort($accepted);
22✔
132

133
        return $accepted;
22✔
134
    }
135

136
    public function registerMimeTypes(string $format, array $mimeTypes): void
137
    {
138
        $this->mimeMap[$format] = $mimeTypes;
11✔
139
    }
140

141
    /**
142
     * Get the max q-factor for a given format from the Accept header.
143
     */
144
    private function getMaxQForFormat(string $format, Request $request): float
145
    {
146
        $mimeTypes = $this->getMimeTypesForFormat($format);
11✔
147
        $acceptHeader = $request->headers->get('Accept', '');
11✔
148
        $maxQ = 0.0;
11✔
149

150
        foreach ($mimeTypes as $mimeType) {
11✔
151
            if (preg_match('#'.preg_quote($mimeType, '#').'(;q=([0-9\.]+))?#', $acceptHeader, $matches)) {
11✔
152
                $q = isset($matches[2]) ? (float) $matches[2] : 1.0;
11✔
153
                if ($q > $maxQ) {
11✔
154
                    $maxQ = $q;
11✔
155
                }
156
            }
157
        }
158

159
        return $maxQ;
11✔
160
    }
161

162
    private function getMimeTypesForFormat(string $format): array
163
    {
164
        return (array) ($this->mimeMap[$format] ?? []);
33✔
165
    }
166
}
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