Completed
Push — master ( 29992f...4542c8 )
by Pavel
04:42
created

ImageBehavior::imageChange()   C

Complexity

Conditions 9
Paths 14

Size

Total Lines 49
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 11.9991

Importance

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