Total Complexity | 65 |
Total Lines | 376 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like FileInfo often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use FileInfo, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
40 | class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { |
||
41 | /** |
||
42 | * @var array $data |
||
43 | */ |
||
44 | private $data; |
||
45 | |||
46 | /** |
||
47 | * @var string $path |
||
48 | */ |
||
49 | private $path; |
||
50 | |||
51 | /** |
||
52 | * @var \OC\Files\Storage\Storage $storage |
||
53 | */ |
||
54 | private $storage; |
||
55 | |||
56 | /** |
||
57 | * @var string $internalPath |
||
58 | */ |
||
59 | private $internalPath; |
||
60 | |||
61 | /** |
||
62 | * @var \OCP\Files\Mount\IMountPoint |
||
63 | */ |
||
64 | private $mount; |
||
65 | |||
66 | /** |
||
67 | * @var IUser |
||
68 | */ |
||
69 | private $owner; |
||
70 | |||
71 | /** |
||
72 | * @var string[] |
||
73 | */ |
||
74 | private $childEtags = []; |
||
75 | |||
76 | /** |
||
77 | * @var IMountPoint[] |
||
78 | */ |
||
79 | private $subMounts = []; |
||
80 | |||
81 | private $subMountsUsed = false; |
||
82 | |||
83 | /** |
||
84 | * The size of the file/folder without any sub mount |
||
85 | * |
||
86 | * @var int |
||
87 | */ |
||
88 | private $rawSize = 0; |
||
89 | |||
90 | /** |
||
91 | * @param string|boolean $path |
||
92 | * @param Storage\Storage $storage |
||
93 | * @param string $internalPath |
||
94 | * @param array|ICacheEntry $data |
||
95 | * @param \OCP\Files\Mount\IMountPoint $mount |
||
96 | * @param \OCP\IUser|null $owner |
||
97 | */ |
||
98 | public function __construct($path, $storage, $internalPath, $data, $mount, $owner= null) { |
||
99 | $this->path = $path; |
||
|
|||
100 | $this->storage = $storage; |
||
101 | $this->internalPath = $internalPath; |
||
102 | $this->data = $data; |
||
103 | $this->mount = $mount; |
||
104 | $this->owner = $owner; |
||
105 | $this->rawSize = $this->data['size'] ?? 0; |
||
106 | } |
||
107 | |||
108 | public function offsetSet($offset, $value) { |
||
110 | } |
||
111 | |||
112 | public function offsetExists($offset) { |
||
113 | return isset($this->data[$offset]); |
||
114 | } |
||
115 | |||
116 | public function offsetUnset($offset) { |
||
117 | unset($this->data[$offset]); |
||
118 | } |
||
119 | |||
120 | public function offsetGet($offset) { |
||
121 | if ($offset === 'type') { |
||
122 | return $this->getType(); |
||
123 | } else if ($offset === 'etag') { |
||
124 | return $this->getEtag(); |
||
125 | } else if ($offset === 'size') { |
||
126 | return $this->getSize(); |
||
127 | } else if ($offset === 'mtime') { |
||
128 | return $this->getMTime(); |
||
129 | } elseif ($offset === 'permissions') { |
||
130 | return $this->getPermissions(); |
||
131 | } elseif (isset($this->data[$offset])) { |
||
132 | return $this->data[$offset]; |
||
133 | } else { |
||
134 | return null; |
||
135 | } |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * @return string |
||
140 | */ |
||
141 | public function getPath() { |
||
142 | return $this->path; |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * @return \OCP\Files\Storage |
||
147 | */ |
||
148 | public function getStorage() { |
||
149 | return $this->storage; |
||
150 | } |
||
151 | |||
152 | /** |
||
153 | * @return string |
||
154 | */ |
||
155 | public function getInternalPath() { |
||
156 | return $this->internalPath; |
||
157 | } |
||
158 | |||
159 | /** |
||
160 | * Get FileInfo ID or null in case of part file |
||
161 | * |
||
162 | * @return int|null |
||
163 | */ |
||
164 | public function getId() { |
||
165 | return isset($this->data['fileid']) ? (int) $this->data['fileid'] : null; |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * @return string |
||
170 | */ |
||
171 | public function getMimetype() { |
||
172 | return $this->data['mimetype']; |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * @return string |
||
177 | */ |
||
178 | public function getMimePart() { |
||
179 | return $this->data['mimepart']; |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * @return string |
||
184 | */ |
||
185 | public function getName() { |
||
186 | return isset($this->data['name']) ? $this->data['name'] : basename($this->getPath()); |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * @return string |
||
191 | */ |
||
192 | public function getEtag() { |
||
193 | $this->updateEntryfromSubMounts(); |
||
194 | if (count($this->childEtags) > 0) { |
||
195 | $combinedEtag = $this->data['etag'] . '::' . implode('::', $this->childEtags); |
||
196 | return md5($combinedEtag); |
||
197 | } else { |
||
198 | return $this->data['etag']; |
||
199 | } |
||
200 | } |
||
201 | |||
202 | /** |
||
203 | * @return int |
||
204 | */ |
||
205 | public function getSize($includeMounts = true) { |
||
206 | if ($includeMounts) { |
||
207 | $this->updateEntryfromSubMounts(); |
||
208 | return isset($this->data['size']) ? 0 + $this->data['size'] : 0; |
||
209 | } else { |
||
210 | return $this->rawSize; |
||
211 | } |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * @return int |
||
216 | */ |
||
217 | public function getMTime() { |
||
218 | $this->updateEntryfromSubMounts(); |
||
219 | return (int) $this->data['mtime']; |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * @return bool |
||
224 | */ |
||
225 | public function isEncrypted() { |
||
226 | return $this->data['encrypted']; |
||
227 | } |
||
228 | |||
229 | /** |
||
230 | * Return the currently version used for the HMAC in the encryption app |
||
231 | * |
||
232 | * @return int |
||
233 | */ |
||
234 | public function getEncryptedVersion() { |
||
235 | return isset($this->data['encryptedVersion']) ? (int) $this->data['encryptedVersion'] : 1; |
||
236 | } |
||
237 | |||
238 | /** |
||
239 | * @return int |
||
240 | */ |
||
241 | public function getPermissions() { |
||
242 | $perms = (int) $this->data['permissions']; |
||
243 | if (\OCP\Util::isSharingDisabledForUser() || ($this->isShared() && !\OC\Share\Share::isResharingAllowed())) { |
||
244 | $perms = $perms & ~\OCP\Constants::PERMISSION_SHARE; |
||
245 | } |
||
246 | return (int) $perms; |
||
247 | } |
||
248 | |||
249 | /** |
||
250 | * @return string \OCP\Files\FileInfo::TYPE_FILE|\OCP\Files\FileInfo::TYPE_FOLDER |
||
251 | */ |
||
252 | public function getType() { |
||
253 | if (!isset($this->data['type'])) { |
||
254 | $this->data['type'] = ($this->getMimetype() === 'httpd/unix-directory') ? self::TYPE_FOLDER : self::TYPE_FILE; |
||
255 | } |
||
256 | return $this->data['type']; |
||
257 | } |
||
258 | |||
259 | public function getData() { |
||
260 | return $this->data; |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * @param int $permissions |
||
265 | * @return bool |
||
266 | */ |
||
267 | protected function checkPermissions($permissions) { |
||
268 | return ($this->getPermissions() & $permissions) === $permissions; |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * @return bool |
||
273 | */ |
||
274 | public function isReadable() { |
||
275 | return $this->checkPermissions(\OCP\Constants::PERMISSION_READ); |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * @return bool |
||
280 | */ |
||
281 | public function isUpdateable() { |
||
282 | return $this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE); |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * Check whether new files or folders can be created inside this folder |
||
287 | * |
||
288 | * @return bool |
||
289 | */ |
||
290 | public function isCreatable() { |
||
291 | return $this->checkPermissions(\OCP\Constants::PERMISSION_CREATE); |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * @return bool |
||
296 | */ |
||
297 | public function isDeletable() { |
||
298 | return $this->checkPermissions(\OCP\Constants::PERMISSION_DELETE); |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * @return bool |
||
303 | */ |
||
304 | public function isShareable() { |
||
305 | return $this->checkPermissions(\OCP\Constants::PERMISSION_SHARE); |
||
306 | } |
||
307 | |||
308 | /** |
||
309 | * Check if a file or folder is shared |
||
310 | * |
||
311 | * @return bool |
||
312 | */ |
||
313 | public function isShared() { |
||
314 | $sid = $this->getStorage()->getId(); |
||
315 | if (!is_null($sid)) { |
||
316 | $sid = explode(':', $sid); |
||
317 | return ($sid[0] === 'shared'); |
||
318 | } |
||
319 | |||
320 | return false; |
||
321 | } |
||
322 | |||
323 | public function isMounted() { |
||
324 | $storage = $this->getStorage(); |
||
325 | if ($storage->instanceOfStorage('\OCP\Files\IHomeStorage')) { |
||
326 | return false; |
||
327 | } |
||
328 | $sid = $storage->getId(); |
||
329 | if (!is_null($sid)) { |
||
330 | $sid = explode(':', $sid); |
||
331 | return ($sid[0] !== 'home' and $sid[0] !== 'shared'); |
||
332 | } |
||
333 | |||
334 | return false; |
||
335 | } |
||
336 | |||
337 | /** |
||
338 | * Get the mountpoint the file belongs to |
||
339 | * |
||
340 | * @return \OCP\Files\Mount\IMountPoint |
||
341 | */ |
||
342 | public function getMountPoint() { |
||
343 | return $this->mount; |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * Get the owner of the file |
||
348 | * |
||
349 | * @return \OCP\IUser |
||
350 | */ |
||
351 | public function getOwner() { |
||
352 | return $this->owner; |
||
353 | } |
||
354 | |||
355 | /** |
||
356 | * @param IMountPoint[] $mounts |
||
357 | */ |
||
358 | public function setSubMounts(array $mounts) { |
||
360 | } |
||
361 | |||
362 | private function updateEntryfromSubMounts() { |
||
363 | if ($this->subMountsUsed) { |
||
364 | return; |
||
365 | } |
||
366 | $this->subMountsUsed = true; |
||
367 | foreach ($this->subMounts as $mount) { |
||
368 | $subStorage = $mount->getStorage(); |
||
369 | if ($subStorage) { |
||
370 | $subCache = $subStorage->getCache(''); |
||
371 | $rootEntry = $subCache->get(''); |
||
372 | $this->addSubEntry($rootEntry, $mount->getMountPoint()); |
||
373 | } |
||
374 | } |
||
375 | } |
||
376 | |||
377 | /** |
||
378 | * Add a cache entry which is the child of this folder |
||
379 | * |
||
380 | * Sets the size, etag and size to for cross-storage childs |
||
381 | * |
||
382 | * @param array|ICacheEntry $data cache entry for the child |
||
383 | * @param string $entryPath full path of the child entry |
||
384 | */ |
||
385 | public function addSubEntry($data, $entryPath) { |
||
386 | $this->data['size'] += isset($data['size']) ? $data['size'] : 0; |
||
387 | if (isset($data['mtime'])) { |
||
388 | $this->data['mtime'] = max($this->data['mtime'], $data['mtime']); |
||
389 | } |
||
390 | if (isset($data['etag'])) { |
||
391 | // prefix the etag with the relative path of the subentry to propagate etag on mount moves |
||
392 | $relativeEntryPath = substr($entryPath, strlen($this->getPath())); |
||
393 | // attach the permissions to propagate etag on permision changes of submounts |
||
394 | $permissions = isset($data['permissions']) ? $data['permissions'] : 0; |
||
395 | $this->childEtags[] = $relativeEntryPath . '/' . $data['etag'] . $permissions; |
||
396 | } |
||
397 | } |
||
398 | |||
399 | /** |
||
400 | * @inheritdoc |
||
401 | */ |
||
402 | public function getChecksum() { |
||
404 | } |
||
405 | |||
406 | public function getExtension(): string { |
||
407 | return pathinfo($this->getName(), PATHINFO_EXTENSION); |
||
408 | } |
||
409 | |||
410 | public function getCreationTime(): int { |
||
411 | return (int) $this->data['creation_time']; |
||
412 | } |
||
413 | |||
414 | public function getUploadTime(): int { |
||
415 | return (int) $this->data['upload_time']; |
||
416 | } |
||
417 | } |
||
418 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.