Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.16% covered (warning)
88.16%
67 / 76
72.73% covered (warning)
72.73%
8 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
WayPointHelper
88.16% covered (warning)
88.16%
67 / 76
72.73% covered (warning)
72.73%
8 / 11
26.04
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getImagePath
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getThumbnailBasePath
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 defineThumbnailPath
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findImage
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 getThumbnailPath
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
6.01
 checkImage
71.43% covered (warning)
71.43%
15 / 21
0.00% covered (danger)
0.00%
0 / 1
5.58
 getRootDir
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getIntelUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 cleanName
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
1
 makeThumb
83.33% covered (warning)
83.33%
10 / 12
0.00% covered (danger)
0.00%
0 / 1
3.04
1<?php
2declare(strict_types=1);
3
4namespace App\Service;
5
6use RuntimeException;
7use UnexpectedValueException;
8use Symfony\Component\DependencyInjection\Attribute\Autowire;
9use Symfony\Component\Filesystem\Filesystem;
10
11class WayPointHelper
12{
13    private readonly string $rootDir;
14
15    public function __construct(
16        #[Autowire('%kernel.project_dir%')] string $projectDir,
17        #[Autowire('%env(INTEL_URL)%')] private readonly string $intelUrl
18    )
19    {
20        $this->rootDir = $projectDir.'/public/wp_images';
21    }
22
23    public function getImagePath(string $wpId): string
24    {
25        return $this->rootDir.'/'.$wpId.'.jpg';
26    }
27
28    private function getThumbnailBasePath(): string
29    {
30        return $this->rootDir.'/thumbs';
31    }
32
33    private function defineThumbnailPath(string $wpId): string
34    {
35        return $this->getThumbnailBasePath().'/'.$wpId.'.jpg';
36    }
37
38    public function findImage(?string $wpId): bool|string
39    {
40        if (!$wpId) {
41            return false;
42        }
43
44        $fileSystem = new Filesystem();
45
46        if (false === $fileSystem->exists($this->rootDir)) {
47            $fileSystem->mkdir($this->rootDir);
48        }
49
50        $imagePath = $this->getImagePath($wpId);
51
52        return $fileSystem->exists($imagePath) ? $imagePath : false;
53    }
54
55    public function getThumbnailPath(?string $wpId, string $imageUrl): string
56    {
57        $fileSystem = new Filesystem();
58
59        $path = $this->defineThumbnailPath($wpId ?? '');
60
61        if ($fileSystem->exists($path)) {
62            return $path;
63        }
64
65        if (false === $fileSystem->exists($this->rootDir)) {
66            $fileSystem->mkdir($this->rootDir);
67        }
68
69        if (false === $fileSystem->exists($this->getThumbnailBasePath())) {
70            $fileSystem->mkdir($this->getThumbnailBasePath());
71        }
72
73        if (false === $this->findImage($wpId)) {
74            $this->checkImage($wpId ?? '', $imageUrl);
75        }
76
77        $imagePath = $this->findImage($wpId);
78
79        if (!\is_string($imagePath)) {
80            throw new RuntimeException('Image not found for waypoint: '.$wpId);
81        }
82
83        return $this->makeThumb($imagePath, $path);
84    }
85
86    public function checkImage(
87        string $wpId,
88        string $imageUrl,
89        bool $forceUpdate = false
90    ): void
91    {
92        $imagePath = $this->findImage($wpId);
93
94        if ($imagePath && false === $forceUpdate) {
95            return;
96        }
97
98        $imagePath = $this->getImagePath($wpId);
99
100        $ch = curl_init($imageUrl);
101        if (false === $ch) {
102            throw new UnexpectedValueException(
103                'Can not init curl for: '.$imageUrl
104            );
105        }
106
107        $fp = fopen($imagePath, 'wb');
108        if (false === $fp) {
109            throw new UnexpectedValueException(
110                'Can not open image file under: '.$imageUrl
111            );
112        }
113
114        curl_setopt($ch, CURLOPT_FILE, $fp);
115        curl_setopt($ch, CURLOPT_HEADER, false);
116        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
117        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
118        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
119
120        curl_exec($ch);
121        fclose($fp);
122    }
123
124    public function getRootDir(): string
125    {
126        return $this->rootDir;
127    }
128
129    public function getIntelUrl(): string
130    {
131        return $this->intelUrl;
132    }
133
134    public function cleanName(string $name): string
135    {
136        $replacements = [
137            'á' => 'a',
138            'é' => 'e',
139            'í' => 'i',
140            'ó' => 'o',
141            'Ó' => 'O',
142            'ú' => 'u',
143            'Ú' => 'U',
144            'ñ' => 'ni',
145            'ü' => 'ue',
146            'ä' => 'ae',
147            'ö' => 'oe',
148        ];
149
150        $name = trim($name);
151        $name = str_replace(['.', ',', ';', ':', '"', "'", '\\'], '', $name);
152
153        return str_replace(array_keys($replacements), $replacements, $name);
154    }
155
156    /** @param int<1, max> $desiredWidth */
157    private function makeThumb(string $srcPath, string $destPath, int $desiredWidth = 60): string
158    {
159        /* read the source image */
160        $sourceImage = imagecreatefromjpeg($srcPath);
161
162        if (false === $sourceImage) {
163            throw new RuntimeException('Cannot read image: '.$srcPath);
164        }
165
166        $width = imagesx($sourceImage);
167        $height = imagesy($sourceImage);
168
169        /* find the "desired height" of this thumbnail, relative to the desired width  */
170        $desiredHeight = max(1, (int)floor($height * ($desiredWidth / $width)));
171
172        /* create a new, "virtual" image */
173        $virtualImage = imagecreatetruecolor($desiredWidth, $desiredHeight);
174
175        if (false === $virtualImage) {
176            throw new RuntimeException('Cannot create thumbnail image');
177        }
178
179        /* copy source image at a resized size */
180        imagecopyresampled($virtualImage, $sourceImage, 0, 0, 0, 0, $desiredWidth, $desiredHeight, $width, $height);
181
182        /* create the physical thumbnail image to its destination */
183        imagejpeg($virtualImage, $destPath);
184
185        return $destPath;
186    }
187}