Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Scanner 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 Scanner, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
51 | class Scanner extends BasicEmitter { |
||
52 | /** |
||
53 | * @var \OC\Files\Storage\Storage $storage |
||
54 | */ |
||
55 | protected $storage; |
||
56 | |||
57 | /** |
||
58 | * @var string $storageId |
||
59 | */ |
||
60 | protected $storageId; |
||
61 | |||
62 | /** |
||
63 | * @var \OC\Files\Cache\Cache $cache |
||
64 | */ |
||
65 | protected $cache; |
||
66 | |||
67 | /** |
||
68 | * @var boolean $cacheActive If true, perform cache operations, if false, do not affect cache |
||
69 | */ |
||
70 | protected $cacheActive; |
||
71 | |||
72 | /** |
||
73 | * @var bool $useTransactions whether to use transactions |
||
74 | */ |
||
75 | protected $useTransactions = true; |
||
76 | |||
77 | /** |
||
78 | * @var \OCP\Lock\ILockingProvider |
||
79 | */ |
||
80 | protected $lockingProvider; |
||
81 | |||
82 | const SCAN_RECURSIVE = true; |
||
83 | const SCAN_SHALLOW = false; |
||
84 | |||
85 | const REUSE_ETAG = 1; |
||
86 | const REUSE_SIZE = 2; |
||
87 | |||
88 | 890 | public function __construct(\OC\Files\Storage\Storage $storage) { |
|
95 | |||
96 | /** |
||
97 | * Whether to wrap the scanning of a folder in a database transaction |
||
98 | * On default transactions are used |
||
99 | * |
||
100 | * @param bool $useTransactions |
||
101 | */ |
||
102 | 4 | public function setUseTransactions($useTransactions) { |
|
105 | |||
106 | /** |
||
107 | * get all the metadata of a file or folder |
||
108 | * * |
||
109 | * |
||
110 | * @param string $path |
||
111 | * @return array an array of metadata of the file |
||
112 | */ |
||
113 | 875 | public function getData($path) { |
|
120 | |||
121 | /** |
||
122 | * scan a single file and store it in the cache |
||
123 | * |
||
124 | * @param string $file |
||
125 | * @param int $reuseExisting |
||
126 | * @param int $parentId |
||
127 | * @param array | null $cacheData existing data in the cache for the file to be scanned |
||
128 | * @param bool $lock set to false to disable getting an additional read lock during scanning |
||
129 | * @return array an array of metadata of the scanned file |
||
130 | * @throws \OC\ServerNotAvailableException |
||
131 | * @throws \OCP\Lock\LockedException |
||
132 | */ |
||
133 | 875 | public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true) { |
|
203 | |||
204 | 24 | protected function removeFromCache($path) { |
|
211 | |||
212 | /** |
||
213 | * @param string $path |
||
214 | * @param array $data |
||
215 | * @param int $fileId |
||
216 | * @return int the id of the added file |
||
217 | */ |
||
218 | 873 | protected function addToCache($path, $data, $fileId = -1) { |
|
232 | |||
233 | /** |
||
234 | * @param string $path |
||
235 | * @param array $data |
||
236 | * @param int $fileId |
||
237 | */ |
||
238 | 818 | protected function updateCache($path, $data, $fileId = -1) { |
|
249 | |||
250 | /** |
||
251 | * scan a folder and all it's children |
||
252 | * |
||
253 | * @param string $path |
||
254 | * @param bool $recursive |
||
255 | * @param int $reuse |
||
256 | * @param bool $lock set to false to disable getting an additional read lock during scanning |
||
257 | * @return array an array of the meta data of the scanned file or folder |
||
258 | */ |
||
259 | 869 | public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) { |
|
260 | 869 | View Code Duplication | if ($reuse === -1) { |
261 | 865 | $reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG; |
|
262 | 865 | } |
|
263 | 869 | View Code Duplication | if ($lock) { |
264 | 555 | $this->lockingProvider->acquireLock('scanner::' . $this->storageId . '::' . $path, ILockingProvider::LOCK_EXCLUSIVE); |
|
265 | 555 | $this->storage->acquireLock($path, ILockingProvider::LOCK_SHARED, $this->lockingProvider); |
|
266 | 869 | } |
|
267 | 869 | $data = $this->scanFile($path, $reuse, -1, null, $lock); |
|
268 | 846 | if ($data and $data['mimetype'] === 'httpd/unix-directory') { |
|
269 | 846 | $size = $this->scanChildren($path, $recursive, $reuse, $data, $lock); |
|
270 | 846 | $data['size'] = $size; |
|
271 | 869 | } |
|
272 | 555 | View Code Duplication | if ($lock) { |
273 | 555 | $this->storage->releaseLock($path, ILockingProvider::LOCK_SHARED, $this->lockingProvider); |
|
274 | 869 | $this->lockingProvider->releaseLock('scanner::' . $this->storageId . '::' . $path, ILockingProvider::LOCK_EXCLUSIVE); |
|
275 | } |
||
276 | return $data; |
||
277 | } |
||
278 | |||
279 | /** |
||
280 | * Get the children currently in the cache |
||
281 | * |
||
282 | * @param int $folderId |
||
283 | 846 | * @return array[] |
|
284 | 846 | */ |
|
285 | 846 | protected function getExistingChildren($folderId) { |
|
293 | |||
294 | /** |
||
295 | * Get the children from the storage |
||
296 | * |
||
297 | * @param string $folder |
||
298 | 846 | * @return string[] |
|
299 | 846 | */ |
|
300 | 846 | protected function getNewChildren($folder) { |
|
313 | |||
314 | /** |
||
315 | * scan all the files and folders in a folder |
||
316 | * |
||
317 | * @param string $path |
||
318 | * @param bool $recursive |
||
319 | * @param int $reuse |
||
320 | * @param array $folderData existing cache data for the folder to be scanned |
||
321 | * @param bool $lock set to false to disable getting an additional read lock during scanning |
||
322 | 846 | * @return int the size of the scanned folder or -1 if the size is unknown at this stage |
|
323 | 846 | */ |
|
324 | protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderData = null, $lock = true) { |
||
400 | |||
401 | /** |
||
402 | * check if the file should be ignored when scanning |
||
403 | * NOTE: files with a '.part' extension are ignored as well! |
||
404 | * prevents unfinished put requests to be scanned |
||
405 | * |
||
406 | * @param string $file |
||
407 | 992 | * @return boolean |
|
408 | 992 | */ |
|
409 | 35 | public static function isPartialFile($file) { |
|
419 | |||
420 | /** |
||
421 | 2 | * walk over any folders that are not fully scanned yet and scan them |
|
422 | 2 | */ |
|
423 | 2 | public function backgroundScan() { |
|
444 | |||
445 | /** |
||
446 | * Set whether the cache is affected by scan operations |
||
447 | * |
||
448 | * @param boolean $active The active state of the cache |
||
449 | */ |
||
450 | public function setCacheActive($active) { |
||
453 | } |
||
454 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.