1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @copyright Bluz PHP Team |
4
|
|
|
* @link https://github.com/bluzphp/skeleton |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
declare(strict_types=1); |
8
|
|
|
|
9
|
|
|
namespace Application\Media; |
10
|
|
|
|
11
|
|
|
use Application\Users; |
12
|
|
|
use Bluz\Application\Exception\BadRequestException; |
13
|
|
|
use Bluz\Config\ConfigException; |
14
|
|
|
use Bluz\Proxy\Auth; |
15
|
|
|
use Bluz\Proxy\Config; |
16
|
|
|
use Image\Thumbnail; |
17
|
|
|
use Zend\Diactoros\UploadedFile; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Manager |
21
|
|
|
* |
22
|
|
|
* @category Application |
23
|
|
|
* @package Media |
24
|
|
|
* @author Anton Shevchuk |
25
|
|
|
*/ |
26
|
|
|
class Manager |
27
|
|
|
{ |
28
|
|
|
const THUMB_HEIGHT = 196; |
29
|
|
|
const THUMB_WIDTH = 196; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var Row |
33
|
|
|
*/ |
34
|
|
|
protected $media; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var UploadedFile |
38
|
|
|
*/ |
39
|
|
|
protected $file; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var string |
43
|
|
|
*/ |
44
|
|
|
protected $name; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var string |
48
|
|
|
*/ |
49
|
|
|
protected $publicPath; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var string |
53
|
|
|
*/ |
54
|
|
|
protected $uploadPath; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @param Row $media |
58
|
|
|
* @param UploadedFile $file |
59
|
|
|
* |
60
|
|
|
*/ |
61
|
|
|
public function __construct($media, $file) |
62
|
|
|
{ |
63
|
|
|
$this->media = $media; |
64
|
|
|
$this->media->module = $this->media->module ?: 'users'; |
65
|
|
|
$this->media->userId = $this->media->userId ?: Auth::getIdentity()->id ?: Users\Table::SYSTEM_USER; |
66
|
|
|
|
67
|
|
|
$this->file = $file; |
68
|
|
|
$this->name = $media->title ?? pathinfo($file->getClientFilename(), PATHINFO_FILENAME); |
69
|
|
|
|
70
|
|
|
$this->checkError(); |
71
|
|
|
$this->checkType(); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Move file to directory |
76
|
|
|
* |
77
|
|
|
* @param string $directory |
78
|
|
|
* |
79
|
|
|
* @return void |
80
|
|
|
* @throws ConfigException |
81
|
|
|
*/ |
82
|
|
|
public function moveToDir($directory) |
83
|
|
|
{ |
84
|
|
|
$uploadPath = Config::getModuleData('media', 'upload_path'); |
85
|
|
|
|
86
|
|
|
if (empty($uploadPath)) { |
87
|
|
|
throw new ConfigException('Upload path is not configured'); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
$fullPath = PATH_PUBLIC.'/'.$uploadPath.'/'.$directory; |
91
|
|
|
|
92
|
|
|
if (!is_dir($fullPath) && !@mkdir($fullPath, 0755, true)) { |
93
|
|
|
throw new ConfigException('Upload folder is not exists, please create it'); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
if (!is_writable($fullPath)) { |
97
|
|
|
throw new ConfigException('Upload folder is not writable'); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
$fileName = $this->getFileName($fullPath); |
101
|
|
|
|
102
|
|
|
$this->publicPath = $uploadPath.'/'.$directory.'/'.$fileName; |
103
|
|
|
$this->uploadPath = $fullPath.'/'.$fileName; |
104
|
|
|
|
105
|
|
|
$this->file->moveTo($this->uploadPath); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Create thumbnail |
110
|
|
|
* |
111
|
|
|
* @return string |
112
|
|
|
*/ |
113
|
|
|
public function createThumbnail() |
114
|
|
|
{ |
115
|
|
|
// set full path |
116
|
|
|
$image = new Thumbnail($this->getUploadPath()); |
117
|
|
|
$image->setHeight(self::THUMB_HEIGHT); |
118
|
|
|
$image->setWidth(self::THUMB_WIDTH); |
119
|
|
|
$thumb = $image->generate(); |
120
|
|
|
// crop full path |
121
|
|
|
$thumb = substr($thumb, strlen(PATH_PUBLIC) + 1); |
122
|
|
|
return $thumb; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Check Error code |
127
|
|
|
* |
128
|
|
|
* @return void |
129
|
|
|
* @throws BadRequestException |
130
|
|
|
*/ |
131
|
|
|
protected function checkError() |
132
|
|
|
{ |
133
|
|
|
// check upload errors |
134
|
|
|
if ($this->file->getError() !== UPLOAD_ERR_OK) { |
135
|
|
|
switch ($this->file->getError()) { |
136
|
|
|
case UPLOAD_ERR_NO_FILE: |
|
|
|
|
137
|
|
|
$message = __('Please choose file for upload'); |
138
|
|
|
break; |
139
|
|
|
case UPLOAD_ERR_INI_SIZE: |
|
|
|
|
140
|
|
|
$message = __( |
141
|
|
|
'The uploaded file size should be lower than %s', |
142
|
|
|
ini_get('upload_max_filesize') |
143
|
|
|
); |
144
|
|
|
break; |
145
|
|
|
default: |
146
|
|
|
$message = UploadedFile::ERROR_MESSAGES[$this->file->getError()]; |
147
|
|
|
} |
148
|
|
|
throw new BadRequestException($message); |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* checkType |
154
|
|
|
* |
155
|
|
|
* @return void |
156
|
|
|
* @throws BadRequestException |
157
|
|
|
*/ |
158
|
|
|
protected function checkType() |
159
|
|
|
{ |
160
|
|
|
// check files' types |
161
|
|
|
$allowTypes = ['image/png', 'image/jpg', 'image/jpeg', 'image/pjpeg', 'image/gif']; |
162
|
|
|
|
163
|
|
|
if (!in_array($this->file->getClientMediaType(), $allowTypes, true)) { |
164
|
|
|
throw new BadRequestException('Wrong file type'); |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Prepare File name for path |
170
|
|
|
* |
171
|
|
|
* @param string $path |
172
|
|
|
* |
173
|
|
|
* @return string |
174
|
|
|
*/ |
175
|
|
|
protected function getFileName($path) : string |
176
|
|
|
{ |
177
|
|
|
/** |
178
|
|
|
* Generate image name |
179
|
|
|
*/ |
180
|
|
|
$pathInfo = pathinfo($this->file->getClientFilename()); |
181
|
|
|
|
182
|
|
|
$fileName = strtolower($this->name ?? $pathInfo['filename']); |
183
|
|
|
$fileExt = strtolower($pathInfo['extension']); |
184
|
|
|
|
185
|
|
|
// Prepare filename |
186
|
|
|
$fileName = preg_replace('/[ _;:]+/', '-', $fileName); |
187
|
|
|
$fileName = preg_replace('/[^a-z0-9.-]+/i', '', $fileName); |
188
|
|
|
|
189
|
|
|
// If name is empty, generate it with current time |
190
|
|
|
if (empty($fileName)) { |
191
|
|
|
$fileName = date('Y-m-d-His'); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
// If file already exists, increment name |
195
|
|
|
$originFileName = $fileName; |
196
|
|
|
$counter = 0; |
197
|
|
|
while (file_exists($path .'/'.$fileName.'.'.$fileExt)) { |
198
|
|
|
$counter++; |
199
|
|
|
$fileName = $originFileName.'-'.$counter; |
200
|
|
|
} |
201
|
|
|
return $fileName.'.'.$fileExt; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* @return string |
206
|
|
|
*/ |
207
|
|
|
public function getPublicPath(): string |
208
|
|
|
{ |
209
|
|
|
return $this->publicPath; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* @return string |
214
|
|
|
*/ |
215
|
|
|
public function getUploadPath(): string |
216
|
|
|
{ |
217
|
|
|
return $this->uploadPath; |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
|
As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next
break
.There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.