From c03189f654e52e509edaae177e3ebcb4325d5525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 28 Nov 2023 17:10:19 +0100 Subject: [PATCH 1/5] Update Psalm to 5.9 to sync with server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- composer.json | 2 +- composer.lock | 14 +++++++------- psalm.xml | 2 ++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 90433a171..f92743bd8 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "sabre/dav": "^4.1", "sabre/xml": "^2.2", "symfony/event-dispatcher": "^5.3.11", - "psalm/phar": "^4.10", + "psalm/phar": "^5.9", "nextcloud/coding-standard": "^1.0", "nextcloud/ocp": "dev-stable28" }, diff --git a/composer.lock b/composer.lock index 731b08c58..7f75e67b6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f1c909c8a12d20c275c8c7ef0144d140", + "content-hash": "256dab986f729e0bcc7941ebf632cc7e", "packages": [ { "name": "php-parallel-lint/php-parallel-lint", @@ -922,16 +922,16 @@ }, { "name": "psalm/phar", - "version": "4.30.0", + "version": "5.18.0", "source": { "type": "git", "url": "https://github.com/psalm/phar.git", - "reference": "33723713902e1345904a5c9064ef7848bee0d490" + "reference": "a78b5c2e8860c3b4242c63bc0864621278705f9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/phar/zipball/33723713902e1345904a5c9064ef7848bee0d490", - "reference": "33723713902e1345904a5c9064ef7848bee0d490", + "url": "https://api.github.com/repos/psalm/phar/zipball/a78b5c2e8860c3b4242c63bc0864621278705f9a", + "reference": "a78b5c2e8860c3b4242c63bc0864621278705f9a", "shasum": "" }, "require": { @@ -951,9 +951,9 @@ "description": "Composer-based Psalm Phar", "support": { "issues": "https://github.com/psalm/phar/issues", - "source": "https://github.com/psalm/phar/tree/4.30.0" + "source": "https://github.com/psalm/phar/tree/5.18.0" }, - "time": "2022-11-06T20:41:58+00:00" + "time": "2023-12-16T09:41:14+00:00" }, { "name": "psr/clock", diff --git a/psalm.xml b/psalm.xml index 0f14f1e62..7a2937cc4 100644 --- a/psalm.xml +++ b/psalm.xml @@ -6,6 +6,8 @@ xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" errorBaseline="tests/psalm-baseline.xml" + findUnusedBaselineEntry="true" + findUnusedCode="false" > From 7554e637e4da27479aae1eece83ac57da990cac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 28 Nov 2023 18:10:06 +0100 Subject: [PATCH 2/5] Fix stubs and fix psalm spotted issues throughout the code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/Command/ListCommand.php | 3 +- lib/Controller/FolderController.php | 4 +- lib/Folder/FolderManager.php | 2 +- .../CircleDestroyedEventListener.php | 3 + .../LoadAdditionalScriptsListener.php | 5 + lib/Versions/VersionsBackend.php | 24 +- tests/psalm-baseline.xml | 46 +- tests/stub.phpstub | 1049 ++++++++++++----- 8 files changed, 813 insertions(+), 323 deletions(-) diff --git a/lib/Command/ListCommand.php b/lib/Command/ListCommand.php index 8ff0cac3f..f8bd8bd84 100644 --- a/lib/Command/ListCommand.php +++ b/lib/Command/ListCommand.php @@ -35,6 +35,7 @@ use Symfony\Component\Console\Output\OutputInterface; class ListCommand extends Base { + /** @var array */ public const PERMISSION_NAMES = [ Constants::PERMISSION_READ => 'read', Constants::PERMISSION_UPDATE => 'write', @@ -132,7 +133,7 @@ private function permissionsToString(int $permissions): string { if ($permissions === 0) { return 'none'; } - return implode(', ', array_filter(self::PERMISSION_NAMES, function ($possiblePermission) use ($permissions) { + return implode(', ', array_filter(self::PERMISSION_NAMES, function (int $possiblePermission) use ($permissions) { return $possiblePermission & $permissions; }, ARRAY_FILTER_USE_KEY)); } diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index f17a849bf..56e7989d1 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -88,8 +88,8 @@ private function filterNonAdminFolder(array $folder): ?array { } /** - * @param array{id: mixed, mount_point: mixed, groups: array, quota: int, size: int, acl: bool} $folder - * @return array{id: mixed, mount_point: mixed, groups:array, group_details: array|mixed, quota: int, size: int, acl: bool} + * @param array{acl: bool, groups: array, id: int, manage: array, mount_point: mixed, quota: int, size: int} $folder + * @return array{acl: bool, group_details: array, groups: array, id: int, manage: array, mount_point: mixed, quota: int, size: int} */ private function formatFolder(array $folder): array { // keep compatibility with the old 'groups' field diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php index 91c394a40..1bdd5778d 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -359,7 +359,7 @@ private function getAllApplicable(): array { * @param CirclesQueryHelper|null $queryHelper * @param string|null $entityId the type of the entity * - * @return array{displayname?: string, id?: string, type?: "group"|"user"|"circle"} + * @return array{displayName: string, permissions: int, type: 'circle'|'group'} */ private function generateApplicableMapEntry( array $row, diff --git a/lib/Listeners/CircleDestroyedEventListener.php b/lib/Listeners/CircleDestroyedEventListener.php index 9b1757392..c0b67fe3f 100644 --- a/lib/Listeners/CircleDestroyedEventListener.php +++ b/lib/Listeners/CircleDestroyedEventListener.php @@ -31,6 +31,9 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; +/** + * @template-implements IEventListener + */ class CircleDestroyedEventListener implements IEventListener { public function __construct( private FolderManager $folderManager, diff --git a/lib/Listeners/LoadAdditionalScriptsListener.php b/lib/Listeners/LoadAdditionalScriptsListener.php index b36033979..85c91137a 100644 --- a/lib/Listeners/LoadAdditionalScriptsListener.php +++ b/lib/Listeners/LoadAdditionalScriptsListener.php @@ -27,7 +27,12 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; +use OCA\Files\Event\LoadAdditionalScriptsEvent; +use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent; +/** + * @template-implements IEventListener + */ class LoadAdditionalScriptsListener implements IEventListener { public function handle(Event $event): void { \OCP\Util::addScript('groupfolders', 'groupfolders-files'); diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index 743e0a7f1..6235c0639 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -59,8 +59,8 @@ public function useBackendForStorage(IStorage $storage): bool { return true; } - public function getVersionsForFile(IUser $user, FileInfo $fileInfo): array { - $mount = $fileInfo->getMountPoint(); + public function getVersionsForFile(IUser $user, FileInfo $file): array { + $mount = $file->getMountPoint(); if (!($mount instanceof GroupMountPoint)) { return []; } @@ -68,9 +68,9 @@ public function getVersionsForFile(IUser $user, FileInfo $fileInfo): array { try { $folderId = $mount->getFolderId(); /** @var Folder $versionsFolder */ - $versionsFolder = $this->getVersionsFolder($mount->getFolderId())->get((string)$fileInfo->getId()); + $versionsFolder = $this->getVersionsFolder($mount->getFolderId())->get((string)$file->getId()); - $versions = $this->getVersionsForFileFromDB($fileInfo, $user, $folderId); + $versions = $this->getVersionsForFileFromDB($file, $user, $folderId); // Early exit if we find any version in the database. // Else we continue to populate the DB from what's on disk. @@ -80,10 +80,10 @@ public function getVersionsForFile(IUser $user, FileInfo $fileInfo): array { // Insert the entry in the DB for the current version. $versionEntity = new GroupVersionEntity(); - $versionEntity->setFileId($fileInfo->getId()); - $versionEntity->setTimestamp($fileInfo->getMTime()); - $versionEntity->setSize($fileInfo->getSize()); - $versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfo->getMimetype())); + $versionEntity->setFileId($file->getId()); + $versionEntity->setTimestamp($file->getMTime()); + $versionEntity->setSize($file->getSize()); + $versionEntity->setMimetype($this->mimeTypeLoader->getId($file->getMimetype())); $versionEntity->setDecodedMetadata([]); $this->groupVersionsMapper->insert($versionEntity); @@ -95,12 +95,12 @@ public function getVersionsForFile(IUser $user, FileInfo $fileInfo): array { } $versionEntity = new GroupVersionEntity(); - $versionEntity->setFileId($fileInfo->getId()); + $versionEntity->setFileId($file->getId()); // HACK: before this commit, versions were created with the current timestamp instead of the version's mtime. // This means that the name of some versions is the exact mtime of the next version. This behavior is now fixed. // To prevent occasional conflicts between the last version and the current one, we decrement the last version mtime. $mtime = (int)$version->getName(); - if ($mtime === $fileInfo->getMTime()) { + if ($mtime === $file->getMTime()) { $versionEntity->setTimestamp($mtime - 1); $version->move($version->getParent()->getPath() . '/' . ($mtime - 1)); } else { @@ -108,12 +108,12 @@ public function getVersionsForFile(IUser $user, FileInfo $fileInfo): array { } $versionEntity->setSize($version->getSize()); // Use the main file mimetype for this initialization as the original mimetype is unknown. - $versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfo->getMimetype())); + $versionEntity->setMimetype($this->mimeTypeLoader->getId($file->getMimetype())); $versionEntity->setDecodedMetadata([]); $this->groupVersionsMapper->insert($versionEntity); } - return $this->getVersionsForFileFromDB($fileInfo, $user, $folderId); + return $this->getVersionsForFileFromDB($file, $user, $folderId); } catch (NotFoundException $e) { return []; } diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml index dd0f8599f..63b8ea0a4 100644 --- a/tests/psalm-baseline.xml +++ b/tests/psalm-baseline.xml @@ -1,52 +1,18 @@ - + - + filemtime hash - + IteratorDirectory - - getDirectoryContent - - - parent::getMetaData($path) - parent::writeStream($path, $stream, $size) - - - - - BeforeTemplateRenderedEvent - CircleDestroyedEvent - - - - - array{id: mixed, mount_point: mixed, groups: array<empty, empty>|mixed, quota: int, size: int|mixed, acl: bool}|false - - - - - $this->__call(__FUNCTION__, func_get_args()) - $this->__call(__FUNCTION__, func_get_args()) - - - - - parent::getMetaData($path) - - - - - parent::__construct($backend, $originalLocation, $deletedTime, $trashPath, $fileInfo, $user) - - - self::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'] === -1 - self::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'] === -1 + + + diff --git a/tests/stub.phpstub b/tests/stub.phpstub index 6fa7f7b27..53ee63855 100644 --- a/tests/stub.phpstub +++ b/tests/stub.phpstub @@ -75,135 +75,188 @@ namespace OCA\Files_Trashbin\Trash { public function getTitle(): string; } - class TrashItem implements ITrashItem { - public function getTrashBackend(): ITrashBackend { - throw new \Exception('stub'); + class TrashItem implements \OCA\Files_Trashbin\Trash\ITrashItem + { + /** @var ITrashBackend */ + private $backend; + /** @var string */ + private $orignalLocation; + /** @var int */ + private $deletedTime; + /** @var string */ + private $trashPath; + /** @var FileInfo */ + private $fileInfo; + /** @var IUser */ + private $user; + public function __construct(\OCA\Files_Trashbin\Trash\ITrashBackend $backend, string $originalLocation, int $deletedTime, string $trashPath, \OCP\Files\FileInfo $fileInfo, \OCP\IUser $user) + { } - - public function getOriginalLocation(): string { - throw new \Exception('stub'); + public function getTrashBackend() : \OCA\Files_Trashbin\Trash\ITrashBackend + { } - - public function getDeletedTime(): int { - throw new \Exception('stub'); + public function getOriginalLocation() : string + { } - - public function getTrashPath(): string { - throw new \Exception('stub'); + public function getDeletedTime() : int + { } - - public function isRootItem(): bool { - throw new \Exception('stub'); + public function getTrashPath() : string + { } - - public function getUser(): IUser { - throw new \Exception('stub'); + public function isRootItem() : bool + { } - - public function getEtag() { + public function getUser() : \OCP\IUser + { } - - public function getId() { + public function getEtag() + { } - - public function getSize($includeMounts = true) { - throw new \Exception('stub'); + public function getSize($includeMounts = true) + { } - - public function getMtime() { - throw new \Exception('stub'); + public function getMtime() + { } - - public function getName() { - throw new \Exception('stub'); + public function getName() + { } - - public function getInternalPath() { - throw new \Exception('stub'); + public function getInternalPath() + { } - - public function getPath() { - throw new \Exception('stub'); + public function getPath() + { } - - public function getMimetype() { - throw new \Exception('stub'); + public function getMimetype() + { } - - public function getMimePart() { - throw new \Exception('stub'); + public function getMimePart() + { } - - public function getStorage() { - throw new \Exception('stub'); + public function getStorage() + { } - - public function isEncrypted() { - throw new \Exception('stub'); + public function getId() + { } - - public function getPermissions() { - throw new \Exception('stub'); + public function isEncrypted() + { } - - public function getType() { - throw new \Exception('stub'); + public function getPermissions() + { } - - public function isReadable() { - throw new \Exception('stub'); + public function getType() + { } - - public function isUpdateable() { - throw new \Exception('stub'); + public function isReadable() + { } - - public function isCreatable() { - throw new \Exception('stub'); + public function isUpdateable() + { } - - public function isDeletable() { - throw new \Exception('stub'); + public function isCreatable() + { } - - public function isShareable() { - throw new \Exception('stub'); + public function isDeletable() + { } - - public function isShared() { - throw new \Exception('stub'); + public function isShareable() + { } - - public function isMounted() { - throw new \Exception('stub'); + public function isShared() + { } - - public function getMountPoint() { - throw new \Exception('stub'); + public function isMounted() + { } - - public function getOwner() { - throw new \Exception('stub'); + public function getMountPoint() + { } - - public function getChecksum() { - throw new \Exception('stub'); + public function getOwner() + { } - - public function getExtension(): string { - throw new \Exception('stub'); + public function getChecksum() + { } - - public function getTitle(): string { - throw new \Exception('stub'); + public function getExtension() : string + { + } + public function getTitle() : string + { } + public function getCreationTime() : int + { + } + public function getUploadTime() : int + { + } + public function getParentId() : int + { + } + /** + * @inheritDoc + * @return array + */ + public function getMetadata() : array + { + } + } +} - public function getCreationTime(): int { - throw new \Exception('stub'); +namespace OCA\Files\Event { + /** + * This event is triggered when the files app is rendered. + * It can be used to add additional scripts to the files app. + * + * @since 17.0.0 + */ + class LoadAdditionalScriptsEvent extends \OCP\EventDispatcher\Event + { + private $hiddenFields = []; + public function addHiddenField(string $name, string $value) : void + { + } + public function getHiddenFields() : array + { } + } +} - public function getUploadTime(): int { - throw new \Exception('stub'); +namespace OCA\Files_Sharing\Event { + /** + * Emitted before the rendering step of the public share page happens. The event + * holds a flag that specifies if it is the authentication page of a public share. + * + * @since 20.0.0 + */ + class BeforeTemplateRenderedEvent extends \OCP\EventDispatcher\Event + { + /** + * @since 20.0.0 + */ + public const SCOPE_PUBLIC_SHARE_AUTH = 'publicShareAuth'; + /** @var IShare */ + private $share; + /** @var string|null */ + private $scope; + /** + * @since 20.0.0 + */ + public function __construct(\OCP\Share\IShare $share, ?string $scope = null) + { + } + /** + * @since 20.0.0 + */ + public function getShare() : \OCP\Share\IShare + { + } + /** + * @since 20.0.0 + */ + public function getScope() : ?string + { } } } @@ -236,28 +289,66 @@ namespace OCA\Files_Versions\Versions { use OCP\Files\Storage\IStorage; use OCP\IUser; - interface IVersionBackend { - public function useBackendForStorage(IStorage $storage): bool; - + /** + * @since 15.0.0 + */ + interface IVersionBackend + { /** - * @return IVersion[] - */ - public function getVersionsForFile(IUser $user, FileInfo $file): array; - - public function createVersion(IUser $user, FileInfo $file); - - public function rollback(IVersion $version); - + * Whether or not this version backend should be used for a storage + * + * If false is returned then the next applicable backend will be used + * + * @param IStorage $storage + * @return bool + * @since 17.0.0 + */ + public function useBackendForStorage(\OCP\Files\Storage\IStorage $storage) : bool; /** - * @return resource|false - * @throws NotFoundException - */ - public function read(IVersion $version); - + * Get all versions for a file + * + * @param IUser $user + * @param FileInfo $file + * @return IVersion[] + * @since 15.0.0 + */ + public function getVersionsForFile(\OCP\IUser $user, \OCP\Files\FileInfo $file) : array; /** - * @param int|string $revision - */ - public function getVersionFile(IUser $user, FileInfo $sourceFile, $revision): ?File; + * Create a new version for a file + * + * @param IUser $user + * @param FileInfo $file + * @since 15.0.0 + */ + public function createVersion(\OCP\IUser $user, \OCP\Files\FileInfo $file); + /** + * Restore this version + * + * @param IVersion $version + * @since 15.0.0 + */ + public function rollback(\OCA\Files_Versions\Versions\IVersion $version); + /** + * Open the file for reading + * + * @param IVersion $version + * @return resource|false + * @throws NotFoundException + * @since 15.0.0 + */ + public function read(\OCA\Files_Versions\Versions\IVersion $version); + /** + * Get the preview for a specific version of a file + * + * @param IUser $user + * @param FileInfo $sourceFile + * @param int|string $revision + * + * @return File + * + * @since 15.0.0 + */ + public function getVersionFile(\OCP\IUser $user, \OCP\Files\FileInfo $sourceFile, $revision) : \OCP\Files\File; } interface INameableVersionBackend { @@ -783,203 +874,631 @@ namespace OC\Files\Storage { use OCP\Files\Storage\IStorage; use OCP\Files\Cache\IScanner; - class Storage implements IStorage { - /** @var ?IScanner */ - public $scanner; + interface Storage extends \OCP\Files\Storage { + /** + * get a cache instance for the storage + * + * @param string $path + * @param \OC\Files\Storage\Storage|null (optional) the storage to pass to the cache + * @return \OC\Files\Cache\Cache + */ + public function getCache($path = '', $storage = null); - public function __construct(array $parameters) { - } + /** + * get a scanner instance for the storage + * + * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner + * @return \OC\Files\Cache\Scanner + */ + public function getScanner($path = '', $storage = null); - public function getId() {} - public function mkdir($path) {} + /** + * get the user id of the owner of a file or folder + * + * @param string $path + * @return string + */ + public function getOwner($path); - public function rmdir($path) {} + /** + * get a watcher instance for the cache + * + * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher + * @return \OC\Files\Cache\Watcher + */ + public function getWatcher($path = '', $storage = null); - public function opendir($path) { - throw new \Exception('stub'); - } + /** + * get a propagator instance for the cache + * + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher + * @return \OC\Files\Cache\Propagator + */ + public function getPropagator($storage = null); - public function is_dir($path) { - throw new \Exception('stub'); - } + /** + * get a updater instance for the cache + * + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher + * @return \OC\Files\Cache\Updater + */ + public function getUpdater($storage = null); - public function is_file($path) { - throw new \Exception('stub'); - } + /** + * @return \OC\Files\Cache\Storage + */ + public function getStorageCache(); - public function stat($path) { - throw new \Exception('stub'); - } + /** + * @param string $path + * @return array|null + */ + public function getMetaData($path); - public function filetype($path) { - throw new \Exception('stub'); - } + /** + * @param string $path The path of the file to acquire the lock for + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + * @param \OCP\Lock\ILockingProvider $provider + * @throws \OCP\Lock\LockedException + */ + public function acquireLock($path, $type, ILockingProvider $provider); - public function filesize($path) { - throw new \Exception('stub'); - } + /** + * @param string $path The path of the file to release the lock for + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + * @param \OCP\Lock\ILockingProvider $provider + * @throws \OCP\Lock\LockedException + */ + public function releaseLock($path, $type, ILockingProvider $provider); - public function isCreatable($path) { - throw new \Exception('stub'); - } + /** + * @param string $path The path of the file to change the lock for + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + * @param \OCP\Lock\ILockingProvider $provider + * @throws \OCP\Lock\LockedException + */ + public function changeLock($path, $type, ILockingProvider $provider); - public function isReadable($path) { - throw new \Exception('stub'); - } + /** + * Get the contents of a directory with metadata + * + * @param string $directory + * @return \Traversable an iterator, containing file metadata + * + * The metadata array will contain the following fields + * + * - name + * - mimetype + * - mtime + * - size + * - etag + * - storage_mtime + * - permissions + */ + public function getDirectoryContent($directory): \Traversable; + } +} - public function isUpdatable($path) { - throw new \Exception('stub'); - } +namespace OC\Files\Storage\Wrapper{ - public function isDeletable($path) { - throw new \Exception('stub'); - } + use OCP\Files\Cache\ICache; + use OCP\Files\Cache\ICacheEntry; + use OCP\Files\Search\ISearchQuery; + use OC\Files\Storage\Storage; + use OCP\Files\Storage\IStorage; + use OCP\Files\Cache\IScanner; - public function isSharable($path) { - throw new \Exception('stub'); + class Wrapper implements \OC\Files\Storage\Storage, \OCP\Files\Storage\ILockingStorage, \OCP\Files\Storage\IWriteStreamStorage + { + /** + * @var \OC\Files\Storage\Storage $storage + */ + protected $storage; + public $cache; + public $scanner; + public $watcher; + public $propagator; + public $updater; + /** + * @param array $parameters + */ + public function __construct($parameters) + { } - - public function getPermissions($path) { - throw new \Exception('stub'); + /** + * @return \OC\Files\Storage\Storage + */ + public function getWrapperStorage() + { } - - public function file_exists($path) { - throw new \Exception('stub'); + /** + * Get the identifier for the storage, + * the returned id should be the same for every storage object that is created with the same parameters + * and two storage objects with the same id should refer to two storages that display the same files. + * + * @return string + */ + public function getId() + { } - - public function filemtime($path) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.mkdir.php + * + * @param string $path + * @return bool + */ + public function mkdir($path) + { } - - public function file_get_contents($path) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.rmdir.php + * + * @param string $path + * @return bool + */ + public function rmdir($path) + { } - - public function file_put_contents($path, $data) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.opendir.php + * + * @param string $path + * @return resource|false + */ + public function opendir($path) + { } - - public function unlink($path) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.is_dir.php + * + * @param string $path + * @return bool + */ + public function is_dir($path) + { } - - public function rename($path1, $path2) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.is_file.php + * + * @param string $path + * @return bool + */ + public function is_file($path) + { } - - public function copy($path1, $path2) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.stat.php + * only the following keys are required in the result: size and mtime + * + * @param string $path + * @return array|bool + */ + public function stat($path) + { } - - public function fopen($path, $mode) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.filetype.php + * + * @param string $path + * @return string|bool + */ + public function filetype($path) + { } - - public function getMimeType($path) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.filesize.php + * The result for filesize when called on a folder is required to be 0 + */ + public function filesize($path) : false|int|float + { } - - public function hash($type, $path, $raw = false) { - throw new \Exception('stub'); + /** + * check if a file can be created in $path + * + * @param string $path + * @return bool + */ + public function isCreatable($path) + { } - - public function free_space($path) { - throw new \Exception('stub'); + /** + * check if a file can be read + * + * @param string $path + * @return bool + */ + public function isReadable($path) + { } - - public function touch($path, $mtime = null) { - throw new \Exception('stub'); + /** + * check if a file can be written to + * + * @param string $path + * @return bool + */ + public function isUpdatable($path) + { } - - public function getLocalFile($path) { - throw new \Exception('stub'); + /** + * check if a file can be deleted + * + * @param string $path + * @return bool + */ + public function isDeletable($path) + { } - - public function hasUpdated($path, $time) { - throw new \Exception('stub'); + /** + * check if a file can be shared + * + * @param string $path + * @return bool + */ + public function isSharable($path) + { } - - public function getETag($path) { - throw new \Exception('stub'); + /** + * get the full permissions of a path. + * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php + * + * @param string $path + * @return int + */ + public function getPermissions($path) + { } - - public function isLocal() { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.file_exists.php + * + * @param string $path + * @return bool + */ + public function file_exists($path) + { } - - public function instanceOfStorage($class) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.filemtime.php + * + * @param string $path + * @return int|bool + */ + public function filemtime($path) + { } - - public function getDirectDownload($path) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.file_get_contents.php + * + * @param string $path + * @return string|false + */ + public function file_get_contents($path) + { } - - public function verifyPath($path, $fileName) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.file_put_contents.php + * + * @param string $path + * @param mixed $data + * @return int|float|false + */ + public function file_put_contents($path, $data) + { } - - public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.unlink.php + * + * @param string $path + * @return bool + */ + public function unlink($path) + { } - - public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.rename.php + * + * @param string $source + * @param string $target + * @return bool + */ + public function rename($source, $target) + { } - - public function test() { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.copy.php + * + * @param string $source + * @param string $target + * @return bool + */ + public function copy($source, $target) + { } - - public function getAvailability() { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.fopen.php + * + * @param string $path + * @param string $mode + * @return resource|bool + */ + public function fopen($path, $mode) + { } - - public function setAvailability($isAvailable) { - throw new \Exception('stub'); + /** + * get the mimetype for a file or folder + * The mimetype for a folder is required to be "httpd/unix-directory" + * + * @param string $path + * @return string|bool + */ + public function getMimeType($path) + { } - - public function getOwner($path) { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.hash.php + * + * @param string $type + * @param string $path + * @param bool $raw + * @return string|bool + */ + public function hash($type, $path, $raw = false) + { } - - public function getCache() { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.free_space.php + * + * @param string $path + * @return int|float|bool + */ + public function free_space($path) + { } - - public function getPropagator() { - throw new \Exception('stub'); + /** + * search for occurrences of $query in file names + * + * @param string $query + * @return array|bool + */ + public function search($query) + { } - - public function getScanner() { - throw new \Exception('stub'); + /** + * see https://www.php.net/manual/en/function.touch.php + * If the backend does not support the operation, false should be returned + * + * @param string $path + * @param int $mtime + * @return bool + */ + public function touch($path, $mtime = null) + { } - - public function getUpdater() { - throw new \Exception('stub'); + /** + * get the path to a local version of the file. + * The local version of the file can be temporary and doesn't have to be persistent across requests + * + * @param string $path + * @return string|false + */ + public function getLocalFile($path) + { } - - public function getWatcher() { - throw new \Exception('stub'); + /** + * check if a file or folder has been updated since $time + * + * @param string $path + * @param int $time + * @return bool + * + * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed. + * returning true for other changes in the folder is optional + */ + public function hasUpdated($path, $time) + { } - } -} - -namespace OC\Files\Storage\Wrapper{ - - use OCP\Files\Cache\ICache; - use OCP\Files\Cache\ICacheEntry; - use OCP\Files\Search\ISearchQuery; - use OC\Files\Storage\Storage; - use OCP\Files\Storage\IStorage; - use OCP\Files\Cache\IScanner; + /** + * get a cache instance for the storage + * + * @param string $path + * @param \OC\Files\Storage\Storage|null (optional) the storage to pass to the cache + * @return \OC\Files\Cache\Cache + */ + public function getCache($path = '', $storage = null) + { + } + /** + * get a scanner instance for the storage + * + * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner + * @return \OC\Files\Cache\Scanner + */ + public function getScanner($path = '', $storage = null) + { + } + /** + * get the user id of the owner of a file or folder + * + * @param string $path + * @return string + */ + public function getOwner($path) + { + } + /** + * get a watcher instance for the cache + * + * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher + * @return \OC\Files\Cache\Watcher + */ + public function getWatcher($path = '', $storage = null) + { + } + public function getPropagator($storage = null) + { + } + public function getUpdater($storage = null) + { + } + /** + * @return \OC\Files\Cache\Storage + */ + public function getStorageCache() + { + } + /** + * get the ETag for a file or folder + * + * @param string $path + * @return string|false + */ + public function getETag($path) + { + } + /** + * Returns true + * + * @return true + */ + public function test() + { + } + /** + * Returns the wrapped storage's value for isLocal() + * + * @return bool wrapped storage's isLocal() value + */ + public function isLocal() + { + } + /** + * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class + * + * @param class-string $class + * @return bool + */ + public function instanceOfStorage($class) + { + } + /** + * @psalm-template T of IStorage + * @psalm-param class-string $class + * @psalm-return T|null + */ + public function getInstanceOfStorage(string $class) + { + } + /** + * Pass any methods custom to specific storage implementations to the wrapped storage + * + * @param string $method + * @param array $args + * @return mixed + */ + public function __call($method, $args) + { + } + /** + * A custom storage implementation can return an url for direct download of a give file. + * + * For now the returned array can hold the parameter url - in future more attributes might follow. + * + * @param string $path + * @return array|bool + */ + public function getDirectDownload($path) + { + } + /** + * Get availability of the storage + * + * @return array [ available, last_checked ] + */ + public function getAvailability() + { + } + /** + * Set availability of the storage + * + * @param bool $isAvailable + */ + public function setAvailability($isAvailable) + { + } + /** + * @param string $path the path of the target folder + * @param string $fileName the name of the file itself + * @return void + * @throws InvalidPathException + */ + public function verifyPath($path, $fileName) + { + } + /** + * @param IStorage $sourceStorage + * @param string $sourceInternalPath + * @param string $targetInternalPath + * @return bool + */ + public function copyFromStorage(\OCP\Files\Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) + { + } + /** + * @param IStorage $sourceStorage + * @param string $sourceInternalPath + * @param string $targetInternalPath + * @return bool + */ + public function moveFromStorage(\OCP\Files\Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) + { + } + public function getMetaData($path) + { + } + /** + * @param string $path + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + * @param \OCP\Lock\ILockingProvider $provider + * @throws \OCP\Lock\LockedException + */ + public function acquireLock($path, $type, \OCP\Lock\ILockingProvider $provider) + { + } + /** + * @param string $path + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + * @param \OCP\Lock\ILockingProvider $provider + */ + public function releaseLock($path, $type, \OCP\Lock\ILockingProvider $provider) + { + } + /** + * @param string $path + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + * @param \OCP\Lock\ILockingProvider $provider + */ + public function changeLock($path, $type, \OCP\Lock\ILockingProvider $provider) + { + } + /** + * @return bool + */ + public function needsPartFile() + { + } + public function writeStream(string $path, $stream, int $size = null) : int + { + } + public function getDirectoryContent($directory) : \Traversable + { + } + } - class Wrapper extends Storage { - protected Storage $storage; - public function getWrapperStorage(): ?IStorage {} - } class Jail extends Wrapper { public function getUnjailedPath(string $path): string {} @@ -994,10 +1513,6 @@ namespace OC\Files\Storage\Wrapper{ } } -namespace OCP\Files\Mount { - interface ISystemMountPoint {} -} - namespace OC\Files\ObjectStore { use OC\Files\Storage\Wrapper\Wrapper; class ObjectStoreStorage extends Wrapper {} @@ -1063,7 +1578,7 @@ namespace OCA\Circles\Model\Probes { namespace OCA\Circles\Events { use OCA\Circles\Model\Circle; - class CircleDestroyedEvent { + class CircleDestroyedEvent extends \OCP\EventDispatcher\Event { public function getCircle(): Circle {} } } From 207e669db5d396c43a94d25ffbe559d69a5014ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 30 Nov 2023 12:13:45 +0100 Subject: [PATCH 3/5] Fix or suppress remaining psalm issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/Mount/GroupFolderStorage.php | 5 ++++- lib/Versions/ExpireManager.php | 1 + lib/Versions/GroupVersionsExpireManager.php | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Mount/GroupFolderStorage.php b/lib/Mount/GroupFolderStorage.php index add33243a..8236d7c76 100644 --- a/lib/Mount/GroupFolderStorage.php +++ b/lib/Mount/GroupFolderStorage.php @@ -49,6 +49,9 @@ public function getFolderId(): int { return $this->folderId; } + /** + * @psalm-suppress FalsableReturnStatement Return type of getOwner is not clear even in server + */ public function getOwner($path) { $user = $this->userSession->getUser(); if ($user !== null) { @@ -70,7 +73,7 @@ public function getCache($path = '', $storage = null) { } public function getScanner($path = '', $storage = null) { - /** @var \OC\Files\Storage\Storage $storage */ + /** @var \OC\Files\Storage\Wrapper\Wrapper $storage */ if (!$storage) { $storage = $this; } diff --git a/lib/Versions/ExpireManager.php b/lib/Versions/ExpireManager.php index a4d8cf642..e4dec1ce9 100644 --- a/lib/Versions/ExpireManager.php +++ b/lib/Versions/ExpireManager.php @@ -97,6 +97,7 @@ protected function getAutoExpireList(int $time, array $versions): array { $newInterval = false; // version checked so we can move to the next one } else { // time to move on to the next interval $interval++; + /** @psalm-suppress InvalidArrayOffset We know that $interval is <= 6 thanks to the -1 intervalEndsAfter in the last step */ $step = self::MAX_VERSIONS_PER_INTERVAL[$interval]['step']; $nextVersion = $prevTimestamp - $step; if (self::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'] === -1) { diff --git a/lib/Versions/GroupVersionsExpireManager.php b/lib/Versions/GroupVersionsExpireManager.php index 1b7a4ad5c..377e20938 100644 --- a/lib/Versions/GroupVersionsExpireManager.php +++ b/lib/Versions/GroupVersionsExpireManager.php @@ -61,7 +61,7 @@ public function expireAll(): void { } /** - * @param array{id: int, mount_point: string, groups: array|array, quota: int, size: int, acl: bool} $folder + * @param array{acl: bool, groups: array>, id: int, mount_point: mixed, quota: int, size: 0} $folder */ public function expireFolder(array $folder): void { $view = new View('/__groupfolders/versions/' . $folder['id']); From 9a847129a64212c9ffcc0d4a9402d1f756a36813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 30 Nov 2023 12:14:10 +0100 Subject: [PATCH 4/5] Run cs:fix with new coding standard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/Listeners/LoadAdditionalScriptsListener.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Listeners/LoadAdditionalScriptsListener.php b/lib/Listeners/LoadAdditionalScriptsListener.php index 85c91137a..a5b1f8e36 100644 --- a/lib/Listeners/LoadAdditionalScriptsListener.php +++ b/lib/Listeners/LoadAdditionalScriptsListener.php @@ -25,10 +25,10 @@ namespace OCA\GroupFolders\Listeners; -use OCP\EventDispatcher\Event; -use OCP\EventDispatcher\IEventListener; use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; /** * @template-implements IEventListener From aec14e993e8bd79f47c5a1b43534118fcf36e4d5 Mon Sep 17 00:00:00 2001 From: Git'Fellow <12234510+solracsf@users.noreply.github.com> Date: Fri, 24 Nov 2023 23:49:57 +0100 Subject: [PATCH 5/5] Rename param names as defined by OCP\Files\Storage\IStorage Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com> --- lib/ACL/ACLStorageWrapper.php | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/ACL/ACLStorageWrapper.php b/lib/ACL/ACLStorageWrapper.php index 678c658da..668b9cd9a 100644 --- a/lib/ACL/ACLStorageWrapper.php +++ b/lib/ACL/ACLStorageWrapper.php @@ -81,23 +81,23 @@ public function getPermissions($path) { return $this->storage->getPermissions($path) & $this->getACLPermissionsForPath($path); } - public function rename($path1, $path2) { - if (strpos($path1, $path2) === 0) { - $part = substr($path1, strlen($path2)); + public function rename($source, $target) { + if (strpos($source, $target) === 0) { + $part = substr($source, strlen($target)); //This is a rename of the transfer file to the original file if (strpos($part, '.ocTransferId') === 0) { - return $this->checkPermissions($path2, Constants::PERMISSION_CREATE) && parent::rename($path1, $path2); + return $this->checkPermissions($target, Constants::PERMISSION_CREATE) && parent::rename($source, $target); } } - $permissions = $this->file_exists($path2) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE; - $sourceParent = dirname($path1); + $permissions = $this->file_exists($target) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE; + $sourceParent = dirname($source); if ($sourceParent === '.') { $sourceParent = ''; } return $this->checkPermissions($sourceParent, Constants::PERMISSION_DELETE) && - $this->checkPermissions($path1, Constants::PERMISSION_UPDATE & Constants::PERMISSION_READ) && - $this->checkPermissions($path2, $permissions) && - parent::rename($path1, $path2); + $this->checkPermissions($source, Constants::PERMISSION_UPDATE & Constants::PERMISSION_READ) && + $this->checkPermissions($target, $permissions) && + parent::rename($source, $target); } public function opendir($path) { @@ -118,11 +118,11 @@ public function opendir($path) { return IteratorDirectory::wrap($items); } - public function copy($path1, $path2) { - $permissions = $this->file_exists($path2) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE; - return $this->checkPermissions($path2, $permissions) && - $this->checkPermissions($path1, Constants::PERMISSION_READ) && - parent::copy($path1, $path2); + public function copy($source, $target) { + $permissions = $this->file_exists($target) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE; + return $this->checkPermissions($target, $permissions) && + $this->checkPermissions($source, Constants::PERMISSION_READ) && + parent::copy($source, $target); } public function touch($path, $mtime = null) {