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

liip / LiipImagineBundle / 18472468786

13 Oct 2025 04:38PM UTC coverage: 81.627% (-0.09%) from 81.715%
18472468786

Pull #1639

github

web-flow
Merge b69877f65 into f8c98a5a9
Pull Request #1639: drop support for unmaintained Symfony versions

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

4 existing lines in 3 files now uncovered.

2288 of 2803 relevant lines covered (81.63%)

103.31 hits per line

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

93.48
/Controller/ImagineController.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\Controller;
13

14
use Imagine\Exception\RuntimeException;
15
use Liip\ImagineBundle\Config\Controller\ControllerConfig;
16
use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException;
17
use Liip\ImagineBundle\Exception\Imagine\Filter\NonExistingFilterException;
18
use Liip\ImagineBundle\Imagine\Cache\Helper\PathHelper;
19
use Liip\ImagineBundle\Imagine\Cache\SignerInterface;
20
use Liip\ImagineBundle\Imagine\Data\DataManager;
21
use Liip\ImagineBundle\Service\FilterService;
22
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
23
use Symfony\Component\HttpFoundation\RedirectResponse;
24
use Symfony\Component\HttpFoundation\Request;
25
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
26
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
27
use Symfony\Component\HttpKernel\Kernel;
28

29
class ImagineController
30
{
31
    /**
32
     * @var FilterService
33
     */
34
    private $filterService;
35

36
    /**
37
     * @var DataManager
38
     */
39
    private $dataManager;
40

41
    /**
42
     * @var SignerInterface
43
     */
44
    private $signer;
45

46
    /**
47
     * @var ControllerConfig
48
     */
49
    private $controllerConfig;
50

51
    public function __construct(
52
        FilterService $filterService,
53
        DataManager $dataManager,
54
        SignerInterface $signer,
55
        ?ControllerConfig $controllerConfig = null
56
    ) {
57
        $this->filterService = $filterService;
325✔
58
        $this->dataManager = $dataManager;
325✔
59
        $this->signer = $signer;
325✔
60

61
        if (null === $controllerConfig) {
325✔
62
            @trigger_error(\sprintf(
13✔
63
                'Instantiating "%s" without a forth argument of type "%s" is deprecated since 2.2.0 and will be required in 3.0.', self::class, ControllerConfig::class
13✔
64
            ), E_USER_DEPRECATED);
13✔
65
        }
66

67
        $this->controllerConfig = $controllerConfig ?? new ControllerConfig(301);
325✔
68
    }
125✔
69

70
    /**
71
     * This action applies a given filter to a given image, saves the image and redirects the browser to the stored
72
     * image.
73
     *
74
     * The resulting image is cached so subsequent requests will redirect to the cached image instead applying the
75
     * filter and storing the image again.
76
     *
77
     * @param string $path
78
     * @param string $filter
79
     *
80
     * @throws RuntimeException
81
     * @throws NotFoundHttpException
82
     *
83
     * @return RedirectResponse
84
     */
85
    public function filterAction(Request $request, $path, $filter)
86
    {
87
        $path = PathHelper::urlPathToFilePath($path);
221✔
88
        // TODO once we limit `symfony/http-foundation` to 6.4 or newer, use `$request->query->getString()`
89
        $resolver = $request->query->has('resolver') ? (string) $request->query->get('resolver') : null;
221✔
90

91
        return $this->createRedirectResponse(function () use ($path, $filter, $resolver, $request) {
136✔
92
            return $this->filterService->getUrlOfFilteredImage(
221✔
93
                $path,
221✔
94
                $filter,
187✔
95
                $resolver,
187✔
96
                $this->isWebpSupported($request)
221✔
97
            );
136✔
98
        }, $path, $filter);
221✔
99
    }
100

101
    /**
102
     * This action applies a given filter -merged with additional runtime filters- to a given image, saves the image and
103
     * redirects the browser to the stored image.
104
     *
105
     * The resulting image is cached so subsequent requests will redirect to the cached image instead applying the
106
     * filter and storing the image again.
107
     *
108
     * @param string $hash
109
     * @param string $path
110
     * @param string $filter
111
     *
112
     * @throws RuntimeException
113
     * @throws BadRequestHttpException
114
     * @throws NotFoundHttpException
115
     *
116
     * @return RedirectResponse
117
     */
118
    public function filterRuntimeAction(Request $request, $hash, $path, $filter)
119
    {
120
        $resolver = $request->query->has('resolver') ? (string) $request->query->get('resolver') : null;
143✔
121
        $path = PathHelper::urlPathToFilePath($path);
143✔
122
        $runtimeConfig = $this->getFiltersBc($request);
143✔
123

124
        if (true !== $this->signer->check($hash, $path, $runtimeConfig)) {
130✔
125
            throw new BadRequestHttpException(\sprintf('Signed url does not pass the sign check for path "%s" and filter "%s" and runtime config %s', $path, $filter, json_encode($runtimeConfig)));
13✔
126
        }
127

128
        return $this->createRedirectResponse(function () use ($path, $filter, $runtimeConfig, $resolver, $request) {
72✔
129
            return $this->filterService->getUrlOfFilteredImageWithRuntimeFilters(
117✔
130
                $path,
117✔
131
                $filter,
99✔
132
                $runtimeConfig,
99✔
133
                $resolver,
99✔
134
                $this->isWebpSupported($request)
117✔
135
            );
72✔
136
        }, $path, $filter, $hash);
117✔
137
    }
138

139
    private function getFiltersBc(Request $request): array
140
    {
141
        try {
142
            return $request->query->all('filters');
143✔
143
        } catch (BadRequestException $e) {
13✔
144
            // for strict BC - BadRequestException seems more suited to this situation.
145
            // remove the try-catch in version 3
146
            throw new NotFoundHttpException(\sprintf('Filters must be an array. Value was "%s"', $request->query->get('filters')));
13✔
147
        }
148
    }
149

150
    private function createRedirectResponse(\Closure $url, string $path, string $filter, ?string $hash = null): RedirectResponse
151
    {
152
        try {
153
            return new RedirectResponse($url(), $this->controllerConfig->getRedirectResponseCode());
260✔
154
        } catch (NotLoadableException $exception) {
26✔
155
            if (null !== $this->dataManager->getDefaultImageUrl($filter)) {
13✔
UNCOV
156
                return new RedirectResponse($this->dataManager->getDefaultImageUrl($filter));
×
157
            }
158

159
            throw new NotFoundHttpException(\sprintf('Source image for path "%s" could not be found', $path), $exception);
13✔
160
        } catch (NonExistingFilterException $exception) {
13✔
161
            throw new NotFoundHttpException(\sprintf('Requested non-existing filter "%s"', $filter), $exception);
13✔
162
        } catch (RuntimeException $exception) {
×
UNCOV
163
            throw new \RuntimeException(vsprintf('Unable to create image for path "%s" and filter "%s". Message was "%s"', [$hash ? \sprintf('%s/%s', $hash, $path) : $path, $filter, $exception->getMessage()]), 0, $exception);
×
164
        }
165
    }
166

167
    private function isWebpSupported(Request $request): bool
168
    {
169
        return false !== mb_stripos($request->headers->get('accept', ''), 'image/webp');
260✔
170
    }
171
}
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