ImageBehavior::imageChange()   B
last analyzed

Complexity

Conditions 10
Paths 18

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 17.4088

Importance

Changes 0
Metric Value
dl 0
loc 56
c 0
b 0
f 0
ccs 29
cts 50
cp 0.58
rs 7.0933
cc 10
nc 18
nop 1
crap 17.4088

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Image behavior for ActiveRecord
4
 *
5
 * @link https://github.com/inblank/yii2-image
6
 * @copyright Copyright (c) 2016 Pavel Aleksandrov <[email protected]>
7
 * @license http://opensource.org/licenses/MIT
8
 */
9
10
namespace inblank\image;
11
12
use Imagine\Filter\Advanced\Canvas;
13
use Imagine\Filter\Transformation;
14
use Imagine\Image\Box;
15
use Imagine\Image\Color;
16
use Imagine\Image\ImageInterface;
17
use Imagine\Image\Point;
18
use yii;
19
use yii\base\Behavior;
20
use yii\db\ActiveRecord;
21
22
/**
23
 * Image Behavior Class
24
 *
25
 * @property ActiveRecord $owner
26
 * @property $imageDefaultUrl URL for default image
27
 * @property $imageUrl image URL for model. If model not have image, will equal $imageDefaultUrl
28
 * @property $imagePath path for image relative document root
29
 * @property $imageAbsolutePath absolute path to image file in filesystem
30
 * @property $imageFile full, with absolute path, image file name in filesystem
31
 */
32
class ImageBehavior extends Behavior
33
{
34
    /** Just proportional resize image to fit size */
35
    const NONE = 0;
36
    /** Crop image resize strategy */
37
    const CROP = 1;
38
    /** Add frame image resize strategy */
39
    const FRAME = 2;
40
    /** Transparent color */
41
    const COLOR_TRANSPARENT = null;
42
    /**
43
     * Name of attribute to store the image
44
     * @var string
45
     */
46
    public $imageAttribute = "image";
47
    /**
48
     * Default image name
49
     * @var string
50
     */
51
    public $imageDefault = "image.png";
52
    /**
53
     * Path to store image files.
54
     * If empty will be init to /images/<ActiveRecord Class Name>
55
     * @var string
56
     */
57
    public $imagePath;
58
    /**
59
     * Size to convert image
60
     * If array: [width, height].
61
     * If integer: use value for with and height.
62
     * If not set: image save as is.
63
     * @var integer|array
64
     */
65
    public $imageSize;
66
    /**
67
     * Image resize strategy
68
     * @var int
69
     */
70
    public $imageResizeStrategy = self::NONE;
71
    /**
72
     * Image frame color
73
     * @var
74
     */
75
    public $imageFrameColor = "#FFF";
76
    /**
77
     * Calculated absolute image path relative to webroot
78
     * @var
79
     */
80
    protected $_imageAbsolutePath;
81
82
    /**
83
     * @inheritdoc
84
     */
85 3
    public function events()
86
    {
87
        return [
88 3
            ActiveRecord::EVENT_AFTER_INSERT => 'afterSave',
89 3
            ActiveRecord::EVENT_AFTER_UPDATE => 'afterSave',
90 3
            ActiveRecord::EVENT_AFTER_DELETE => 'afterDelete',
91 3
            ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate',
92 3
        ];
93
    }
94
95
    /**
96
     * Before update action
97
     */
98
    public function beforeUpdate()
99
    {
100
        // prevent save empty image name value and also for correct remove old image
101
        $this->owner->setAttribute($this->imageAttribute, $this->owner->getOldAttribute($this->imageAttribute));
102
    }
103
104
    /**
105
     * After save action
106
     */
107 2
    public function afterSave()
108
    {
109 2
        $this->imageChangeByUpload();
110 2
    }
111
112
    /**
113
     * After delete action
114
     */
115 1
    public function afterDelete()
116
    {
117 1
        $this->imageRemoveFile();
118 1
    }
119
120
    /**
121
     * Get image path
122
     * @return string
123
     */
124 3
    public function getImagePath()
125
    {
126 3
        if ($this->imagePath === null) {
127 3
            $this->imagePath = '/images/' . strtolower((new \ReflectionClass($this->owner))->getShortName());
128 3
        }
129 3
        return $this->imagePath;
130
    }
131
132
    /**
133
     * Get default image URL
134
     * @return string
135
     * @throws yii\base\InvalidConfigException
136
     */
137
    public function getImageDefaultUrl()
138
    {
139
        return $this->_imageUrl($this->imageDefault);
140
    }
141
142
    /**
143
     * Get absolute team image path in filesystem
144
     * @return string
145
     */
146 3
    public function getImageAbsolutePath()
147
    {
148 3
        if ($this->_imageAbsolutePath === null) {
149 3
            $this->_imageAbsolutePath = Yii::getAlias('@webroot') .
150 3
                '/' . (defined('IS_BACKEND') ? '../' : '') .
151 3
                ltrim($this->getImagePath(), '/');
152 3
            if (!file_exists($this->_imageAbsolutePath)) {
153
                yii\helpers\FileHelper::createDirectory($this->_imageAbsolutePath);
154
            }
155 3
        }
156 3
        return $this->_imageAbsolutePath;
157
    }
158
159
    /**
160
     * Check team image
161
     * @return bool
162
     * @throws yii\base\InvalidConfigException
163
     */
164 3
    public function hasImage()
165
    {
166 3
        $image = $this->owner->getAttribute($this->imageAttribute);
167 3
        return !empty($image) && $image != $this->imageDefault;
168
    }
169
170
    /**
171
     * Check that image file exists
172
     */
173
    public function imageFileExists()
174
    {
175
        return file_exists($this->getImageFile());
176
    }
177
178
    /**
179
     * Get team image URL
180
     * @return string
181
     * @throws yii\base\InvalidConfigException
182
     */
183
    public function getImageUrl()
184
    {
185
        return !$this->hasImage() ?
186
            $this->getImageDefaultUrl() :
187
            $this->_imageUrl($this->owner->getAttribute($this->imageAttribute));
188
    }
189
190
    /**
191
     * Return filename in filesystem
192
     * @return string
193
     */
194 3
    public function getImageFile()
195
    {
196 3
        return $this->getImageAbsolutePath() . '/' . $this->owner->getAttribute($this->imageAttribute);
197
    }
198
199
    /**
200
     * Change image by uploaded file
201
     */
202 2
    public function imageChangeByUpload()
0 ignored issues
show
Coding Style introduced by
imageChangeByUpload uses the super-global variable $_POST 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...
Coding Style introduced by
imageChangeByUpload uses the super-global variable $_FILES 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...
203
    {
204 2
        $formName = $this->owner->formName();
205 2
        if (!empty($_POST[$formName][$this->imageAttribute]) && $_POST[$formName][$this->imageAttribute] == 'empty') {
206
            // reset image
207
            $this->imageReset();
208
        } else {
209
            // upload new
210 2
            if (!empty($_FILES[$formName]['tmp_name'][$this->imageAttribute])) {
211
                $this->imageChange(
212
                    [$_FILES[$formName]['name'][$this->imageAttribute] => $_FILES[$formName]['tmp_name'][$this->imageAttribute]]
213
                );
214
            }
215
        }
216 2
    }
217
218
    /**
219
     * Change image
220
     * @param string|array $sourceFile source file. if set as array ['fileName' => 'file_in_filesystem']
221
     * @return bool true, if image was changed and old image file was deleted. false, if image not changed
222
     * @throws yii\base\InvalidConfigException
223
     */
224 3
    public function imageChange($sourceFile)
225
    {
226 3
        if (is_array($sourceFile)) {
227
            $fileName = key($sourceFile);
228
            $sourceFile = current($sourceFile);
229
        } else {
230 3
            $fileName = $sourceFile;
231
        }
232 3
        if (!file_exists($sourceFile)) {
233 1
            return false;
234
        }
235 3
        $imageName = $this->imageAttribute . '_' .
236 3
            md5(implode('-', (array)$this->owner->getPrimaryKey()) . microtime(true) . rand()) .
237 3
            '.' . pathinfo($fileName)['extension'];
238 3
        $destinationFile = $this->getImageAbsolutePath() . '/' . $imageName;
239 3
        if (!copy($sourceFile, $destinationFile)) {
240
            return false;
241
        }
242 3
        $this->imageRemoveFile();
243 3
        if (!empty($this->imageSize)) {
244 1
            $size = $this->imageSize;
245 1
            if (!is_array($size)) {
246 1
                $size = [$size, $size];
247 1
            }
248 1
            $newBox = new Box($size[0], $size[1]);
249 1
            $image = (new Transformation())->thumbnail(
250 1
                $newBox,
251 1
                $this->imageResizeStrategy == self::CROP ?
252 1
                    ImageInterface::THUMBNAIL_OUTBOUND : ImageInterface::THUMBNAIL_INSET
253 1
            )->apply(yii\imagine\Image::getImagine()->open($destinationFile));
254 1
            $currentBox = $image->getSize();
255 1
            if ($this->imageResizeStrategy === self::FRAME) {
256
                if ($this->imageFrameColor === self::COLOR_TRANSPARENT) {
257
                    $frameColor = '#fff';
258
                    $alpha = 100;
259
                } else {
260
                    $frameColor = $this->imageFrameColor;
261
                    $alpha = 0;
262
                }
263
                $image = (new Transformation())->add(
264
                    new Canvas(
265
                        yii\imagine\Image::getImagine(),
266
                        $newBox,
267
                        $size[0] == $currentBox->getWidth() ?
268
                            new Point(0, ($size[1] - $currentBox->getHeight()) / 2) :
269
                            new Point(($size[0] - $currentBox->getWidth()) / 2, 0),
270
                        new Color($frameColor, $alpha)
271
                    )
272
                )->apply($image);
273
            }
274 1
            $image->save($destinationFile);
275 1
        }
276 3
        $this->owner->setAttribute($this->imageAttribute, $imageName);
277 3
        $this->owner->updateAttributes([$this->imageAttribute]);
278 3
        return true;
279
    }
280
281
    /**
282
     * Reset image to default
283
     * @throws yii\base\InvalidConfigException
284
     */
285 1
    public function imageReset()
286
    {
287 1
        $this->imageRemoveFile();
288 1
        $this->owner->setAttribute($this->imageAttribute, null);
289 1
        $this->owner->updateAttributes([$this->imageAttribute]);
290 1
    }
291
292
    /**
293
     * Remove current image
294
     * @throws yii\base\InvalidConfigException
295
     */
296 3
    protected function imageRemoveFile()
297
    {
298 3
        if ($this->hasImage()) {
299 2
            @unlink($this->getImageFile());
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...
300 2
        }
301 3
    }
302
303
    /**
304
     * Make image URL
305
     * @param string $imageFileName image filename
306
     * @return string
307
     * @throws yii\base\InvalidConfigException
308
     */
309
    protected function _imageUrl($imageFileName)
310
    {
311
        return $this->getImagePath() . '/' . $imageFileName;
312
    }
313
314
}
315