Completed
Push — master ( cff413...7f9d4c )
by Peter
07:00
created

File::_set()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 59

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 5.0384

Importance

Changes 0
Metric Value
dl 0
loc 59
ccs 23
cts 26
cp 0.8846
rs 8.5833
c 0
b 0
f 0
cc 5
nc 12
nop 3
crap 5.0384

How to fix   Long Method   

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
/**
4
 * This software package is licensed under AGPL or Commercial license.
5
 *
6
 * @package maslosoft/mangan
7
 * @licence AGPL or Commercial
8
 * @copyright Copyright (c) Piotr Masełkowski <[email protected]>
9
 * @copyright Copyright (c) Maslosoft
10
 * @copyright Copyright (c) Others as mentioned in code
11
 * @link https://maslosoft.com/mangan/
12
 */
13
14
namespace Maslosoft\Mangan\Model;
15
16
use finfo;
17
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
18
use Maslosoft\Mangan\EmbeddedDocument;
19
use Maslosoft\Mangan\EntityManager;
20
use Maslosoft\Mangan\Events\Event;
21
use Maslosoft\Mangan\Exceptions\FileNotFoundException;
22
use Maslosoft\Mangan\Helpers\IdHelper;
23
use Maslosoft\Mangan\Interfaces\FileInterface;
24
use Maslosoft\Mangan\Mangan;
25
use MongoDB;
26
use MongoGridFSFile;
27
use MongoId;
28
29
/**
30
 * Class for storing embedded files
31
 * @since 2.0.1
32
 * @author Piotr
33
 */
34
class File extends EmbeddedDocument
35
{
36
37
	const TmpPrefix = 'tmp';
38
39
	/**
40
	 * NOTE: This is also in gridfs, here is added to avoid querying gridfs just to get filename
41
	 * @var string
42
	 */
43
	public $filename = '';
44
45
	/**
46
	 * Size in bytes NOTE: @see $filename
47
	 * @var int
48
	 */
49
	public $size = 0;
50
51
	/**
52
	 * Root document class
53
	 * @var string Class name
54
	 */
55
	public $rootClass = '';
56
57
	/**
58
	 * Root document ID
59
	 * @var MongoId
60
	 */
61
	public $rootId = '';
62
63
	/**
64
	 * NOTE: Same not as for $filename field
65
	 * @var string
66
	 */
67
	public $contentType = '';
68
69
	/**
70
	 * Mongo DB instance
71
	 * @var MongoDB
72
	 */
73
	private $_db = null;
74
75 6
	public function __construct($scenario = 'insert', $lang = '')
76
	{
77 6
		parent::__construct($scenario, $lang);
78 6
		$this->_id = new MongoId;
79 6
		$mangan = Mangan::fromModel($this);
80 6
		$this->_db = $mangan->getDbInstance();
81 6
	}
82
83 6
	public function setOwner(AnnotatedInterface $owner = null)
84
	{
85 6
		parent::setOwner($owner);
86 6
		$root = $this->getRoot();
87
		$onAfterDelete = function()
88
		{
89 2
			$this->_onAfterDelete();
90 6
		};
91 6
		$onAfterDelete->bindTo($this);
92 6
		Event::on($root, EntityManager::EventAfterDelete, $onAfterDelete);
93 6
	}
94
95
	public function getId()
96
	{
97
		if (!$this->_id instanceof MongoId)
98
		{
99
			$this->_id = new MongoId($this->_id);
100
		}
101
		return $this->_id;
102
	}
103
104
	/**
105
	 * Get file from mongo grid
106
	 * @return MongoGridFSFile
107
	 */
108 3
	public function get()
109
	{
110 3
		return $this->_get();
111
	}
112
113
	/**
114
	 * Send file to browser
115
	 */
116
	public function send()
117
	{
118
		$this->_send($this->_get());
119
	}
120
121
	/**
122
	 * Stream file to browser
123
	 */
124
	public function stream()
125
	{
126
		$this->_stream($this->_get());
127
	}
128
129
	/**
130
	 * Set file data
131
	 * @param FileInterface|string $file
132
	 */
133 5
	public function set($file)
134
	{
135 5
		if ($file instanceof FileInterface)
136
		{
137 1
			$tempName = $file->getTempName();
138 1
			$fileName = $file->getFileName();
139
		}
140
		else
141
		{
142 4
			$tempName = $file;
143 4
			$fileName = $file;
144
		}
145 5
		$this->_set($tempName, $fileName);
146 5
	}
147
148
	/**
149
	 * Get file with optional criteria params
150
	 * @param mixed[] $params
151
	 * @return MongoGridFSFile
152
	 */
153 5
	protected function _get($params = [])
154
	{
155
		$criteria = [
156 5
			'parentId' => $this->_id,
157
			'isTemp' => false
158
		];
159 5
		$conditions = array_merge($criteria, $params);
160 5
		if (!$conditions['isTemp'])
161
		{
162 5
			return $this->_db->getGridFS()->findOne($conditions);
163
		}
164
		else
165
		{
166 2
			return $this->_db->getGridFS(self::TmpPrefix)->findOne($conditions);
167
		}
168
	}
169
170
	/**
171
	 * Send file to the browser
172
	 * @param MongoGridFSFile $file
173
	 */
174
	protected function _send(MongoGridFSFile $file = null)
175
	{
176
		if (null === $file)
177
		{
178
			throw new FileNotFoundException('File not found');
179
		}
180
		$meta = (object) $file->file;
181
		header(sprintf('Content-Length: %d', $file->getSize()));
182
		header(sprintf('Content-Type: %s', $meta->contentType));
183
		header(sprintf('ETag: %s', $meta->md5));
184
		header(sprintf('Last-Modified: %s', gmdate('D, d M Y H:i:s \G\M\T', $meta->uploadDate->sec)));
185
		header(sprintf('Content-Disposition: filename="%s"', basename($meta->filename)));
186
187
		// Cache it
188
		header('Pragma: public');
189
		header('Cache-Control: max-age=86400');
190
		header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + 86400));
191
		echo $file->getBytes();
192
		// Exit application after sending file
193
		exit;
194
	}
195
196
	protected function _stream(MongoGridFSFile $file)
197
	{
198
		$meta = (object) $file->file;
199
		if (ob_get_length())
200
		{
201
			ob_end_clean();
202
		}
203
		header(sprintf('Content-Length: %d', $file->getSize()));
204
		header(sprintf('Content-Type: %s', $meta->contentType));
205
		header(sprintf('ETag: %s', $meta->md5));
206
		header(sprintf('Last-Modified: %s', gmdate('D, d M Y H:i:s \G\M\T', $meta->uploadDate->sec)));
207
		header(sprintf('Content-Disposition: filename="%s"', basename($meta->filename)));
208
		// Cache it
209
		header('Pragma: public');
210
		header('Cache-Control: max-age=86400');
211
		header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + 86400));
212
		$stream = $file->getResource();
213
214
		while (!feof($stream))
215
		{
216
			echo fread($stream, 8192);
217
			if (ob_get_length())
218
			{
219
				ob_flush();
220
			}
221
			flush();
222
		}
223
		// Exit application after stream completed
224
		exit;
225
	}
226
227
	/**
228
	 * Set file with optional criteria params
229
	 * @param string $tempName
230
	 * @param string $fileName
231
	 * @param mixed[] $params
232
	 */
233 5
	protected function _set($tempName, $fileName, $params = [])
234
	{
235 5
		$info = new finfo(FILEINFO_MIME);
236 5
		$mime = $info->file($tempName);
237
		/**
238
		 * TODO Check if root data is saved corectly
239
		 */
240 5
		if (!$this->getRoot()->_id instanceof MongoId)
241
		{
242
			// Assume string id
243
			if (IdHelper::isId($this->getRoot()->_id))
244
			{
245
				// Convert existing string id to MongoId
246
				$this->getRoot()->_id = new MongoId((string) $this->getRoot()->_id);
247
			}
248
			else
249
			{
250
				// Set new id now
251
				$this->getRoot()->_id = new MongoId;
252
			}
253
		}
254 5
		$rootId = $this->getRoot()->_id;
255
		$data = [
256 5
			'_id' => new MongoId(),
257 5
			'parentId' => $this->_id,
258 5
			'rootClass' => get_class($this->getRoot()),
259 5
			'rootId' => $rootId,
260 5
			'filename' => $fileName,
261 5
			'contentType' => $mime,
262
			'isTemp' => false
263
		];
264
265 5
		$this->filename = $fileName;
266 5
		$this->contentType = $mime;
267 5
		$this->size = filesize($tempName);
268 5
		$params = array_merge($data, $params);
269
270
		// Replace existing file, remove previous
271 5
		if (!$params['isTemp'])
272
		{
273
			$oldFiles = [
274 5
				'parentId' => $this->_id
275
			];
276 5
			$this->_db->getGridFS()->remove($oldFiles);
277 5
			$this->_db->getGridFS(self::TmpPrefix)->remove($oldFiles);
278
		}
279
280
		// Store new file
281 5
		if (!$params['isTemp'])
282
		{
283
			// In main storage
284 5
			$this->_db->getGridFS()->put($tempName, $params);
285
		}
286
		else
287
		{
288
			// In temporary gfs
289 2
			$this->_db->getGridFS(self::TmpPrefix)->put($tempName, $params);
290
		}
291 5
	}
292
293
	/**
294
	 * This is fired after delete to remove chunks from gridfs
295
	 */
296 2
	protected function _onAfterDelete()
297
	{
298
		$criteria = [
299 2
			'parentId' => $this->_id
300
		];
301 2
		$this->_db->getGridFS()->remove($criteria);
302 2
		$this->_db->getGridFS(self::TmpPrefix)->remove($criteria);
303 2
	}
304
305
}
306