Skip to content

Commit 04d4b5e

Browse files
committed
refactor(psalm): Bump psalm to level 1
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
1 parent 17503b0 commit 04d4b5e

7 files changed

Lines changed: 68 additions & 62 deletions

File tree

lib/AppInfo/Application.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535

3636
include_once __DIR__ . '/../../vendor/autoload.php';
3737

38+
/**
39+
* @psalm-api
40+
*/
3841
class Application extends App implements IBootstrap {
3942

4043
public const appID = 'sharelisting';

lib/Command/AbstractCommand.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,19 @@ public function configure(): void {
4545
);
4646
}
4747

48+
/**
49+
* @return array{0: string, 1: string, 2: string, 3: int}
50+
*/
4851
protected function getOptions(InputInterface $input): array {
52+
/** @var string $user */
4953
$user = $input->getOption('user');
54+
/** @var string $path */
5055
$path = $input->getOption('path');
56+
/** @var string $token */
5157
$token = $input->getOption('token');
52-
$filter = $this->sharesList->filterStringToInt($input->getOption('filter'));
58+
/** @var string $filter */
59+
$filter = $input->getOption('filter');
5360

54-
return [$user, $path, $token, $filter];
61+
return [$user, $path, $token, $this->sharesList->filterStringToInt($filter)];
5562
}
5663
}

lib/Controller/ApiController.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
use function iter\map;
3737
use function iter\toArray;
3838

39+
/**
40+
* @psalm-api
41+
*/
3942
class ApiController extends OCSController {
4043
public function __construct(
4144
string $appName,
@@ -67,15 +70,17 @@ public function getSharedSubfolders(string $path): DataResponse {
6770
$formattedShares = map(fn (IShare $share) => $this->sharesList->formatShare($share), $shares);
6871

6972
// remove current folder
70-
$filteredShares = filter(fn ($share) => $share['path'] !== $path, $formattedShares);
73+
$filteredShares = filter(fn (array $share): bool => $share['path'] !== $path, $formattedShares);
7174

7275
// sort directories first
7376
$sortedShares = toArray($filteredShares);
74-
usort($sortedShares, function ($a, $b) {
77+
usort($sortedShares, function (array $a, array $b): int {
78+
/** @var array{is_directory: bool, path: string} $a */
79+
/** @var array{is_directory: bool, path: string} $b */
7580
if ($a['is_directory'] && $b['is_directory']) {
7681
return strcmp($a['path'], $b['path']);
7782
}
78-
return $b['is_directory'] - $a['is_directory'];
83+
return (int)$b['is_directory'] - (int)$a['is_directory'];
7984
});
8085

8186
return new DataResponse($sortedShares);

lib/Listener/LoadSidebarScript.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
/**
3535
* @template-implements IEventListener<LoadSidebar>
3636
*/
37-
class LoadSidebarScript implements IEventListener {
37+
final class LoadSidebarScript implements IEventListener {
3838
public function handle(Event $event): void {
3939
if (!($event instanceof LoadSidebar)) {
4040
return;

lib/Service/ReportSender.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232
use Psr\Log\LoggerInterface;
3333
use Swaggest\JsonDiff\JsonDiff;
3434

35-
class ReportSender {
35+
final class ReportSender {
3636
protected const REPORT_NAME = ' - Shares report.';
3737

38+
/** @var array{url: string, fileName: string}|null */
3839
protected ?array $diffReport = null;
40+
/** @var array<string, array{url: string, data: string}> */
3941
protected array $reports = [];
4042

4143
public function __construct(
@@ -75,12 +77,10 @@ public function createReport(
7577

7678
$formats = ['json', 'csv'];
7779
$formatedDateTime = $dateTime->format('YmdHi');
78-
foreach ($formats as $key => $format) {
80+
$shares = iterator_to_array($this->sharesList->getFormattedShares($userId, $filter, $path, $token));
81+
foreach ($formats as $format) {
7982
$fileName = $formatedDateTime . self::REPORT_NAME . $format;
8083
if (!array_key_exists($fileName, $this->reports)) {
81-
if ($key === array_key_first($formats)) {
82-
$shares = iterator_to_array($this->sharesList->getFormattedShares($userId, $filter, $path, $token));
83-
}
8484
$reportFile = $folder->newFile($fileName);
8585
$data = $this->sharesList->getSerializedShares($shares, $format);
8686
$reportFile->putContent($data);
@@ -96,7 +96,7 @@ public function createReport(
9696
}
9797

9898
public function sendReport(string $recipient, \DateTimeImmutable $dateTime): void {
99-
$defaultLanguage = $this->config->getSystemValue('default_language', 'en');
99+
$defaultLanguage = $this->config->getSystemValueString('default_language', 'en');
100100
$userLanguages = $this->config->getUserValue($recipient, 'core', 'lang');
101101
$language = (!empty($userLanguages)) ? $userLanguages : $defaultLanguage;
102102

@@ -215,14 +215,16 @@ public function diff(
215215
$previousFile = $search[0];
216216
$previousFilename = $previousFile->getName();
217217
$previousDateTime = substr($previousFilename, 0, 12);
218-
$previousContent = json_decode($previousFile->getContent());
218+
/** @var list<object{id: string}> $previousContent */
219+
$previousContent = json_decode($previousFile->getContent(), flags: JSON_THROW_ON_ERROR);
219220
$previousContentWithId = [];
220221
foreach ($previousContent as $value) {
221222
$previousContentWithId[$value->id] = $value;
222223
}
223224

224225
$newFilename = array_keys($this->reports)[0];
225226
$newDateTime = substr($newFilename, 0, 12);
227+
/** @var list<object{id: string}> $newContent */
226228
$newContent = json_decode(array_values($this->reports)[0]['data']);
227229
$newContentWithId = [];
228230
foreach ($newContent as $value) {
@@ -243,7 +245,7 @@ public function diff(
243245
'modified' => $jsonDiff->getModifiedDiff()
244246
];
245247

246-
$reportFile->putContent(json_encode($res, JSON_PRETTY_PRINT));
248+
$reportFile->putContent(json_encode($res, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
247249

248250
$this->diffReport = [
249251
'url' => $this->url->linkToRouteAbsolute(

lib/Service/SharesList.php

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,12 @@
11
<?php
22

33
declare(strict_types=1);
4-
/**
5-
* @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
6-
*
7-
* @author Florent Poinsaut <florent@solution-libre.fr>
8-
* @author Roeland Jago Douma <roeland@famdouma.nl>
9-
* @author John Molakvoæ <skjnldsv@protonmail.com>
10-
*
11-
* @license GNU AGPL version 3 or any later version
12-
*
13-
* This program is free software: you can redistribute it and/or modify
14-
* it under the terms of the GNU Affero General Public License as
15-
* published by the Free Software Foundation, either version 3 of the
16-
* License, or (at your option) any later version.
17-
*
18-
* This program is distributed in the hope that it will be useful,
19-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21-
* GNU Affero General Public License for more details.
22-
*
23-
* You should have received a copy of the GNU Affero General Public License
24-
* along with this program. If not, see <http://www.gnu.org/licenses/>.
25-
*
26-
*/
4+
5+
// SPDX-FileCopyrightText: 2018 Nextcloud GmbH
6+
// SPDX-FileCopyrightText: 2022 Solution Libre SAS
7+
// SPDX-FileContributor: Roeland Jago Douma <roeland@famdouma.nl>
8+
// SPDX-FileContributor: Florent Poinsaut <florent@solution-libre.fr>
9+
// SPDX-FileContributor: John Molakvoæ <skjnldsv@protonmail.com>
2710

2811
namespace OCA\ShareListing\Service;
2912

@@ -33,7 +16,6 @@
3316
use OCP\Files\Folder;
3417
use OCP\Files\IRootFolder;
3518
use OCP\Files\NotFoundException;
36-
use OCP\IUserManager;
3719
use OCP\Share;
3820
use OCP\Share\IManager as ShareManager;
3921
use OCP\Share\IShare;
@@ -44,7 +26,7 @@
4426
use function iter\filter;
4527
use function iter\map;
4628

47-
class SharesList {
29+
final class SharesList {
4830

4931
public const FILTER_NONE = 0;
5032
public const FILTER_OWNER = 1;
@@ -56,11 +38,13 @@ class SharesList {
5638

5739
public function __construct(
5840
private ShareManager $shareManager,
59-
private IUserManager $userManager,
6041
private IRootFolder $rootFolder,
6142
) {
6243
}
6344

45+
/**
46+
* @return non-empty-array<IShare::TYPE_*>
47+
*/
6448
private function getShareTypes(): array {
6549
return [
6650
IShare::TYPE_USER,
@@ -71,11 +55,18 @@ private function getShareTypes(): array {
7155
];
7256
}
7357

58+
/**
59+
* @return Iterator<IShare>
60+
*/
7461
public function get(?string $userId, int $filter, ?string $path = null, ?string $token = null): Iterator {
62+
/** @var iterable<IShare> $shares */
7563
$shares = $this->getShares($userId);
7664

7765
// If path is set. Filter for the current user
7866
if ($path !== null) {
67+
if ($userId === null) {
68+
throw new \RuntimeException('Unable to query a path if no user is set.');
69+
}
7970
$userFolder = $this->rootFolder->getUserFolder($userId);
8071
try {
8172
$node = $userFolder->get($path);
@@ -137,8 +128,10 @@ public function get(?string $userId, int $filter, ?string $path = null, ?string
137128
* Get all shares. And filter them by being a subpath of the current path.
138129
* This allows us to build a list of subfiles/folder that are shared
139130
* as well
131+
* @return Iterator<IShare>
140132
*/
141133
public function getSub(string $userId, int $filter, string $path): Iterator {
134+
/** @var iterable<IShare> $shares */
142135
$shares = $this->shareManager->getAllShares();
143136

144137
// If path is set. Filter for the current user
@@ -150,7 +143,7 @@ public function getSub(string $userId, int $filter, string $path): Iterator {
150143
return new EmptyIterator();
151144
}
152145

153-
$shares = filter(function (IShare $share) use ($node) {
146+
$shares = filter(function (IShare $share) use ($node): bool {
154147
if ($node->getId() === $share->getNodeId()) {
155148
return false;
156149
}
@@ -161,30 +154,26 @@ public function getSub(string $userId, int $filter, string $path): Iterator {
161154
}, $shares);
162155

163156
if ($filter === self::FILTER_OWNER) {
164-
$shares = filter(fn (IShare $share) => $share->getShareOwner() === $userId, $shares);
157+
$shares = filter(fn (IShare $share): bool => $share->getShareOwner() === $userId, $shares);
165158
}
166159
if ($filter === self::FILTER_INITIATOR) {
167-
$shares = filter(fn (IShare $share) => $share->getSharedBy() === $userId, $shares);
160+
$shares = filter(fn (IShare $share): bool => $share->getSharedBy() === $userId, $shares);
168161
}
169162
if ($filter === self::FILTER_RECIPIENT) {
170163
// We can't check the recipient since this might be a group share etc. However you can't share to yourself
171-
$shares = filter(fn (IShare $share) => $share->getShareOwner() !== $userId && $share->getSharedBy() !== $userId, $shares);
164+
$shares = filter(fn (IShare $share): bool => $share->getShareOwner() !== $userId && $share->getSharedBy() !== $userId, $shares);
172165
}
173166

174-
$shares = filter(function (IShare $share) {
167+
return filter(function (IShare $share): bool {
175168
try {
176169
$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
177-
} catch (NoUserException) {
178-
return false;
179170
} catch (Throwable) {
180171
return false;
181172
}
182173
$nodes = $userFolder->getById($share->getNodeId());
183174

184175
return $nodes !== [];
185176
}, $shares);
186-
187-
return $shares;
188177
}
189178

190179
public function getFormattedShares(?string $userId = null, int $filter = self::FILTER_NONE, ?string $path = null, ?string $token = null): Iterator {
@@ -196,7 +185,8 @@ public function getFormattedShares(?string $userId = null, int $filter = self::F
196185
}
197186

198187
private function getShares(?string $userId): Iterator {
199-
if (empty($userId)) {
188+
if ($userId === null) {
189+
/** @var iterable<IShare> $shares */
200190
$shares = $this->shareManager->getAllShares();
201191
} else {
202192
$shareTypes = $this->getShareTypes();
@@ -232,12 +222,12 @@ public function formatShare(IShare $share): array {
232222
];
233223

234224
$nodes = $userFolder->getById($share->getNodeId());
235-
$node = array_shift($nodes);
236-
$data['path'] = $userFolder->getRelativePath($node->getPath());
237-
$data['name'] = $node->getName();
238-
$data['is_directory'] = $node->getType() === 'dir';
239-
240-
225+
if (!empty($nodes)) {
226+
$node = array_shift($nodes);
227+
$data['path'] = $userFolder->getRelativePath($node->getPath());
228+
$data['name'] = $node->getName();
229+
$data['is_directory'] = $node->getType() === 'dir';
230+
}
241231

242232
if ($share->getShareType() === IShare::TYPE_USER) {
243233
$data['type'] = 'user';
@@ -261,24 +251,23 @@ public function formatShare(IShare $share): array {
261251
$data['recipient'] = $share->getSharedWith();
262252
}
263253

264-
if ($share->getExpirationDate() !== null) {
265-
$data['expiration'] = $share->getExpirationDate()->format('Y-m-d H:i:s');
254+
$expirationDate = $share->getExpirationDate();
255+
if ($expirationDate !== null) {
256+
$data['expiration'] = $expirationDate->format('Y-m-d H:i:s');
266257
}
267258

268259
return $data;
269260
}
270261

271262
public function filterStringToInt(?string $filterString): int {
272-
$filter = match ($filterString) {
263+
return match ($filterString) {
273264
'owner' => SharesList::FILTER_OWNER,
274265
'initiator' => SharesList::FILTER_INITIATOR,
275266
'recipient' => SharesList::FILTER_RECIPIENT,
276267
'has-expiration' => SharesList::FILTER_HAS_EXPIRATION,
277268
'no-expiration' => SharesList::FILTER_NO_EXPIRATION,
278269
default => SharesList::FILTER_NONE,
279270
};
280-
281-
return $filter;
282271
}
283272

284273
public function getSerializedShares(array $shares, ?string $format = 'json'): string {

psalm.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0"?>
22
<psalm
3-
errorLevel="4"
3+
errorLevel="1"
44
resolveFromConfigFile="true"
55
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
66
xmlns="https://getpsalm.org/schema/config"

0 commit comments

Comments
 (0)