Completed
Pull Request — master (#191)
by
unknown
02:40
created

Rotate::getOrientation()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
rs 8.8571
cc 5
eloc 10
nc 5
nop 1
1
<?php
2
namespace Fab\Media\FileUpload\Optimizer;
3
4
/*
5
 * This file is part of the Fab/Media project under GPLv2 or later.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.md file that was distributed with this source code.
9
 */
10
11
use Fab\Media\FileUpload\ImageOptimizerInterface;
12
13
/**
14
 * Class that optimize an image according to some settings.
15
 */
16
class Rotate implements ImageOptimizerInterface
17
{
18
19
    /**
20
     * @var \TYPO3\CMS\Frontend\Imaging\GifBuilder
21
     */
22
    protected $gifCreator;
23
24
    /**
25
     * @return \Fab\Media\FileUpload\Optimizer\Rotate
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
26
     */
27
    public function __construct()
28
    {
29
        $this->gifCreator = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Imaging\\GifBuilder');
30
        $this->gifCreator->init();
31
        $this->gifCreator->absPrefix = PATH_site;
32
    }
33
34
    /**
35
     * Optimize the given uploaded image
36
     *
37
     * @param \Fab\Media\FileUpload\UploadedFileInterface $uploadedFile
38
     * @return \Fab\Media\FileUpload\UploadedFileInterface
39
     */
40
    public function optimize($uploadedFile)
0 ignored issues
show
Coding Style introduced by
optimize uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
41
    {
42
43
        $orientation = $this->getOrientation($uploadedFile->getFileWithAbsolutePath());
44
        $isRotated = $this->isRotated($orientation);
45
46
        // Only rotate image if necessary!
47
        if ($isRotated > 0) {
48
            $transformation = $this->getTransformation($orientation);
49
50
            $imParams = '###SkipStripProfile###';
51
            if ($transformation !== '') {
52
                $imParams .= ' ' . $transformation;
53
            }
54
55
            $tempFileInfo = $this->gifCreator->imageMagickConvert($uploadedFile->getFileWithAbsolutePath(), '', '', '', $imParams, '', [], true);
56
            if ($tempFileInfo) {
57
                // Replace original file
58
                @unlink($uploadedFile->getFileWithAbsolutePath());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
59
                @rename($tempFileInfo[3], $uploadedFile->getFileWithAbsolutePath());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
60
61
                if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['im_version_5'] === 'gm') {
62
                    $this->resetOrientation($uploadedFile->getFileWithAbsolutePath());
63
                }
64
            }
65
        }
66
        return $uploadedFile;
67
    }
68
69
    /**
70
     * Returns the EXIF orientation of a given picture.
71
     *
72
     * @param string $filename
73
     * @return integer
74
     */
75
    protected function getOrientation($filename)
76
    {
77
        $extension = strtolower(substr($filename, strrpos($filename, '.') + 1));
78
        $orientation = 1; // Fallback to "straight"
79
        if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('jpg,jpeg,tif,tiff', $extension) && function_exists('exif_read_data')) {
80
            try {
81
                $exif = exif_read_data($filename);
82
                if ($exif) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $exif of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
83
                    $orientation = $exif['Orientation'];
84
                }
85
            } catch (\Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
86
        }
87
        return $orientation;
88
    }
89
90
    /**
91
     * Returns true if the given picture is rotated.
92
     *
93
     * @param integer $orientation EXIF orientation
94
     * @return integer
95
     * @see http://www.impulseadventure.com/photo/exif-orientation.html
96
     */
97
    protected function isRotated($orientation)
98
    {
99
        $ret = false;
100
        switch ($orientation) {
101
            case 2: // horizontal flip
102
            case 3: // 180°
103
            case 4: // vertical flip
104
            case 5: // vertical flip + 90 rotate right
105
            case 6: // 90° rotate right
106
            case 7: // horizontal flip + 90 rotate right
107
            case 8: // 90° rotate left
108
                $ret = true;
109
                break;
110
        }
111
        return $ret;
112
    }
113
114
    /**
115
     * Returns a command line parameter to fix the orientation of a rotated picture.
116
     *
117
     * @param integer $orientation
118
     * @return string
119
     */
120
    protected function getTransformation($orientation)
0 ignored issues
show
Coding Style introduced by
getTransformation uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
121
    {
122
        $transformation = '';
123
        if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['im_version_5'] !== 'gm') {
124
            // ImageMagick
125
            if ($orientation >= 2 && $orientation <= 8) {
126
                $transformation = '-auto-orient';
127
            }
128
        } else {
129
            // GraphicsMagick
130
            switch ($orientation) {
131
                case 2: // horizontal flip
132
                    $transformation = '-flip horizontal';
133
                    break;
134
                case 3: // 180°
135
                    $transformation = '-rotate 180';
136
                    break;
137
                case 4: // vertical flip
138
                    $transformation = '-flip vertical';
139
                    break;
140
                case 5: // vertical flip + 90 rotate right
141
                    $transformation = '-transpose';
142
                    break;
143
                case 6: // 90° rotate right
144
                    $transformation = '-rotate 90';
145
                    break;
146
                case 7: // horizontal flip + 90 rotate right
147
                    $transformation = '-transverse';
148
                    break;
149
                case 8: // 90° rotate left
150
                    $transformation = '-rotate 270';
151
                    break;
152
            }
153
        }
154
        return $transformation;
155
    }
156
157
    /**
158
     * Resets the EXIF orientation flag of a picture.
159
     *
160
     * @param string $filename
161
     * @return void
162
     * @see http://sylvana.net/jpegcrop/exif_orientation.html
163
     */
164
    protected function resetOrientation($filename)
165
    {
166
        JpegExifOrient::setOrientation($filename, 1);
167
    }
168
169
}
170