Completed
Pull Request — stable8.2 (#24893)
by Robin
10:29
created

Scanner::scan()   C

Complexity

Conditions 11
Paths 24

Size

Total Lines 42
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 11.968

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 30
nc 24
nop 1
dl 0
loc 42
ccs 24
cts 30
cp 0.8
crap 11.968
rs 5.2653
c 1
b 0
f 0

How to fix   Complexity   

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
 * @author Jörn Friedrich Dreyer <[email protected]>
4
 * @author Morris Jobke <[email protected]>
5
 * @author Olivier Paroz <[email protected]>
6
 * @author Robin Appelman <[email protected]>
7
 * @author Scrutinizer Auto-Fixer <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 *
10
 * @copyright Copyright (c) 2015, ownCloud, Inc.
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
27
namespace OC\Files\Utils;
28
29
use OC\Files\View;
30
use OC\Files\Cache\ChangePropagator;
31
use OC\Files\Cache\Cache;
32
use OC\Files\Filesystem;
33
use OC\ForbiddenException;
34
use OC\Hooks\PublicEmitter;
35
use OC\Lock\DBLockingProvider;
36
use OCP\Files\StorageNotAvailableException;
37
use OCP\ILogger;
38
39
/**
40
 * Class Scanner
41
 *
42
 * Hooks available in scope \OC\Utils\Scanner
43
 *  - scanFile(string $absolutePath)
44
 *  - scanFolder(string $absolutePath)
45
 *
46
 * @package OC\Files\Utils
47
 */
48
class Scanner extends PublicEmitter {
49
	/**
50
	 * @var string $user
51
	 */
52
	private $user;
53
54
	/**
55
	 * @var \OC\Files\Cache\ChangePropagator
56
	 */
57
	protected $propagator;
58
59
	/**
60
	 * @var \OCP\IDBConnection
61
	 */
62
	protected $db;
63
64
	/**
65
	 * @var ILogger
66
	 */
67
	protected $logger;
68
69
	/**
70
	 * @param string $user
71
	 * @param \OCP\IDBConnection $db
72
	 * @param ILogger $logger
73 8
	 */
74 8
	public function __construct($user, $db, ILogger $logger) {
75 8
		$this->logger = $logger;
76 8
		$this->user = $user;
77 8
		$this->propagator = new ChangePropagator(new View(''));
78 8
		$this->db = $db;
79
	}
80
81
	/**
82
	 * get all storages for $dir
83
	 *
84
	 * @param string $dir
85
	 * @return \OC\Files\Mount\MountPoint[]
86 2
	 */
87
	protected function getMounts($dir) {
88 2
		//TODO: move to the node based fileapi once that's done
89 2
		\OC_Util::tearDownFS();
90
		\OC_Util::setupFS($this->user);
91 2
92 2
		$mountManager = Filesystem::getMountManager();
93 2
		$mounts = $mountManager->findIn($dir);
94 2
		$mounts[] = $mountManager->find($dir);
95
		$mounts = array_reverse($mounts); //start with the mount of $dir
96 2
97
		return $mounts;
98
	}
99
100
	/**
101
	 * attach listeners to the scanner
102
	 *
103
	 * @param \OC\Files\Mount\MountPoint $mount
104 5
	 */
105 5
	protected function attachListener($mount) {
106 5
		$scanner = $mount->getStorage()->getScanner();
107
		$emitter = $this;
108 4
		$scanner->listen('\OC\Files\Cache\Scanner', 'scanFile', function ($path) use ($mount, $emitter) {
109 5
			$emitter->emit('\OC\Files\Utils\Scanner', 'scanFile', array($mount->getMountPoint() . $path));
110
		});
111 4
		$scanner->listen('\OC\Files\Cache\Scanner', 'scanFolder', function ($path) use ($mount, $emitter) {
112 5
			$emitter->emit('\OC\Files\Utils\Scanner', 'scanFolder', array($mount->getMountPoint() . $path));
113
		});
114 4
		$scanner->listen('\OC\Files\Cache\Scanner', 'postScanFile', function ($path) use ($mount, $emitter) {
115 5
			$emitter->emit('\OC\Files\Utils\Scanner', 'postScanFile', array($mount->getMountPoint() . $path));
116
		});
117 4
		$scanner->listen('\OC\Files\Cache\Scanner', 'postScanFolder', function ($path) use ($mount, $emitter) {
118 5
			$emitter->emit('\OC\Files\Utils\Scanner', 'postScanFolder', array($mount->getMountPoint() . $path));
119
		});
120 5
		// propagate etag and mtimes when files are changed or removed
121 5
		$propagator = $this->propagator;
122 4
		$propagatorListener = function ($path) use ($mount, $propagator) {
123 4
			$fullPath = Filesystem::normalizePath($mount->getMountPoint() . $path);
124 5
			$propagator->addChange($fullPath);
125 5
		};
126 5
		$scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', $propagatorListener);
127 5
		$scanner->listen('\OC\Files\Cache\Scanner', 'removeFromCache', $propagatorListener);
128
	}
129
130
	/**
131
	 * @param string $dir
132 1
	 */
133 1
	public function backgroundScan($dir) {
134 1
		$mounts = $this->getMounts($dir);
135 1
		foreach ($mounts as $mount) {
136
			if (is_null($mount->getStorage())) {
137
				continue;
138
			}
139 1
			// don't scan the root storage
140 1
			if ($mount->getStorage()->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') {
141
				continue;
142 1
			}
143 1
			$scanner = $mount->getStorage()->getScanner();
144 1
			$this->attachListener($mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $mount on line 135 can be null; however, OC\Files\Utils\Scanner::attachListener() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
145 1
			$scanner->backgroundScan();
146 1
		}
147 1
		$this->propagator->propagateChanges(time());
148
	}
149
150
	/**
151
	 * @param string $dir
152
	 * @throws \OC\ForbiddenException
153 7
	 */
154 7
	public function scan($dir = '') {
155 3
		if (!Filesystem::isValidPath($dir)) {
156
			throw new \InvalidArgumentException('Invalid path to scan');
157 4
		}
158 4
		$mounts = $this->getMounts($dir);
159 4
		foreach ($mounts as $mount) {
160
			if (is_null($mount->getStorage())) {
161
				continue;
162 4
			}
163
			$storage = $mount->getStorage();
164 4
			// if the home storage isn't writable then the scanner is run as the wrong user
165
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home') and
166 4
				(!$storage->isCreatable('') or !$storage->isCreatable('files'))
167
			) {
168
				throw new ForbiddenException();
169 4
			}
170 4
			$relativePath = $mount->getInternalPath($dir);
171 4
			$scanner = $storage->getScanner();
172 4
			$scanner->setUseTransactions(false);
173 4
			$this->attachListener($mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $mount on line 159 can be null; however, OC\Files\Utils\Scanner::attachListener() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
174 4
			$isDbLocking = \OC::$server->getLockingProvider() instanceof DBLockingProvider;
175 4
			if (!$isDbLocking) {
176 4
				$this->db->beginTransaction();
177
			}
178 4
			try {
179 4
				$scanner->scan($relativePath, \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE);
180
				$cache = $storage->getCache();
181
				if ($cache instanceof Cache) {
182
					// only re-calculate for the root folder we scanned, anything below that is taken care of by the scanner
183
					$cache->correctFolderSize($relativePath);
184 4
				}
185 4
			} catch (StorageNotAvailableException $e) {
186 4
				$this->logger->error('Storage ' . $storage->getId() . ' not available');
187 4
				$this->logger->logException($e);
188 4
				$this->emit('\OC\Files\Utils\Scanner', 'StorageNotAvailable', [$e]);
189 4
			}
190
			if (!$isDbLocking) {
191
				$this->db->commit();
192
			}
193
		}
194
		$this->propagator->propagateChanges(time());
195
	}
196
}
197
198