nextcloud /
server
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * @copyright Copyright (c) 2016, ownCloud, Inc. |
||
| 4 | * |
||
| 5 | * @author Björn Schießle <[email protected]> |
||
| 6 | * @author Morris Jobke <[email protected]> |
||
| 7 | * @author Roeland Jago Douma <[email protected]> |
||
| 8 | * |
||
| 9 | * @license AGPL-3.0 |
||
| 10 | * |
||
| 11 | * This code is free software: you can redistribute it and/or modify |
||
| 12 | * it under the terms of the GNU Affero General Public License, version 3, |
||
| 13 | * as published by the Free Software Foundation. |
||
| 14 | * |
||
| 15 | * This program is distributed in the hope that it will be useful, |
||
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 18 | * GNU Affero General Public License for more details. |
||
| 19 | * |
||
| 20 | * You should have received a copy of the GNU Affero General Public License, version 3, |
||
| 21 | * along with this program. If not, see <http://www.gnu.org/licenses/> |
||
| 22 | * |
||
| 23 | */ |
||
| 24 | |||
| 25 | namespace OCA\Files_Sharing; |
||
| 26 | |||
| 27 | use Doctrine\DBAL\Connection; |
||
| 28 | use OCP\IDBConnection; |
||
| 29 | use OC\Cache\CappedMemoryCache; |
||
| 30 | |||
| 31 | /** |
||
| 32 | * Class Migration |
||
| 33 | * |
||
| 34 | * @package OCA\Files_Sharing |
||
| 35 | * @group DB |
||
| 36 | */ |
||
| 37 | class Migration { |
||
| 38 | |||
| 39 | /** @var IDBConnection */ |
||
| 40 | private $connection; |
||
| 41 | |||
| 42 | /** @var array with all shares we already saw */ |
||
| 43 | private $shareCache; |
||
| 44 | |||
| 45 | /** @var string */ |
||
| 46 | private $table = 'share'; |
||
| 47 | |||
| 48 | public function __construct(IDBConnection $connection) { |
||
| 49 | $this->connection = $connection; |
||
| 50 | |||
| 51 | // We cache up to 10k share items (~20MB) |
||
| 52 | $this->shareCache = new CappedMemoryCache(10000); |
||
| 53 | } |
||
| 54 | |||
| 55 | /** |
||
| 56 | * move all re-shares to the owner in order to have a flat list of shares |
||
| 57 | * upgrade from oC 8.2 to 9.0 with the new sharing |
||
| 58 | */ |
||
| 59 | public function removeReShares() { |
||
| 60 | |||
| 61 | $stmt = $this->getReShares(); |
||
| 62 | |||
| 63 | $owners = []; |
||
| 64 | while($share = $stmt->fetch()) { |
||
| 65 | |||
| 66 | $this->shareCache[$share['id']] = $share; |
||
| 67 | |||
| 68 | $owners[$share['id']] = [ |
||
| 69 | 'owner' => $this->findOwner($share), |
||
| 70 | 'initiator' => $share['uid_owner'], |
||
| 71 | 'type' => $share['share_type'], |
||
| 72 | ]; |
||
| 73 | |||
| 74 | if (count($owners) === 1000) { |
||
| 75 | $this->updateOwners($owners); |
||
| 76 | $owners = []; |
||
| 77 | } |
||
| 78 | } |
||
| 79 | |||
| 80 | $stmt->closeCursor(); |
||
| 81 | |||
| 82 | if (count($owners)) { |
||
| 83 | $this->updateOwners($owners); |
||
| 84 | } |
||
| 85 | } |
||
| 86 | |||
| 87 | /** |
||
| 88 | * update all owner information so that all shares have an owner |
||
| 89 | * and an initiator for the upgrade from oC 8.2 to 9.0 with the new sharing |
||
| 90 | */ |
||
| 91 | public function updateInitiatorInfo() { |
||
| 92 | while (true) { |
||
| 93 | $shares = $this->getMissingInitiator(1000); |
||
| 94 | |||
| 95 | if (empty($shares)) { |
||
| 96 | break; |
||
| 97 | } |
||
| 98 | |||
| 99 | $owners = []; |
||
| 100 | foreach ($shares as $share) { |
||
| 101 | $owners[$share['id']] = [ |
||
| 102 | 'owner' => $share['uid_owner'], |
||
| 103 | 'initiator' => $share['uid_owner'], |
||
| 104 | 'type' => $share['share_type'], |
||
| 105 | ]; |
||
| 106 | } |
||
| 107 | $this->updateOwners($owners); |
||
| 108 | } |
||
| 109 | } |
||
| 110 | |||
| 111 | /** |
||
| 112 | * find the owner of a re-shared file/folder |
||
| 113 | * |
||
| 114 | * @param array $share |
||
| 115 | * @return array |
||
| 116 | */ |
||
| 117 | private function findOwner($share) { |
||
| 118 | $currentShare = $share; |
||
| 119 | while(!is_null($currentShare['parent'])) { |
||
| 120 | if (isset($this->shareCache[$currentShare['parent']])) { |
||
| 121 | $currentShare = $this->shareCache[$currentShare['parent']]; |
||
| 122 | } else { |
||
| 123 | $currentShare = $this->getShare((int)$currentShare['parent']); |
||
| 124 | $this->shareCache[$currentShare['id']] = $currentShare; |
||
| 125 | } |
||
| 126 | } |
||
| 127 | |||
| 128 | return $currentShare['uid_owner']; |
||
| 129 | } |
||
| 130 | |||
| 131 | /** |
||
| 132 | * Get $n re-shares from the database |
||
| 133 | * |
||
| 134 | * @param int $n The max number of shares to fetch |
||
|
0 ignored issues
–
show
|
|||
| 135 | * @return \Doctrine\DBAL\Driver\Statement |
||
| 136 | */ |
||
| 137 | View Code Duplication | private function getReShares() { |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. Loading history...
|
|||
| 138 | $query = $this->connection->getQueryBuilder(); |
||
| 139 | $query->select(['id', 'parent', 'uid_owner', 'share_type']) |
||
| 140 | ->from($this->table) |
||
| 141 | ->where($query->expr()->in( |
||
| 142 | 'share_type', |
||
| 143 | $query->createNamedParameter( |
||
| 144 | [ |
||
| 145 | \OCP\Share::SHARE_TYPE_USER, |
||
| 146 | \OCP\Share::SHARE_TYPE_GROUP, |
||
| 147 | \OCP\Share::SHARE_TYPE_LINK, |
||
| 148 | \OCP\Share::SHARE_TYPE_REMOTE, |
||
| 149 | ], |
||
| 150 | Connection::PARAM_INT_ARRAY |
||
| 151 | ) |
||
| 152 | )) |
||
| 153 | ->andWhere($query->expr()->in( |
||
| 154 | 'item_type', |
||
| 155 | $query->createNamedParameter( |
||
| 156 | ['file', 'folder'], |
||
| 157 | Connection::PARAM_STR_ARRAY |
||
| 158 | ) |
||
| 159 | )) |
||
| 160 | ->andWhere($query->expr()->isNotNull('parent')) |
||
| 161 | ->orderBy('id', 'asc'); |
||
| 162 | return $query->execute(); |
||
|
0 ignored issues
–
show
|
|||
| 163 | |||
| 164 | |||
| 165 | $shares = $result->fetchAll(); |
||
|
0 ignored issues
–
show
$shares = $result->fetchAll(); does not seem to be reachable.
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed. Unreachable code is most often the result of function fx() {
try {
doSomething();
return true;
}
catch (\Exception $e) {
return false;
}
return false;
}
In the above example, the last Loading history...
|
|||
| 166 | $result->closeCursor(); |
||
| 167 | |||
| 168 | $ordered = []; |
||
| 169 | foreach ($shares as $share) { |
||
| 170 | $ordered[(int)$share['id']] = $share; |
||
| 171 | } |
||
| 172 | |||
| 173 | return $ordered; |
||
|
0 ignored issues
–
show
The return type of
return $ordered; (array) is incompatible with the return type documented by OCA\Files_Sharing\Migration::getReShares of type Doctrine\DBAL\Driver\Statement.
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function Loading history...
|
|||
| 174 | } |
||
| 175 | |||
| 176 | /** |
||
| 177 | * Get $n re-shares from the database |
||
| 178 | * |
||
| 179 | * @param int $n The max number of shares to fetch |
||
| 180 | * @return array |
||
| 181 | */ |
||
| 182 | View Code Duplication | private function getMissingInitiator($n = 1000) { |
|
| 183 | $query = $this->connection->getQueryBuilder(); |
||
| 184 | $query->select(['id', 'uid_owner', 'share_type']) |
||
| 185 | ->from($this->table) |
||
| 186 | ->where($query->expr()->in( |
||
| 187 | 'share_type', |
||
| 188 | $query->createNamedParameter( |
||
| 189 | [ |
||
| 190 | \OCP\Share::SHARE_TYPE_USER, |
||
| 191 | \OCP\Share::SHARE_TYPE_GROUP, |
||
| 192 | \OCP\Share::SHARE_TYPE_LINK, |
||
| 193 | \OCP\Share::SHARE_TYPE_REMOTE, |
||
| 194 | ], |
||
| 195 | Connection::PARAM_INT_ARRAY |
||
| 196 | ) |
||
| 197 | )) |
||
| 198 | ->andWhere($query->expr()->in( |
||
| 199 | 'item_type', |
||
| 200 | $query->createNamedParameter( |
||
| 201 | ['file', 'folder'], |
||
| 202 | Connection::PARAM_STR_ARRAY |
||
| 203 | ) |
||
| 204 | )) |
||
| 205 | ->andWhere($query->expr()->isNull('uid_initiator')) |
||
| 206 | ->orderBy('id', 'asc') |
||
| 207 | ->setMaxResults($n); |
||
| 208 | $result = $query->execute(); |
||
| 209 | $shares = $result->fetchAll(); |
||
| 210 | $result->closeCursor(); |
||
| 211 | |||
| 212 | $ordered = []; |
||
| 213 | foreach ($shares as $share) { |
||
| 214 | $ordered[(int)$share['id']] = $share; |
||
| 215 | } |
||
| 216 | |||
| 217 | return $ordered; |
||
| 218 | } |
||
| 219 | |||
| 220 | /** |
||
| 221 | * get a specific share |
||
| 222 | * |
||
| 223 | * @param int $id |
||
| 224 | * @return array |
||
| 225 | */ |
||
| 226 | private function getShare($id) { |
||
| 227 | $query = $this->connection->getQueryBuilder(); |
||
| 228 | $query->select(['id', 'parent', 'uid_owner']) |
||
| 229 | ->from($this->table) |
||
| 230 | ->where($query->expr()->eq('id', $query->createNamedParameter($id))); |
||
| 231 | $result = $query->execute(); |
||
| 232 | $share = $result->fetchAll(); |
||
| 233 | $result->closeCursor(); |
||
| 234 | |||
| 235 | return $share[0]; |
||
| 236 | } |
||
| 237 | |||
| 238 | /** |
||
| 239 | * update database with the new owners |
||
| 240 | * |
||
| 241 | * @param array $owners |
||
| 242 | * @throws \Exception |
||
| 243 | */ |
||
| 244 | private function updateOwners($owners) { |
||
| 245 | |||
| 246 | $this->connection->beginTransaction(); |
||
| 247 | |||
| 248 | try { |
||
| 249 | |||
| 250 | foreach ($owners as $id => $owner) { |
||
| 251 | $query = $this->connection->getQueryBuilder(); |
||
| 252 | $query->update($this->table) |
||
| 253 | ->set('uid_owner', $query->createNamedParameter($owner['owner'])) |
||
| 254 | ->set('uid_initiator', $query->createNamedParameter($owner['initiator'])); |
||
| 255 | |||
| 256 | |||
| 257 | if ((int)$owner['type'] !== \OCP\Share::SHARE_TYPE_LINK) { |
||
| 258 | $query->set('parent', $query->createNamedParameter(null)); |
||
| 259 | } |
||
| 260 | |||
| 261 | $query->where($query->expr()->eq('id', $query->createNamedParameter($id))); |
||
| 262 | |||
| 263 | $query->execute(); |
||
| 264 | } |
||
| 265 | |||
| 266 | $this->connection->commit(); |
||
| 267 | |||
| 268 | } catch (\Exception $e) { |
||
| 269 | $this->connection->rollBack(); |
||
| 270 | throw $e; |
||
| 271 | } |
||
| 272 | |||
| 273 | } |
||
| 274 | |||
| 275 | } |
||
| 276 |
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.
Consider the following example. The parameter
$italyis not defined by the methodfinale(...).The most likely cause is that the parameter was removed, but the annotation was not.