Issues (8)

php-src/Content/ImageOrientate.php (1 issue)

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 9
    public function __construct(Graphics $graphics, ISizes $config, Sources\Image $image, ?IIMTranslations $lang = null)
36
    {
37 9
        $this->setImLang($lang);
38 9
        $this->libImage = $image;
39 9
        $this->libGraphics = $graphics;
40 9
        $this->config = $config;
41
    }
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 7
    public function process(array $sourcePath, ?array $targetPath = null): bool
53
    {
54 7
        $sourceFull = array_values($sourcePath);
55 7
        $targetFull = $targetPath ? array_values($targetPath) : $sourceFull;
56
57 7
        $tempPath = strval(tempnam(sys_get_temp_dir(), $this->config->getTempPrefix()));
58
59
        // get from the storage
60 7
        $resource = $this->libImage->get($sourceFull);
61 7
        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 6
        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 6
            $exif = @exif_read_data($tempPath);
75 6
            if (false === $exif) {
76 2
                throw new ImagesException($this->getImLang()->imImageCannotOrientate());
77
            }
78
79
            // now process image locally
80 4
            if (!empty($exif['Orientation'])) {
81 4
                $orientate = intval($exif['Orientation']);
82 4
                $this->libGraphics->rotate(
83 4
                    $this->getAngle($orientate),
84 4
                    $this->getMirror($orientate),
85 4
                    $tempPath,
86 4
                    $sourceFull,
87 4
                    $targetFull
88 4
                );
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 4
        $result = @file_get_contents($tempPath);
98 4
        if (false === $result) {
99
            // @codeCoverageIgnoreStart
100
            @unlink($tempPath);
101
            throw new FilesException($this->getImLang()->imThumbCannotLoadTemporaryImage());
102
        }
103
        // @codeCoverageIgnoreEnd
104
105 4
        $set = $this->libImage->set($targetFull, $result);
106 4
        @unlink($tempPath);
107 4
        return $set;
108
    }
109
110
    /**
111
     * @param int $orientation
112
     * @throws ImagesException
113
     * @return float
114
     */
115 4
    protected function getAngle(int $orientation): float
116
    {
117
        switch ($orientation) {
118 4
            case IExifConstants::EXIF_ORIENTATION_ON_LEFT:
119 3
            case IExifConstants::EXIF_ORIENTATION_MIRROR_ON_LEFT:
120 1
                return 90;
121 3
            case IExifConstants::EXIF_ORIENTATION_UPSIDE_DOWN:
122 2
            case IExifConstants::EXIF_ORIENTATION_MIRROR_UPSIDE_DOWN:
123 1
                return 180;
124 2
            case IExifConstants::EXIF_ORIENTATION_ON_RIGHT:
125 1
            case IExifConstants::EXIF_ORIENTATION_MIRROR_ON_RIGHT:
126 1
                return 270;
127 1
            case IExifConstants::EXIF_ORIENTATION_NORMAL:
128 1
            case IExifConstants::EXIF_ORIENTATION_MIRROR_SIMPLE:
129 1
                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 4
    protected function getMirror(int $orientation): ?int
142
    {
143
        switch ($orientation) {
144 4
            case IExifConstants::EXIF_ORIENTATION_MIRROR_UPSIDE_DOWN:
145 4
            case IExifConstants::EXIF_ORIENTATION_MIRROR_ON_RIGHT:
146 4
            case IExifConstants::EXIF_ORIENTATION_MIRROR_ON_LEFT:
147 4
            case IExifConstants::EXIF_ORIENTATION_MIRROR_SIMPLE:
148 1
                return IMG_FLIP_HORIZONTAL;
149
            default:
150 3
                return null;
151
        }
152
    }
153
154 4
    public function getImage(): Sources\Image
155
    {
156 4
        return $this->libImage;
157
    }
158
}
159