Passed
Push — master ( 3d9115...77fb6a )
by Petr
08:28
created

ImageOrientate::getAngle()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 18
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
cc 9
eloc 15
nc 9
nop 1
dl 0
loc 18
ccs 0
cts 14
cp 0
crap 90
rs 8.0555
c 0
b 0
f 0
1
<?php
2
3
namespace kalanis\kw_images\Content;
4
5
6
use kalanis\kw_files\FilesException;
7
use kalanis\kw_images\Graphics;
8
use kalanis\kw_images\ImagesException;
9
use kalanis\kw_images\Interfaces\IExifConstants;
10
use kalanis\kw_images\Interfaces\IIMTranslations;
11
use kalanis\kw_images\Interfaces\ISizes;
12
use kalanis\kw_images\Sources;
13
use kalanis\kw_images\Traits\TLang;
14
use kalanis\kw_mime\MimeException;
15
use kalanis\kw_paths\PathsException;
16
17
18
/**
19
 * Class ImageOrientate
20
 * Orientate image against the data in its exif
21
 * @package kalanis\kw_images\Content
22
 * @link https://stackoverflow.com/questions/7489742/php-read-exif-data-and-adjust-orientation
23
 * @link https://jdhao.github.io/2019/07/31/image_rotation_exif_info/#exif-orientation-flag
24
 * The main difference between rotation and orientation classes is from where came the data which will define what kind
25
 * of operation will be processed. Orientation has them in image EXIF, rotation got them from external input.
26
 */
27
class ImageOrientate
28
{
29
    use TLang;
30
31
    protected Sources\Image $libImage;
32
    protected Graphics $libGraphics;
33
    protected ISizes $config;
34
35 5
    public function __construct(Graphics $graphics, ISizes $config, Sources\Image $image, ?IIMTranslations $lang = null)
36
    {
37 5
        $this->setImLang($lang);
38 5
        $this->libImage = $image;
39 5
        $this->libGraphics = $graphics;
40 5
        $this->config = $config;
41 5
    }
42
43
    /**
44
     * @param string[] $sourcePath
45
     * @param string[]|null $targetPath
46
     * @throws FilesException
47
     * @throws ImagesException
48
     * @throws MimeException
49
     * @throws PathsException
50
     * @return bool
51
     */
52 3
    public function process(array $sourcePath, ?array $targetPath = null): bool
53
    {
54 3
        $sourceFull = array_values($sourcePath);
55 3
        $targetFull = $targetPath ? array_values($targetPath) : $sourceFull;
56
57 3
        $tempPath = strval(tempnam(sys_get_temp_dir(), $this->config->getTempPrefix()));
58
59
        // get from the storage
60 3
        $resource = $this->libImage->get($sourceFull);
61 3
        if (empty($resource)) {
62 1
            @unlink($tempPath);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

62
            /** @scrutinizer ignore-unhandled */ @unlink($tempPath);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
63 1
            throw new FilesException($this->getImLang()->imThumbCannotGetBaseImage());
64
        }
65
66 2
        if (false === @file_put_contents($tempPath, $resource)) {
67
            // @codeCoverageIgnoreStart
68
            @unlink($tempPath);
69
            throw new FilesException($this->getImLang()->imThumbCannotStoreTemporaryImage());
70
        }
71
        // @codeCoverageIgnoreEnd
72
73
        try {
74 2
            $exif = @exif_read_data($tempPath);
75 2
            if (false === $exif) {
76 2
                throw new ImagesException($this->getImLang()->imImageCannotOrientate());
77
            }
78
79
            // now process image locally
80
            if (!empty($exif['Orientation'])) {
81
                $orientate = intval($exif['Orientation']);
82
                $this->libGraphics->rotate(
83
                    $this->getAngle($orientate),
84
                    $this->getMirror($orientate),
85
                    $tempPath,
86
                    $sourceFull,
87
                    $targetFull
88
                );
89
            }
90 2
        } catch (ImagesException $ex) {
91
            // clear when fails
92 2
            @unlink($tempPath);
93 2
            throw $ex;
94
        }
95
96
        // return result to the storage as new file
97
        $result = @file_get_contents($tempPath);
98
        if (false === $result) {
99
            // @codeCoverageIgnoreStart
100
            @unlink($tempPath);
101
            throw new FilesException($this->getImLang()->imThumbCannotLoadTemporaryImage());
102
        }
103
        // @codeCoverageIgnoreEnd
104
105
        $set = $this->libImage->set($targetFull, $result);
106
        @unlink($tempPath);
107
        return $set;
108
    }
109
110
    /**
111
     * @param int $orientation
112
     * @throws ImagesException
113
     * @return float
114
     */
115
    protected function getAngle(int $orientation): float
116
    {
117
        switch ($orientation) {
118
            case IExifConstants::EXIF_ORIENTATION_ON_LEFT:
119
            case IExifConstants::EXIF_ORIENTATION_MIRROR_ON_LEFT:
120
                return 90;
121
            case IExifConstants::EXIF_ORIENTATION_UPSIDE_DOWN:
122
            case IExifConstants::EXIF_ORIENTATION_MIRROR_UPSIDE_DOWN:
123
                return 180;
124
            case IExifConstants::EXIF_ORIENTATION_ON_RIGHT:
125
            case IExifConstants::EXIF_ORIENTATION_MIRROR_ON_RIGHT:
126
                return 270;
127
            case IExifConstants::EXIF_ORIENTATION_NORMAL:
128
            case IExifConstants::EXIF_ORIENTATION_MIRROR_SIMPLE:
129
                return 0;
130
                // @codeCoverageIgnoreStart
131
            default:
132
                throw new ImagesException($this->getImLang()->imImageCannotOrientate());
133
            // @codeCoverageIgnoreEnd
134
        }
135
    }
136
137
    /**
138
     * @param int $orientation
139
     * @return int|null
140
     */
141
    protected function getMirror(int $orientation): ?int
142
    {
143
        switch ($orientation) {
144
            case IExifConstants::EXIF_ORIENTATION_MIRROR_UPSIDE_DOWN:
145
            case IExifConstants::EXIF_ORIENTATION_MIRROR_ON_RIGHT:
146
            case IExifConstants::EXIF_ORIENTATION_MIRROR_ON_LEFT:
147
            case IExifConstants::EXIF_ORIENTATION_MIRROR_SIMPLE:
148
                return IMG_FLIP_HORIZONTAL;
149
            default:
150
                return null;
151
        }
152
    }
153
154
    public function getImage(): Sources\Image
155
    {
156
        return $this->libImage;
157
    }
158
}
159