diff --git a/apps/comments/lib/Search/CommentsSearchProvider.php b/apps/comments/lib/Search/CommentsSearchProvider.php index 3ce6abf306c55..81a05c7d37e70 100644 --- a/apps/comments/lib/Search/CommentsSearchProvider.php +++ b/apps/comments/lib/Search/CommentsSearchProvider.php @@ -53,10 +53,6 @@ public function getOrder(string $route, array $routeParameters): int { public function search(IUser $user, ISearchQuery $query): SearchResult { $userFolder = $this->rootFolder->getUserFolder($user->getUID()); - if ($userFolder === null) { - return SearchResult::complete($this->l10n->t('Comments'), []); - } - $result = []; $numComments = 50; $offset = 0; diff --git a/apps/dav/lib/Connector/Sabre/Directory.php b/apps/dav/lib/Connector/Sabre/Directory.php index f4e8ee1d99a77..12e732f2b415e 100644 --- a/apps/dav/lib/Connector/Sabre/Directory.php +++ b/apps/dav/lib/Connector/Sabre/Directory.php @@ -7,7 +7,9 @@ */ namespace OCA\DAV\Connector\Sabre; +use OC\Files\Mount\DummyMountPoint; use OC\Files\Mount\MoveableMount; +use OC\Files\Storage\FailedStorage; use OC\Files\Utils\PathHelper; use OC\Files\View; use OCA\DAV\AppInfo\Application; @@ -113,9 +115,9 @@ public function createFile($name, $data = null) { $info = $this->fileView->getFileInfo($this->path . '/' . $name); if (!$info) { // use a dummy FileInfo which is acceptable here since it will be refreshed after the put is complete - $info = new \OC\Files\FileInfo($path, null, null, [ + $info = new \OC\Files\FileInfo($path, new FailedStorage(['exception' => new \LogicException('Dummy storage') ]), '', [ 'type' => FileInfo::TYPE_FILE - ], null); + ], new DummyMountPoint()); } $node = new File($this->fileView, $info); diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php index 64e8faa5c368b..b0bead3583d03 100644 --- a/apps/dav/lib/Connector/Sabre/File.php +++ b/apps/dav/lib/Connector/Sabre/File.php @@ -55,12 +55,6 @@ class File extends Node implements IFile { /** * Sets up the node, expects a full path name - * - * @param View $view - * @param FileInfo $info - * @param ?\OCP\Share\IManager $shareManager - * @param ?IRequest $request - * @param ?IL10N $l10n */ public function __construct(View $view, FileInfo $info, ?IManager $shareManager = null, ?IRequest $request = null, ?IL10N $l10n = null) { parent::__construct($view, $info, $shareManager); @@ -369,7 +363,7 @@ public function put($data) { if ($checksumHeader) { $checksum = trim($checksumHeader); $this->setChecksum($checksum); - } elseif ($this->getChecksum() !== null && $this->getChecksum() !== '') { + } elseif ($this->getChecksum() !== '') { $this->setChecksum(''); } } catch (StorageNotAvailableException $e) { @@ -549,9 +543,6 @@ public function getDirectDownload(): array|false { } $node = $this->getNode(); $storage = $node->getStorage(); - if (!$storage) { - return false; - } if (!($node->getPermissions() & Constants::PERMISSION_READ)) { return false; @@ -612,10 +603,8 @@ private function convertToSabreException(\Exception $e) { /** * Get the checksum for this file - * - * @return string|null */ - public function getChecksum() { + public function getChecksum(): string { return $this->info->getChecksum(); } diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index 7b2f144dfa1d8..47138b938fd58 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -278,7 +278,7 @@ public function httpGet(RequestInterface $request, ResponseInterface $response) if ($node instanceof File) { //Add OC-Checksum header $checksum = $node->getChecksum(); - if ($checksum !== null && $checksum !== '') { + if ($checksum !== '') { $response->addHeader('OC-Checksum', $checksum); } } @@ -312,7 +312,7 @@ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) }); $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function () use ($node) { - return $node->getInternalFileId(); + return (string)$node->getInternalFileId(); }); $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use ($node) { @@ -500,7 +500,7 @@ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function () use ($node) { $checksum = $node->getChecksum(); - if ($checksum === null || $checksum === '') { + if ($checksum === '') { return null; } diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php index 14ac7063ace4e..3babcbcafd6bb 100644 --- a/apps/dav/lib/Connector/Sabre/Node.php +++ b/apps/dav/lib/Connector/Sabre/Node.php @@ -27,18 +27,8 @@ abstract class Node implements \Sabre\DAV\INode { /** * The path to the current node - * - * @var string - */ - protected $path; - - protected FileInfo $info; - - /** - * @var IManager */ - protected $shareManager; - + protected string $path = ''; protected \OCP\Files\Node $node; /** @@ -46,14 +36,11 @@ abstract class Node implements \Sabre\DAV\INode { */ public function __construct( protected View $fileView, - FileInfo $info, - ?IManager $shareManager = null, + protected FileInfo $info, + protected ?IManager $shareManager = null, ) { $this->path = $this->fileView->getRelativePath($info->getPath()); - $this->info = $info; - if ($shareManager) { - $this->shareManager = $shareManager; - } else { + if (!$this->shareManager) { $this->shareManager = Server::get(\OCP\Share\IManager::class); } if ($info instanceof Folder || $info instanceof File) { @@ -141,7 +128,7 @@ public function setName($name) { public function getLastModified() { $timestamp = $this->info->getMtime(); if (!empty($timestamp)) { - return (int)$timestamp; + return $timestamp; } return $timestamp; } @@ -207,7 +194,7 @@ public function getSize(): int|float { * @return int */ public function getId() { - return $this->info->getId(); + return $this->info->getId() ?? -1; } /** @@ -221,11 +208,8 @@ public function getFileId() { return null; } - /** - * @return integer - */ - public function getInternalFileId() { - return $this->info->getId(); + public function getInternalFileId(): int { + return $this->info->getId() ?? -1; } public function getInternalPath(): string { diff --git a/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php b/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php index e2567af602d6a..1e4d8cb9951d0 100644 --- a/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php @@ -70,6 +70,8 @@ protected function setUp(): void { parent::setUp(); $this->view = $this->createMock(View::class); + $this->view->method('getAbsolutePath')->willReturnArgument(0); + $this->info = $this->createMock(FileInfo::class); $this->storage = $this->createMock(IStorage::class); $this->info->method('getStorage') @@ -107,7 +109,7 @@ public function testDeleteRootFolderFails(): void { ->willReturn(true); $this->view->expects($this->never()) ->method('rmdir'); - $dir = $this->getDir(); + $dir = $this->getDir('/'); $dir->delete(); } @@ -231,6 +233,8 @@ public function testGetChildrenNoPermission(): void { ->method('isReadable') ->willReturn(false); + $this->view->method('getRelativePath')->willReturnArgument(0); + $dir = new Directory($this->view, $info); $dir->getChildren(); } @@ -243,6 +247,8 @@ public function testGetChildNoPermission(): void { ->method('isReadable') ->willReturn(false); + $this->view->method('getRelativePath')->willReturnArgument(0); + $dir = new Directory($this->view, $this->info); $dir->getChild('test'); } @@ -255,6 +261,8 @@ public function testGetChildThrowStorageNotAvailableException(): void { ->method('getFileInfo') ->willThrowException(new StorageNotAvailableException()); + $this->view->method('getRelativePath')->willReturnArgument(0); + $dir = new Directory($this->view, $this->info); $dir->getChild('.'); } @@ -269,6 +277,8 @@ public function testGetChildThrowInvalidPath(): void { $this->view->expects($this->never()) ->method('getFileInfo'); + $this->view->method('getRelativePath')->willReturnArgument(0); + $dir = new Directory($this->view, $this->info); $dir->getChild('.'); } @@ -562,12 +572,12 @@ public static function moveSuccessProvider(): array { private function moveTest(string $source, string $destination, array $updatables, array $deletables): void { $view = new TestViewDirectory($updatables, $deletables); - $sourceInfo = new FileInfo($source, null, null, [ + $sourceInfo = new FileInfo($source, $this->createMock(IStorage::class), '', [ 'type' => FileInfo::TYPE_FOLDER, - ], null); - $targetInfo = new FileInfo(dirname($destination), null, null, [ + ], $this->createMock(IMountPoint::class)); + $targetInfo = new FileInfo(dirname($destination), $this->createMock(IStorage::class), '', [ 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $sourceNode = new Directory($view, $sourceInfo); $targetNode = $this->getMockBuilder(Directory::class) @@ -592,8 +602,8 @@ public function testFailingMove(): void { $view = new TestViewDirectory($updatables, $deletables); - $sourceInfo = new FileInfo($source, null, null, ['type' => FileInfo::TYPE_FOLDER], null); - $targetInfo = new FileInfo(dirname($destination), null, null, ['type' => FileInfo::TYPE_FOLDER], null); + $sourceInfo = new FileInfo($source, $this->createMock(IStorage::class), '', ['type' => FileInfo::TYPE_FOLDER], $this->createMock(IMountPoint::class)); + $targetInfo = new FileInfo(dirname($destination), $this->createMock(IStorage::class), '', ['type' => FileInfo::TYPE_FOLDER], $this->createMock(IMountPoint::class)); $sourceNode = new Directory($view, $sourceInfo); $targetNode = $this->getMockBuilder(Directory::class) diff --git a/apps/dav/tests/unit/Connector/Sabre/FileTest.php b/apps/dav/tests/unit/Connector/Sabre/FileTest.php index 64ee2df025379..fa28199642d98 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FileTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FileTest.php @@ -26,6 +26,7 @@ use OCP\Files\InvalidContentException; use OCP\Files\InvalidPathException; use OCP\Files\LockNotAcquiredException; +use OCP\Files\Mount\IMountPoint; use OCP\Files\NotPermittedException; use OCP\Files\Storage\IStorage; use OCP\Files\StorageNotAvailableException; @@ -184,10 +185,10 @@ function ($path) use ($storage) { ->method('getRelativePath') ->willReturnArgument(0); - $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); @@ -227,12 +228,12 @@ private function doPut(string $path, ?string $viewRoot = null, ?Request $request $info = new \OC\Files\FileInfo( $viewRoot . '/' . ltrim($path, '/'), $this->getMockStorage(), - null, + '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], - null + $this->createMock(IMountPoint::class), ); /** @var File&MockObject $file */ @@ -495,10 +496,10 @@ public function testSimplePutFailsSizeCheck(): void { 'method' => 'PUT', ], $this->requestId, $this->config, null); - $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info, null, $request); @@ -529,10 +530,10 @@ public function testSimplePutFailsMoveFromStorage(): void { // simulate situation where the target file is locked $view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE); - $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); @@ -567,10 +568,10 @@ public function testSimplePutInvalidChars(): void { ->method('getRelativePath') ->willReturnArgument(0); - $info = new \OC\Files\FileInfo("/i\nvalid", $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo("/i\nvalid", $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); // action @@ -608,10 +609,10 @@ public function testSetNameInvalidChars(): void { ->method('getRelativePath') ->willReturnArgument(0); - $info = new \OC\Files\FileInfo('/valid', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/valid', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); $file->setName("/i\nvalid"); @@ -642,10 +643,10 @@ public function testUploadAbort(): void { 'method' => 'PUT', ], $this->requestId, $this->config, null); - $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info, null, $request); @@ -674,14 +675,17 @@ public function testDeleteWhenAllowed(): void { $view = $this->getMockBuilder(View::class) ->getMock(); + $view->method('getAbsolutePath')->willReturnArgument(0); + $view->method('getRelativePath')->willReturnArgument(0); + $view->expects($this->once()) ->method('unlink') ->willReturn(true); - $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); @@ -698,10 +702,13 @@ public function testDeleteThrowsWhenDeletionNotAllowed(): void { $view = $this->getMockBuilder(View::class) ->getMock(); - $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ + $view->method('getAbsolutePath')->willReturnArgument(0); + $view->method('getRelativePath')->willReturnArgument(0); + + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [ 'permissions' => 0, 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); @@ -718,15 +725,18 @@ public function testDeleteThrowsWhenDeletionFailed(): void { $view = $this->getMockBuilder(View::class) ->getMock(); + $view->method('getAbsolutePath')->willReturnArgument(0); + $view->method('getRelativePath')->willReturnArgument(0); + // but fails $view->expects($this->once()) ->method('unlink') ->willReturn(false); - $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); @@ -743,15 +753,18 @@ public function testDeleteThrowsWhenDeletionThrows(): void { $view = $this->getMockBuilder(View::class) ->getMock(); + $view->method('getAbsolutePath')->willReturnArgument(0); + $view->method('getRelativePath')->willReturnArgument(0); + // but fails $view->expects($this->once()) ->method('unlink') ->willThrowException(new ForbiddenException('', true)); - $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); @@ -785,12 +798,12 @@ public function testPutLocking(): void { $info = new \OC\Files\FileInfo( '/' . $this->user . '/files/' . $path, $this->getMockStorage(), - null, + '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], - null + $this->createMock(IMountPoint::class) ); $file = new File($view, $info); @@ -921,10 +934,10 @@ public function testGetFopenFails(): void { ->method('fopen') ->willReturn(false); - $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FILE, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); @@ -943,10 +956,10 @@ public function testGetFopenThrows(): void { ->method('fopen') ->willThrowException(new ForbiddenException('', true)); - $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FILE, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); @@ -964,10 +977,10 @@ public function testGetThrowsIfNoPermission(): void { $view->expects($this->never()) ->method('fopen'); - $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [ 'permissions' => Constants::PERMISSION_CREATE, // no read perm 'type' => FileInfo::TYPE_FOLDER, - ], null); + ], $this->createMock(IMountPoint::class)); $file = new File($view, $info); @@ -1012,12 +1025,12 @@ public function testPutLockExpired(): void { $info = new \OC\Files\FileInfo( '/' . $this->user . '/files/' . $path, $this->getMockStorage(), - null, + '', [ 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], - null + $this->createMock(IMountPoint::class) ); $file = new File($view, $info); diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php index 8d27bc337e4af..b837124957990 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php @@ -96,7 +96,7 @@ private function createTestNode(string $class, string $path = '/dummypath'): Moc ->willReturn('00000123instanceid'); $node->expects($this->any()) ->method('getInternalFileId') - ->willReturn('123'); + ->willReturn(123); $node->expects($this->any()) ->method('getEtag') ->willReturn('"abc"'); diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php index 176949f999c78..5b4ec68defe4e 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php @@ -57,6 +57,8 @@ protected function setUp(): void { $this->tree = $this->createMock(Tree::class); $this->view = $this->createMock(View::class); + $this->view->method('getAbsolutePath')->willReturnArgument(0); + $this->view->method('getRelativePath')->willReturnArgument(0); $this->server = $this->getMockBuilder(Server::class) ->setConstructorArgs([$this->tree]) @@ -315,14 +317,14 @@ public function testPrepareResponses(): void { $node1->expects($this->once()) ->method('getInternalFileId') - ->willReturn('111'); + ->willReturn(111); $node1->expects($this->any()) ->method('getPath') ->willReturn('/node1'); $node1->method('getFileInfo')->willReturn($fileInfo); $node2->expects($this->once()) ->method('getInternalFileId') - ->willReturn('222'); + ->willReturn(222); $node2->expects($this->once()) ->method('getSize') ->willReturn(1024); diff --git a/apps/dav/tests/unit/Connector/Sabre/NodeTest.php b/apps/dav/tests/unit/Connector/Sabre/NodeTest.php index ddea281ea1d49..6521e2bf980a7 100644 --- a/apps/dav/tests/unit/Connector/Sabre/NodeTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/NodeTest.php @@ -55,7 +55,7 @@ public static function davPermissionsProvider(): array { public function testDavPermissions(int $permissions, string $type, bool $shared, int $shareRootPermissions, bool $mounted, string $internalPath, string $expected): void { $info = $this->getMockBuilder(FileInfo::class) ->disableOriginalConstructor() - ->onlyMethods(['getPermissions', 'isShared', 'isMounted', 'getType', 'getInternalPath', 'getStorage', 'getMountPoint']) + ->onlyMethods(['getPermissions', 'isShared', 'isMounted', 'getType', 'getPath', 'getInternalPath', 'getStorage', 'getMountPoint']) ->getMock(); $info->method('getPermissions') ->willReturn($permissions); @@ -65,6 +65,8 @@ public function testDavPermissions(int $permissions, string $type, bool $shared, ->willReturn($mounted); $info->method('getType') ->willReturn($type); + $info->method('getPath') + ->willReturn(''); $info->method('getInternalPath') ->willReturn($internalPath); $info->method('getMountPoint') @@ -94,8 +96,10 @@ public function testDavPermissions(int $permissions, string $type, bool $shared, $info->method('getStorage') ->willReturn($storage); $view = $this->createMock(View::class); + $view->method('getRelativePath')->willReturnArgument(0); + $view->method('getAbsolutePath')->willReturnArgument(0); - $node = new File($view, $info); + $node = new File($view, $info); $this->assertEquals($expected, $node->getDavPermissions()); } @@ -160,15 +164,18 @@ public function testSharePermissions(string $type, ?string $user, int $permissio $info = $this->getMockBuilder(FileInfo::class) ->disableOriginalConstructor() - ->onlyMethods(['getStorage', 'getType', 'getMountPoint', 'getPermissions']) + ->onlyMethods(['getStorage', 'getType', 'getPath', 'getMountPoint', 'getPermissions']) ->getMock(); $info->method('getStorage')->willReturn($storage); $info->method('getType')->willReturn($type); + $info->method('getPath')->willReturn(''); $info->method('getMountPoint')->willReturn($mountpoint); $info->method('getPermissions')->willReturn($permissions); $view = $this->createMock(View::class); + $view->method('getRelativePath')->willReturnArgument(0); + $view->method('getAbsolutePath')->willReturnArgument(0); $node = new File($view, $info); $this->invokePrivate($node, 'shareManager', [$shareManager]); @@ -196,14 +203,17 @@ public function testShareAttributes(): void { /** @var Folder&MockObject $info */ $info = $this->getMockBuilder(Folder::class) ->disableOriginalConstructor() - ->onlyMethods(['getStorage', 'getType']) + ->onlyMethods(['getStorage', 'getType', 'getPath']) ->getMock(); $info->method('getStorage')->willReturn($storage); $info->method('getType')->willReturn(FileInfo::TYPE_FOLDER); + $info->method('getPath')->willReturn(''); /** @var View&MockObject $view */ $view = $this->createMock(View::class); + $view->method('getRelativePath')->willReturnArgument(0); + $view->method('getAbsolutePath')->willReturnArgument(0); $node = new File($view, $info); $this->invokePrivate($node, 'shareManager', [$shareManager]); @@ -217,14 +227,17 @@ public function testShareAttributesNonShare(): void { /** @var Folder&MockObject */ $info = $this->getMockBuilder(Folder::class) ->disableOriginalConstructor() - ->onlyMethods(['getStorage', 'getType']) + ->onlyMethods(['getStorage', 'getType', 'getPath']) ->getMock(); $info->method('getStorage')->willReturn($storage); $info->method('getType')->willReturn(FileInfo::TYPE_FOLDER); + $info->method('getPath')->willReturn(''); /** @var View&MockObject */ $view = $this->createMock(View::class); + $view->method('getRelativePath')->willReturnArgument(0); + $view->method('getAbsolutePath')->willReturnArgument(0); $node = new File($view, $info); $this->invokePrivate($node, 'shareManager', [$shareManager]); @@ -243,6 +256,9 @@ public function testSanitizeMtime(string|int $mtime, int $expected): void { $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor() ->getMock(); + $view->method('getAbsolutePath')->willReturnArgument(0); + $view->method('getRelativePath')->willReturnArgument(0); + $info = $this->getMockBuilder(FileInfo::class) ->disableOriginalConstructor() ->getMock(); @@ -263,6 +279,8 @@ public function testInvalidSanitizeMtime(int|string $mtime): void { $this->expectException(\InvalidArgumentException::class); $view = $this->createMock(View::class); + $view->method('getRelativePath')->willReturnArgument(0); + $view->method('getAbsolutePath')->willReturnArgument(0); $info = $this->createMock(FileInfo::class); $node = new File($view, $info); diff --git a/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php b/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php index 592d6c958d0f3..3904e1224015c 100644 --- a/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php @@ -19,6 +19,7 @@ use OCA\DAV\Connector\Sabre\File; use OCA\DAV\Connector\Sabre\ObjectTree; use OCP\Files\Mount\IMountManager; +use PHPUnit\Framework\MockObject\MockObject; /** * Class ObjectTreeTest @@ -41,7 +42,7 @@ public static function copyDataProvider(): array { #[\PHPUnit\Framework\Attributes\DataProvider('copyDataProvider')] public function testCopy(string $sourcePath, string $targetPath, string $targetParent): void { - $view = $this->createMock(View::class); + $view = $this->createView(); $view->expects($this->once()) ->method('verifyPath') ->with($targetParent); @@ -85,7 +86,7 @@ public function testCopy(string $sourcePath, string $targetPath, string $targetP public function testCopyFailNotCreatable($sourcePath, $targetPath, $targetParent): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); - $view = $this->createMock(View::class); + $view = $this->createView(); $view->expects($this->never()) ->method('verifyPath'); $view->expects($this->once()) @@ -129,7 +130,7 @@ public function testGetNodeForPath( ): void { $rootNode = $this->createMock(Directory::class); $mountManager = $this->createMock(Manager::class); - $view = $this->createMock(View::class); + $view = $this->createView(); $fileInfo = $this->createMock(FileInfo::class); $fileInfo->method('getType') ->willReturn($type); @@ -240,4 +241,11 @@ public function testGetNodeForPathRoot(): void { $this->assertInstanceOf('\Sabre\DAV\INode', $tree->getNodeForPath($path)); } + + private function createView(): View&MockObject { + $view = $this->createMock(View::class); + $view->method('getAbsolutePath')->willReturnArgument(0); + $view->method('getRelativePath')->willReturnArgument(0); + return $view; + } } diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php index 404dc7fa5d7b4..d623cec705fd9 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php @@ -118,7 +118,7 @@ protected function makeRequest(Server $server, Request $request) { $sapi = new Sapi($request); $server->sapi = $sapi; $server->httpRequest = $request; - $server->exec(); + $server->start(); return $sapi->getResponse(); } diff --git a/apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php b/apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php index eefbc53fd22cb..66f69315590a4 100644 --- a/apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php +++ b/apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php @@ -97,6 +97,7 @@ public static function providesDataForCanGet(): array { #[\PHPUnit\Framework\Attributes\DataProvider('providesDataForCanGet')] public function testCanGet(bool $isVersion, ?bool $attrEnabled, bool $expectCanDownloadFile, bool $allowViewWithoutDownload): void { $nodeInfo = $this->createMock(File::class); + $nodeInfo->method('getId')->willReturn(42); if ($isVersion) { $davPath = 'versions/alice/versions/117/123456'; $version = $this->createMock(IVersion::class); diff --git a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php index 30da7befa8fc5..d561e0bc5238d 100644 --- a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php +++ b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php @@ -313,6 +313,9 @@ protected function executeAcceptShare(IShare $share): void { try { $fileId = $share->getNode()->getId(); + if ($fileId == null) { + throw new \LogicException('Invalid node for share'); + } [$file, $link] = $this->getFile($user, $fileId); } catch (\Exception) { throw new ShareNotFound(); @@ -388,6 +391,9 @@ protected function executeDeclineShare(IShare $share): void { try { $fileId = $share->getNode()->getId(); + if ($fileId == null) { + throw new \LogicException('Invalid node for share'); + } [$file, $link] = $this->getFile($user, $fileId); } catch (\Exception) { throw new ShareNotFound(); diff --git a/apps/files/lib/Controller/ApiController.php b/apps/files/lib/Controller/ApiController.php index d8fb0ab0d59fc..fa56702205636 100644 --- a/apps/files/lib/Controller/ApiController.php +++ b/apps/files/lib/Controller/ApiController.php @@ -201,9 +201,7 @@ private function getShareTypesForNodes(array $nodes): array { ]; $shareTypes = []; - $nodeIds = array_map(function (Node $node) { - return $node->getId(); - }, $nodes); + $nodeIds = array_filter(array_map(fn (Node $node): ?int => $node->getId(), $nodes)); foreach ($requestedShareTypes as $shareType) { $nodesLeft = array_combine($nodeIds, array_fill(0, count($nodeIds), true)); diff --git a/apps/files/lib/Controller/ConversionApiController.php b/apps/files/lib/Controller/ConversionApiController.php index 40a42d6ca4c8f..27fee6c318cfd 100644 --- a/apps/files/lib/Controller/ConversionApiController.php +++ b/apps/files/lib/Controller/ConversionApiController.php @@ -99,7 +99,7 @@ public function convert(int $fileId, string $targetMimeType, ?string $destinatio } $file = $userFolder->get($convertedFileRelativePath); - $fileId = $file->getId(); + $fileId = $file->getId() ?? -1; return new DataResponse([ 'path' => $convertedFileRelativePath, diff --git a/apps/files/lib/Listener/SyncLivePhotosListener.php b/apps/files/lib/Listener/SyncLivePhotosListener.php index b6773e8c45239..8771baa86b9fb 100644 --- a/apps/files/lib/Listener/SyncLivePhotosListener.php +++ b/apps/files/lib/Listener/SyncLivePhotosListener.php @@ -134,14 +134,18 @@ private function handleMove(Node $sourceFile, Node $targetFile, Node $peerFile): return; } - $this->pendingRenames[] = $sourceFile->getId(); + $sourceFileId = $sourceFile->getId(); + if ($sourceFileId === null) { + throw new \LogicException('Invalid source file given with a null id'); + } + $this->pendingRenames[] = $sourceFileId; try { $peerFile->move($targetParent->getPath() . '/' . $peerTargetName); } catch (\Throwable $ex) { throw new AbortedEventException($ex->getMessage()); } - $this->pendingRenames = array_diff($this->pendingRenames, [$sourceFile->getId()]); + $this->pendingRenames = array_diff($this->pendingRenames, [$sourceFileId]); } @@ -163,15 +167,26 @@ private function handleCopy(File $sourceFile, File $targetFile, File $peerFile): $targetPeerFile = $peerFile->copy($targetParent->getPath() . '/' . $peerTargetName); } + $targetFileId = $targetFile->getId(); + if ($targetFileId === null) { + throw new \LogicException('Invalid target file given with a null id'); + } + + $targetPeerFileId = $targetPeerFile->getId(); + if ($targetPeerFileId === null) { + throw new \LogicException('Invalid target peer file given with a null id'); + } + /** @var FilesMetadata $targetMetadata */ - $targetMetadata = $this->filesMetadataManager->getMetadata($targetFile->getId(), true); + $targetMetadata = $this->filesMetadataManager->getMetadata($targetFileId, true); $targetMetadata->setStorageId($targetFile->getStorage()->getCache()->getNumericStorageId()); - $targetMetadata->setString('files-live-photo', (string)$targetPeerFile->getId()); + $targetMetadata->setString('files-live-photo', (string)$targetPeerFileId); $this->filesMetadataManager->saveMetadata($targetMetadata); + /** @var FilesMetadata $peerMetadata */ - $peerMetadata = $this->filesMetadataManager->getMetadata($targetPeerFile->getId(), true); + $peerMetadata = $this->filesMetadataManager->getMetadata($targetPeerFileId, true); $peerMetadata->setStorageId($targetPeerFile->getStorage()->getCache()->getNumericStorageId()); - $peerMetadata->setString('files-live-photo', (string)$targetFile->getId()); + $peerMetadata->setString('files-live-photo', (string)$targetFileId); $this->filesMetadataManager->saveMetadata($peerMetadata); } @@ -185,14 +200,22 @@ private function handleCopy(File $sourceFile, File $targetFile, File $peerFile): private function handleDeletion(BeforeNodeDeletedEvent $event, Node $peerFile): void { $deletedFile = $event->getNode(); if ($deletedFile->getMimetype() === 'video/quicktime') { - if (isset($this->pendingDeletion[$peerFile->getId()])) { - unset($this->pendingDeletion[$peerFile->getId()]); + $peerFileId = $peerFile->getId(); + if ($peerFileId === null) { + throw new \LogicException('Invalid peer file given with a null id'); + } + if (isset($this->pendingDeletion[$peerFileId])) { + unset($this->pendingDeletion[$peerFileId]); return; } else { throw new AbortedEventException('Cannot delete the video part of a live photo'); } } else { - $this->pendingDeletion[$deletedFile->getId()] = true; + $deletedFileId = $peerFile->getId(); + if ($deletedFileId === null) { + throw new \LogicException('Invalid deleted file given with a null id'); + } + $this->pendingDeletion[$deletedFileId] = true; try { $peerFile->delete(); } catch (\Throwable $ex) { @@ -243,7 +266,7 @@ private function handleCopyRecursive(Event $event, Node $sourceNode, Node $targe $this->pendingCopies[] = $peerFileId; if ($event instanceof BeforeNodeCopiedEvent) { $this->runMoveOrCopyChecks($sourceNode, $targetNode, $peerFile); - } elseif ($event instanceof NodeCopiedEvent) { + } elseif ($event instanceof NodeCopiedEvent && $peerFile instanceof File) { $this->handleCopy($sourceNode, $targetNode, $peerFile); } $this->pendingCopies = array_diff($this->pendingCopies, [$peerFileId]); diff --git a/apps/files/lib/Service/TagService.php b/apps/files/lib/Service/TagService.php index 95176ce95fae0..92b97538ef2f7 100644 --- a/apps/files/lib/Service/TagService.php +++ b/apps/files/lib/Service/TagService.php @@ -45,6 +45,9 @@ public function updateFileTags($path, $tags) { } $fileId = $this->homeFolder->get($path)->getId(); + if ($fileId === null) { + return []; + } $currentTags = $this->tagger->getTagsForObjects([$fileId]); diff --git a/apps/files/tests/Controller/ConversionApiControllerTest.php b/apps/files/tests/Controller/ConversionApiControllerTest.php index 659fbe1a9560d..5d6f0bc3c381c 100644 --- a/apps/files/tests/Controller/ConversionApiControllerTest.php +++ b/apps/files/tests/Controller/ConversionApiControllerTest.php @@ -43,6 +43,7 @@ protected function setUp(): void { $this->request = $this->createMock(IRequest::class); $this->fileConversionManager = $this->createMock(IConversionManager::class); $this->file = $this->createMock(File::class); + $this->file->method('isReadable')->willReturn(true); $this->l10n = $this->createMock(IL10N::class); $this->user = 'userid'; diff --git a/apps/files/tests/HelperTest.php b/apps/files/tests/HelperTest.php index ba93fa0efdf26..1ce279fcfbad3 100644 --- a/apps/files/tests/HelperTest.php +++ b/apps/files/tests/HelperTest.php @@ -9,12 +9,14 @@ use OC\Files\FileInfo; use OCA\Files\Helper; +use OCP\Files\Mount\IMountPoint; +use OCP\Files\Storage\IStorage; class HelperTest extends \Test\TestCase { - private static function makeFileInfo($name, $size, $mtime, $isDir = false): FileInfo { + private function makeFileInfo($name, $size, $mtime, $isDir = false): FileInfo { return new FileInfo( '/' . $name, - null, + $this->createMock(IStorage::class), '/', [ 'name' => $name, @@ -23,21 +25,21 @@ private static function makeFileInfo($name, $size, $mtime, $isDir = false): File 'type' => $isDir ? 'dir' : 'file', 'mimetype' => $isDir ? 'httpd/unix-directory' : 'application/octet-stream' ], - null + $this->createMock(IMountPoint::class), ); } /** * Returns a file list for testing */ - private static function getTestFileList(): array { + private function getTestFileList(): array { return [ - self::makeFileInfo('a.txt', 4, 2.3 * pow(10, 9)), - self::makeFileInfo('q.txt', 5, 150), - self::makeFileInfo('subdir2', 87, 128, true), - self::makeFileInfo('b.txt', 2.2 * pow(10, 9), 800), - self::makeFileInfo('o.txt', 12, 100), - self::makeFileInfo('subdir', 88, 125, true), + $this->makeFileInfo('a.txt', 4, 2.3 * pow(10, 9)), + $this->makeFileInfo('q.txt', 5, 150), + $this->makeFileInfo('subdir2', 87, 128, true), + $this->makeFileInfo('b.txt', 2.2 * pow(10, 9), 800), + $this->makeFileInfo('o.txt', 12, 100), + $this->makeFileInfo('subdir', 88, 125, true), ]; } @@ -81,7 +83,7 @@ public function testSortByName(string $sort, bool $sortDescending, array $expect if (($sort === 'mtime') && (PHP_INT_SIZE < 8)) { $this->markTestSkipped('Skip mtime sorting on 32bit'); } - $files = self::getTestFileList(); + $files = $this->getTestFileList(); $files = Helper::sortFiles($files, $sort, $sortDescending); $fileNames = []; foreach ($files as $fileInfo) { diff --git a/apps/files_sharing/lib/Command/ListShares.php b/apps/files_sharing/lib/Command/ListShares.php index 2d5cdbf78129b..3c2e53ca58b3d 100644 --- a/apps/files_sharing/lib/Command/ListShares.php +++ b/apps/files_sharing/lib/Command/ListShares.php @@ -90,7 +90,7 @@ private function getFileId(string $file): int { if (is_numeric($file)) { return (int)$file; } - return $this->getFile($file)->getId(); + return $this->getFile($file)->getId() ?? -1; } private function getFile(string $file): Node { diff --git a/apps/files_sharing/lib/Controller/DeletedShareAPIController.php b/apps/files_sharing/lib/Controller/DeletedShareAPIController.php index 1384b2b0646e4..35625f2cb5587 100644 --- a/apps/files_sharing/lib/Controller/DeletedShareAPIController.php +++ b/apps/files_sharing/lib/Controller/DeletedShareAPIController.php @@ -73,7 +73,7 @@ private function formatShare(IShare $share): array { if (!$node) { // fallback to guessing the path $node = $userFolder->get($share->getTarget()); - if ($node === null || $share->getTarget() === '') { + if ($share->getTarget() === '') { throw new NotFoundException(); } } diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 8ed52a1de41d6..0351fb00b5b69 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -157,7 +157,7 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra if (!$node) { // fallback to guessing the path $node = $userFolder->get($share->getTarget()); - if ($node === null || $share->getTarget() === '') { + if ($share->getTarget() === '') { throw new NotFoundException(); } } @@ -200,9 +200,9 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra $result['has_preview'] = $this->previewManager->isAvailable($node); $result['storage_id'] = $node->getStorage()->getId(); $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId(); - $result['item_source'] = $node->getId(); - $result['file_source'] = $node->getId(); - $result['file_parent'] = $node->getParent()->getId(); + $result['item_source'] = $node->getId() ?? -1; + $result['file_source'] = $node->getId() ?? -1; + $result['file_parent'] = $node->getParent()->getId() ?? -1; $result['file_target'] = $share->getTarget(); $result['item_size'] = $node->getSize(); $result['item_mtime'] = $node->getMTime(); @@ -1440,7 +1440,7 @@ public function pendingShares(): DataResponse { if (!$node) { // fallback to guessing the path $node = $userFolder->get($share->getTarget()); - if ($node === null || $share->getTarget() === '') { + if ($share->getTarget() === '') { return null; } } diff --git a/apps/files_sharing/lib/Controller/ShareInfoController.php b/apps/files_sharing/lib/Controller/ShareInfoController.php index b7e79aec8304a..782eda2d2e663 100644 --- a/apps/files_sharing/lib/Controller/ShareInfoController.php +++ b/apps/files_sharing/lib/Controller/ShareInfoController.php @@ -138,8 +138,8 @@ private function parseFolder(Folder $folder, int $permissionMask, int $depth): a private function format(Node $node, int $permissionMask): array { $entry = []; - $entry['id'] = $node->getId(); - $entry['parentId'] = $node->getParent()->getId(); + $entry['id'] = $node->getId() ?? -1; + $entry['parentId'] = $node->getParent()->getId() ?? -1; $entry['mtime'] = $node->getMTime(); $entry['name'] = $node->getName(); diff --git a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php index 6f79420b11338..2874cce45ea89 100644 --- a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php @@ -522,7 +522,7 @@ public function testDeleteSharedWithGroupIDontBelongTo(): void { public function testDeleteShareOwnerless(): void { $ocs = $this->mockFormatShare(); - $mount = $this->createMock(IShareOwnerlessMount::class); + $mount = $this->createMockForIntersectionOfInterfaces([IShareOwnerlessMount::class, IMountPoint::class]); $file = $this->createMock(File::class); $file @@ -609,7 +609,7 @@ public function createShare( ?string $password = null, string $label = '', ?IShareAttributes $attributes = null, - ): MockObject { + ): IShare&MockObject { $share = $this->createMock(IShare::class); $share->method('getId')->willReturn($id); $share->method('getShareType')->willReturn($shareType); @@ -855,6 +855,7 @@ public function testGetShare(array $shareParams, array $result, bool $attributes $mountPoint->method('getMountType')->willReturn(''); $nodeParams = $shareParams[5]; + /** @var \OCP\Files\Node&MockObject $node */ $node = $this->createMock($nodeParams['class']); $node->method('getId')->willReturn($nodeParams['id']); $node->method('getPath')->willReturn($nodeParams['path']); @@ -863,7 +864,7 @@ public function testGetShare(array $shareParams, array $result, bool $attributes $node->method('getSize')->willReturn(123465); $node->method('getMTime')->willReturn(1234567890); $node->method('getMimeType')->willReturn($nodeParams['mimeType']); - $node->method('getMountPoint')->willReturn($mountPoint); + $node->method('getInternalPath')->willReturn(ltrim($nodeParams['path'], '/')); $shareParams[5] = $node; @@ -4011,7 +4012,7 @@ public function testUpdateShareCanIncreasePermissionsIfOwner(): void { public function testUpdateShareOwnerless(): void { $ocs = $this->mockFormatShare(); - $mount = $this->createMock(IShareOwnerlessMount::class); + $mount = $this->createMockForIntersectionOfInterfaces([IShareOwnerlessMount::class, IMountPoint::class]); $file = $this->createMock(File::class); $file @@ -4942,6 +4943,7 @@ function ($user) { $expects['attributes'] = \json_encode($shareParams['attributes']); } if (isset($shareParams['node'])) { + /** @var MockObject&\OCP\Files\Node $node */ $node = $this->createMock($shareParams['node']['class']); $node->method('getMimeType')->willReturn($shareParams['node']['mimeType']); @@ -4951,6 +4953,7 @@ function ($user) { $node->method('getMountPoint')->willReturn($mountPoint); $node->method('getPath')->willReturn($shareParams['node']['path']); + $node->method('getInternalPath')->willReturn(ltrim($shareParams['node']['path'], '/')); $node->method('getId')->willReturn($shareParams['node']['id']); $parent = $this->createMock(Folder::class); @@ -5169,6 +5172,7 @@ public function testFormatRoomShare(array $expects, bool $helperAvailable, array $file->method('getSize')->willReturn(123456); $file->method('getMTime')->willReturn(1234567890); + $file->method('getInternalPath')->willReturn(ltrim('file', '/')); $mountPoint = $this->createMock(IMountPoint::class); $mountPoint->method('getMountType')->willReturn(''); diff --git a/apps/files_trashbin/lib/Listeners/SyncLivePhotosListener.php b/apps/files_trashbin/lib/Listeners/SyncLivePhotosListener.php index 2cb3a94aa1dc8..c5681a4d5df0b 100644 --- a/apps/files_trashbin/lib/Listeners/SyncLivePhotosListener.php +++ b/apps/files_trashbin/lib/Listeners/SyncLivePhotosListener.php @@ -72,8 +72,13 @@ private function handleRestore(BeforeNodeRestoredEvent $event, Node $peerFile): $sourceFile = $event->getSource(); if ($sourceFile->getMimetype() === 'video/quicktime') { - if (isset($this->pendingRestores[$peerFile->getId()])) { - unset($this->pendingRestores[$peerFile->getId()]); + $peerFileId = $peerFile->getId(); + if ($peerFileId === null) { + throw new \LogicException('Invalid peer file given with a null id'); + } + + if (isset($this->pendingRestores[$peerFileId])) { + unset($this->pendingRestores[$peerFileId]); return; } else { $event->abortOperation(new NotPermittedException('Cannot restore the video part of a live photo')); @@ -97,7 +102,12 @@ private function handleRestore(BeforeNodeRestoredEvent $event, Node $peerFile): $event->abortOperation(new NotFoundException("Couldn't find peer file in trashbin")); } - $this->pendingRestores[$sourceFile->getId()] = true; + $sourceFileId = $sourceFile->getId(); + if ($sourceFileId === null) { + throw new \LogicException('Invalid source file given with a null id'); + } + + $this->pendingRestores[$sourceFileId] = true; try { $this->trashManager->restoreItem($trashItem); } catch (\Throwable $ex) { diff --git a/apps/files_trashbin/lib/Sabre/AbstractTrash.php b/apps/files_trashbin/lib/Sabre/AbstractTrash.php index f032395437b9b..9e750ab15d752 100644 --- a/apps/files_trashbin/lib/Sabre/AbstractTrash.php +++ b/apps/files_trashbin/lib/Sabre/AbstractTrash.php @@ -31,7 +31,7 @@ public function getDeletionTime(): int { } public function getFileId(): int { - return $this->data->getId(); + return $this->data->getId() ?? -1; } public function getFileInfo(): FileInfo { diff --git a/apps/files_trashbin/lib/Trash/TrashItem.php b/apps/files_trashbin/lib/Trash/TrashItem.php index 70d5164747f0b..de824adc30ab3 100644 --- a/apps/files_trashbin/lib/Trash/TrashItem.php +++ b/apps/files_trashbin/lib/Trash/TrashItem.php @@ -7,7 +7,10 @@ namespace OCA\Files_Trashbin\Trash; use OCP\Files\FileInfo; +use OCP\Files\Mount\IMountPoint; +use OCP\Files\Storage\IStorage; use OCP\IUser; +use Override; class TrashItem implements ITrashItem { @@ -46,126 +49,150 @@ public function getUser(): IUser { return $this->user; } - public function getEtag() { + public function getDeletedBy(): ?IUser { + return $this->deletedBy; + } + + #[Override] + public function getEtag(): string { return $this->fileInfo->getEtag(); } - public function getSize($includeMounts = true) { + #[Override] + public function getSize(bool $includeMounts = true): int|float { return $this->fileInfo->getSize($includeMounts); } - public function getMtime() { + #[Override] + public function getMtime(): int { return $this->fileInfo->getMtime(); } - public function getName() { + #[Override] + public function getName(): string { return $this->fileInfo->getName(); } - public function getInternalPath() { + #[Override] + public function getInternalPath(): string { return $this->fileInfo->getInternalPath(); } - public function getPath() { + #[Override] + public function getPath(): string { return $this->fileInfo->getPath(); } + #[Override] public function getMimetype(): string { return $this->fileInfo->getMimetype(); } - public function getMimePart() { + #[Override] + public function getMimePart(): string { return $this->fileInfo->getMimePart(); } - public function getStorage() { + #[Override] + public function getStorage(): IStorage { return $this->fileInfo->getStorage(); } - public function getId() { + #[Override] + public function getId(): ?int { return $this->fileInfo->getId(); } - public function isEncrypted() { + #[Override] + public function isEncrypted(): bool { return $this->fileInfo->isEncrypted(); } - public function getPermissions() { + #[Override] + public function getPermissions(): int { return $this->fileInfo->getPermissions(); } - public function getType() { + #[Override] + public function getType(): string { return $this->fileInfo->getType(); } - public function isReadable() { + #[Override] + public function isReadable(): bool { return $this->fileInfo->isReadable(); } - public function isUpdateable() { + #[Override] + public function isUpdateable(): bool { return $this->fileInfo->isUpdateable(); } - public function isCreatable() { + #[Override] + public function isCreatable(): bool { return $this->fileInfo->isCreatable(); } - public function isDeletable() { + #[Override] + public function isDeletable(): bool { return $this->fileInfo->isDeletable(); } - public function isShareable() { + public function isShareable(): bool { return $this->fileInfo->isShareable(); } - public function isShared() { + #[Override] + public function isShared(): bool { return $this->fileInfo->isShared(); } - public function isMounted() { + #[Override] + public function isMounted(): bool { return $this->fileInfo->isMounted(); } - public function getMountPoint() { + #[Override] + public function getMountPoint(): IMountPoint { return $this->fileInfo->getMountPoint(); } - public function getOwner() { + #[Override] + public function getOwner(): ?IUser { return $this->fileInfo->getOwner(); } - public function getChecksum() { + #[Override] + public function getChecksum(): string { return $this->fileInfo->getChecksum(); } + #[Override] public function getExtension(): string { return $this->fileInfo->getExtension(); } + #[Override] public function getTitle(): string { return $this->getOriginalLocation(); } + #[Override] public function getCreationTime(): int { return $this->fileInfo->getCreationTime(); } + #[Override] public function getUploadTime(): int { return $this->fileInfo->getUploadTime(); } + #[Override] public function getParentId(): int { return $this->fileInfo->getParentId(); } - public function getDeletedBy(): ?IUser { - return $this->deletedBy; - } - - /** - * @inheritDoc - * @return array - */ + #[Override] public function getMetadata(): array { return $this->fileInfo->getMetadata(); } diff --git a/apps/files_versions/lib/Listener/FileEventsListener.php b/apps/files_versions/lib/Listener/FileEventsListener.php index c200db7a83d48..936e1b85d45d3 100644 --- a/apps/files_versions/lib/Listener/FileEventsListener.php +++ b/apps/files_versions/lib/Listener/FileEventsListener.php @@ -8,7 +8,6 @@ namespace OCA\Files_Versions\Listener; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use OC\DB\Exceptions\DbalException; use OC\Files\Filesystem; use OC\Files\Mount\MoveableMount; use OC\Files\Node\NonExistingFile; @@ -122,7 +121,12 @@ public function pre_touch_hook(Node $node): void { return; } - $this->nodesTouched[$node->getId()] = $node; + $nodeId = $node->getId(); + if ($nodeId === null) { + return; + } + + $this->nodesTouched[$nodeId] = $node; } public function touch_hook(Node $node): void { @@ -142,13 +146,17 @@ public function touch_hook(Node $node): void { return; } - $previousNode = $this->nodesTouched[$node->getId()] ?? null; + $nodeId = $node->getId(); + if ($nodeId === null) { + throw new \LogicException('Invalid node given'); + } + $previousNode = $this->nodesTouched[$nodeId] ?? null; if ($previousNode === null) { return; } - unset($this->nodesTouched[$node->getId()]); + unset($this->nodesTouched[$nodeId]); try { if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) { @@ -157,15 +165,15 @@ public function touch_hook(Node $node): void { // We update the timestamp of the version entity associated with the previousNode. $this->versionManager->updateVersionEntity($node, $revision, ['timestamp' => $node->getMTime()]); } - } catch (DbalException $ex) { + } catch (Exception $ex) { // Ignore UniqueConstraintViolationException, as we are probably in the middle of a rollback - // Where the previous node would temporary have the mtime of the old version, so the rollback touches it to fix it. - if (!($ex->getPrevious() instanceof UniqueConstraintViolationException)) { + // Where the previous node would temporarily have the mtime of the old version, so the rollback touches it to fix it. + if ($ex->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { throw $ex; } } catch (DoesNotExistException $ex) { // Ignore DoesNotExistException, as we are probably in the middle of a rollback - // Where the previous node would temporary have a wrong mtime, so the rollback touches it to fix it. + // Where the previous node would temporarily have a wrong mtime, so the rollback touches it to fix it. } } @@ -208,8 +216,13 @@ public function write_hook(Node $node): void { $path = $this->getPathForNode($node); $result = Storage::store($path); + $nodeId = $node->getId(); + if ($nodeId === null) { + throw new \LogicException('Invalid node given'); + } + // Store the result of the version creation so it can be used in post_write_hook. - $this->writeHookInfo[$node->getId()] = [ + $this->writeHookInfo[$nodeId] = [ 'previousNode' => $node, 'versionCreated' => $result !== false ]; @@ -235,7 +248,12 @@ public function post_write_hook(Node $node): void { return; } - $writeHookInfo = $this->writeHookInfo[$node->getId()] ?? null; + $nodeId = $node->getId(); + if ($nodeId === null) { + throw new \LogicException('Invalid node given'); + } + + $writeHookInfo = $this->writeHookInfo[$nodeId] ?? null; if ($writeHookInfo === null) { return; diff --git a/apps/files_versions/lib/Storage.php b/apps/files_versions/lib/Storage.php index ca3a130d9b394..e8eb66c2f32ce 100644 --- a/apps/files_versions/lib/Storage.php +++ b/apps/files_versions/lib/Storage.php @@ -885,16 +885,16 @@ public static function expire($filename, $uid) { if ($quota >= 0) { if ($softQuota) { $root = Server::get(IRootFolder::class); - $userFolder = $root->getUserFolder($uid); - if (is_null($userFolder)) { - $availableSpace = 0; - } else { + try { + $userFolder = $root->getUserFolder($uid); $free = $quota - $userFolder->getSize(false); // remaining free space for user if ($free > 0) { $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions } else { $availableSpace = $free - $versionsSize; } + } catch (NoUserException $e) { + $availableSpace = 0; } } else { $availableSpace = $quota; diff --git a/apps/systemtags/lib/Activity/Listener.php b/apps/systemtags/lib/Activity/Listener.php index 93e3f102c94b9..d090198c62c1c 100644 --- a/apps/systemtags/lib/Activity/Listener.php +++ b/apps/systemtags/lib/Activity/Listener.php @@ -136,7 +136,7 @@ public function mapperEvent(MapperEvent $event) { foreach ($mounts as $mount) { $owner = $mount->getUser()->getUID(); $ownerFolder = $this->rootFolder->getUserFolder($owner); - $nodes = $ownerFolder->getById($event->getObjectId()); + $nodes = $ownerFolder->getById((int)$event->getObjectId()); if (!empty($nodes)) { /** @var Node $node */ $node = array_shift($nodes); diff --git a/apps/workflowengine/lib/Entity/File.php b/apps/workflowengine/lib/Entity/File.php index e19c10c17b2e5..8109587aea3a2 100644 --- a/apps/workflowengine/lib/Entity/File.php +++ b/apps/workflowengine/lib/Entity/File.php @@ -136,9 +136,9 @@ protected function getNode(): Node { if (!$this->event instanceof MapperEvent || $this->event->getObjectType() !== 'files') { throw new NotFoundException(); } - $nodes = $this->root->getById((int)$this->event->getObjectId()); - if (is_array($nodes) && isset($nodes[0])) { - $this->node = $nodes[0]; + $node = $this->root->getFirstNodeById((int)$this->event->getObjectId()); + if ($node !== null) { + $this->node = $node; return $this->node; } break; diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index eb736b1e1dd1e..6f4e7223d938a 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -566,11 +566,6 @@ - - - - - @@ -641,14 +636,6 @@ - - - - - - info->getId()]]> - info->getId()]]> - @@ -1821,14 +1808,6 @@ - - - - - - data->getId()]]> - - @@ -3724,44 +3703,11 @@ - - - getStorage()]]> - findByNumericId($id)]]> - findByStorageId($id)]]> - - - - - - - - - - view->hash($type, $this->path, $raw)]]> - - - - - - - - createNode($file->getPath(), $file); - }, $files)]]> - - - - - - - - @@ -3779,64 +3725,6 @@ - - - __call(__FUNCTION__, func_get_args())]]> - - - - - - - - - - - - - - - - - - - parent]]> - - - - - - fileInfo]]> - - - - - - fileInfo]]> - fileInfo]]> - - - - - - mountManager->findByNumericId($numericId)]]> - mountManager->findByStorageId($storageId)]]> - mountManager->findIn($mountPoint)]]> - user]]> - - - - - - - - - user]]> - - - - - diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index beb3d02749dd4..e4395801bead4 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1737,6 +1737,7 @@ 'OC\\Files\\Filesystem' => $baseDir . '/lib/private/Files/Filesystem.php', 'OC\\Files\\Lock\\LockManager' => $baseDir . '/lib/private/Files/Lock/LockManager.php', 'OC\\Files\\Mount\\CacheMountProvider' => $baseDir . '/lib/private/Files/Mount/CacheMountProvider.php', + 'OC\\Files\\Mount\\DummyMountPoint' => $baseDir . '/lib/private/Files/Mount/DummyMountPoint.php', 'OC\\Files\\Mount\\HomeMountPoint' => $baseDir . '/lib/private/Files/Mount/HomeMountPoint.php', 'OC\\Files\\Mount\\LocalHomeMountProvider' => $baseDir . '/lib/private/Files/Mount/LocalHomeMountProvider.php', 'OC\\Files\\Mount\\Manager' => $baseDir . '/lib/private/Files/Mount/Manager.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index ff0f8b124ba7e..d98ab5c7eb1ad 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1778,6 +1778,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Files\\Filesystem' => __DIR__ . '/../../..' . '/lib/private/Files/Filesystem.php', 'OC\\Files\\Lock\\LockManager' => __DIR__ . '/../../..' . '/lib/private/Files/Lock/LockManager.php', 'OC\\Files\\Mount\\CacheMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/CacheMountProvider.php', + 'OC\\Files\\Mount\\DummyMountPoint' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/DummyMountPoint.php', 'OC\\Files\\Mount\\HomeMountPoint' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/HomeMountPoint.php', 'OC\\Files\\Mount\\LocalHomeMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/LocalHomeMountProvider.php', 'OC\\Files\\Mount\\Manager' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/Manager.php', diff --git a/lib/private/Files/AppData/AppData.php b/lib/private/Files/AppData/AppData.php index c13372ae1d9e7..dc303baa7947e 100644 --- a/lib/private/Files/AppData/AppData.php +++ b/lib/private/Files/AppData/AppData.php @@ -149,6 +149,6 @@ public function getDirectoryListing(): array { } public function getId(): int { - return $this->getAppDataFolder()->getId(); + return $this->getAppDataFolder()->getId() ?? -1; } } diff --git a/lib/private/Files/FileInfo.php b/lib/private/Files/FileInfo.php index 967d404b8a4f0..92ca241f84e7f 100644 --- a/lib/private/Files/FileInfo.php +++ b/lib/private/Files/FileInfo.php @@ -12,61 +12,29 @@ use OCA\Files_Sharing\ISharedMountPoint; use OCP\Files\Cache\ICacheEntry; use OCP\Files\Mount\IMountPoint; +use OCP\Files\Storage\IStorage; use OCP\IUser; +use Override; /** * @template-implements \ArrayAccess */ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { private array|ICacheEntry $data; - /** - * @var string - */ - private $path; - - /** - * @var \OC\Files\Storage\Storage $storage - */ - private $storage; - - /** - * @var string - */ - private $internalPath; - - /** - * @var \OCP\Files\Mount\IMountPoint - */ - private $mount; - + private string $path; + private IStorage $storage; + private string $internalPath; + private IMountPoint $mount; private ?IUser $owner; - - /** - * @var string[] - */ + /** @var string[] */ private array $childEtags = []; - - /** - * @var IMountPoint[] - */ + /** @var IMountPoint[] */ private array $subMounts = []; - private bool $subMountsUsed = false; - - /** - * The size of the file/folder without any sub mount - */ + /** The size of the file/folder without any sub mount */ private int|float $rawSize = 0; - /** - * @param string|boolean $path - * @param Storage\Storage $storage - * @param string $internalPath - * @param array|ICacheEntry $data - * @param IMountPoint $mount - * @param ?IUser $owner - */ - public function __construct($path, $storage, $internalPath, $data, $mount, $owner = null) { + public function __construct(string|bool $path, IStorage $storage, string $internalPath, array|ICacheEntry $data, IMountPoint $mount, ?IUser $owner = null) { $this->path = $path; $this->storage = $storage; $this->internalPath = $internalPath; @@ -106,30 +74,23 @@ public function offsetGet(mixed $offset): mixed { }; } - /** - * @return string - */ - public function getPath() { + #[Override] + public function getPath(): string { return $this->path; } - public function getStorage() { + #[Override] + public function getStorage(): IStorage { return $this->storage; } - /** - * @return string - */ - public function getInternalPath() { + #[Override] + public function getInternalPath(): string { return $this->internalPath; } - /** - * Get FileInfo ID or null in case of part file - * - * @return int|null - */ - public function getId() { + #[Override] + public function getId(): ?int { return isset($this->data['fileid']) ? (int)$this->data['fileid'] : null; } @@ -137,40 +98,31 @@ public function getMimetype(): string { return $this->data['mimetype'] ?? 'application/octet-stream'; } - /** - * @return string - */ - public function getMimePart() { + #[Override] + public function getMimePart(): string { return $this->data['mimepart']; } - /** - * @return string - */ - public function getName() { + #[Override] + public function getName(): string { return empty($this->data['name']) ? basename($this->getPath()) : $this->data['name']; } - /** - * @return string - */ - public function getEtag() { + #[Override] + public function getEtag(): string { $this->updateEntryFromSubMounts(); if (count($this->childEtags) > 0) { $combinedEtag = $this->data['etag'] . '::' . implode('::', $this->childEtags); return md5($combinedEtag); } else { - return $this->data['etag']; + return $this->data['etag'] ?? ''; } } - /** - * @param bool $includeMounts - * @return int|float - */ - public function getSize($includeMounts = true) { + #[Override] + public function getSize(bool $includeMounts = true): int|float { if ($includeMounts) { $this->updateEntryFromSubMounts(); @@ -184,18 +136,14 @@ public function getSize($includeMounts = true) { } } - /** - * @return int - */ - public function getMTime() { + #[Override] + public function getMTime(): int { $this->updateEntryFromSubMounts(); return (int)$this->data['mtime']; } - /** - * @return bool - */ - public function isEncrypted() { + #[Override] + public function isEncrypted(): bool { return $this->data['encrypted'] ?? false; } @@ -206,108 +154,81 @@ public function getEncryptedVersion(): int { return isset($this->data['encryptedVersion']) ? (int)$this->data['encryptedVersion'] : 1; } - /** - * @return int - */ - public function getPermissions() { - return (int)$this->data['permissions']; + #[Override] + public function getPermissions(): int { + /** @var \OCP\Constants::PERMISSION_* $permission */ + $permission = (int)$this->data['permissions']; + return $permission; } - /** - * @return string \OCP\Files\FileInfo::TYPE_FILE|\OCP\Files\FileInfo::TYPE_FOLDER - */ - public function getType() { + #[Override] + public function getType(): string { if (!isset($this->data['type'])) { $this->data['type'] = ($this->getMimetype() === self::MIMETYPE_FOLDER) ? self::TYPE_FOLDER : self::TYPE_FILE; } return $this->data['type']; } - public function getData() { + public function getData(): array|ICacheEntry { return $this->data; } - /** - * @param int $permissions - * @return bool - */ - protected function checkPermissions($permissions) { + protected function checkPermissions(int $permissions): bool { return ($this->getPermissions() & $permissions) === $permissions; } - /** - * @return bool - */ - public function isReadable() { + #[Override] + public function isReadable(): bool { return $this->checkPermissions(\OCP\Constants::PERMISSION_READ); } - /** - * @return bool - */ - public function isUpdateable() { + #[Override] + public function isUpdateable(): bool { return $this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE); } - /** - * Check whether new files or folders can be created inside this folder - * - * @return bool - */ - public function isCreatable() { + #[Override] + public function isCreatable(): bool { return $this->checkPermissions(\OCP\Constants::PERMISSION_CREATE); } - /** - * @return bool - */ - public function isDeletable() { + #[Override] + public function isDeletable(): bool { return $this->checkPermissions(\OCP\Constants::PERMISSION_DELETE); } - /** - * @return bool - */ - public function isShareable() { + #[Override] + public function isShareable(): bool { return $this->checkPermissions(\OCP\Constants::PERMISSION_SHARE); } - /** - * Check if a file or folder is shared - * - * @return bool - */ - public function isShared() { + #[Override] + public function isShared(): bool { return $this->mount instanceof ISharedMountPoint; } - public function isMounted() { + #[Override] + public function isMounted(): bool { $isHome = $this->mount instanceof HomeMountPoint; return !$isHome && !$this->isShared(); } - /** - * Get the mountpoint the file belongs to - * - * @return \OCP\Files\Mount\IMountPoint - */ - public function getMountPoint() { + #[Override] + public function getMountPoint(): IMountPoint { return $this->mount; } /** - * Get the owner of the file - * - * @return ?IUser + * Get the owner of the file. */ - public function getOwner() { + public function getOwner(): ?IUser { return $this->owner; } /** * @param IMountPoint[] $mounts */ - public function setSubMounts(array $mounts) { + public function setSubMounts(array $mounts): void { $this->subMounts = $mounts; } @@ -321,21 +242,23 @@ private function updateEntryFromSubMounts(): void { if ($subStorage) { $subCache = $subStorage->getCache(''); $rootEntry = $subCache->get(''); - $this->addSubEntry($rootEntry, $mount->getMountPoint()); + if (!empty($rootEntry)) { + $this->addSubEntry($rootEntry, $mount->getMountPoint()); + } } } } /** - * Add a cache entry which is the child of this folder + * Add a cache entry which is the child of this folder. * - * Sets the size, etag and size to for cross-storage childs + * Sets the size, etag and size to for cross-storage children. * * @param array|ICacheEntry $data cache entry for the child * @param string $entryPath full path of the child entry */ - public function addSubEntry($data, $entryPath) { - if (!$data) { + public function addSubEntry(array|ICacheEntry $data, string $entryPath): void { + if (empty($data)) { return; } $hasUnencryptedSize = isset($data['unencrypted_size']) && $data['unencrypted_size'] > 0; @@ -360,33 +283,32 @@ public function addSubEntry($data, $entryPath) { } } - /** - * @inheritdoc - */ - public function getChecksum() { - return $this->data['checksum']; + #[Override] + public function getChecksum(): string { + return $this->data['checksum'] ?? ''; } + #[Override] public function getExtension(): string { return pathinfo($this->getName(), PATHINFO_EXTENSION); } + #[Override] public function getCreationTime(): int { return (int)$this->data['creation_time']; } + #[Override] public function getUploadTime(): int { return (int)$this->data['upload_time']; } + #[Override] public function getParentId(): int { return $this->data['parent'] ?? -1; } - /** - * @inheritDoc - * @return array - */ + #[Override] public function getMetadata(): array { return $this->data['metadata'] ?? []; } diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php index eb374d9326b3d..9a8dec12797c3 100644 --- a/lib/private/Files/Filesystem.php +++ b/lib/private/Files/Filesystem.php @@ -246,7 +246,7 @@ public static function getMountPoints($path) { * get the storage mounted at $mountPoint * * @param string $mountPoint - * @return \OC\Files\Storage\Storage|null + * @return \OCP\Files\Storage\IStorage|null */ public static function getStorage($mountPoint) { $mount = self::getMountManager()->find($mountPoint); @@ -255,7 +255,7 @@ public static function getStorage($mountPoint) { /** * @param string $id - * @return Mount\MountPoint[] + * @return \OCP\Files\Mount\IMountPoint[] */ public static function getMountByStorageId($id) { return self::getMountManager()->findByStorageId($id); @@ -263,7 +263,7 @@ public static function getMountByStorageId($id) { /** * @param int $id - * @return Mount\MountPoint[] + * @return \OCP\Files\Mount\IMountPoint[] */ public static function getMountByNumericId($id) { return self::getMountManager()->findByNumericId($id); diff --git a/lib/private/Files/Mount/DummyMountPoint.php b/lib/private/Files/Mount/DummyMountPoint.php new file mode 100644 index 0000000000000..c640cb97500e3 --- /dev/null +++ b/lib/private/Files/Mount/DummyMountPoint.php @@ -0,0 +1,70 @@ + new \LogicException('Dummy storage') ]); + } + + public function getStorageId(): string { + return ''; + } + + public function getNumericStorageId(): int { + return -1; + } + + public function getInternalPath($path): string { + return $path; + } + + public function wrapStorage($wrapper): void { + } + + public function getOption($name, $default): mixed { + if ($name === 'previews') { + return false; + } + return $default; + } + + public function getOptions(): array { + return ['previews' => false]; + } + + public function getStorageRootId(): int { + return -1; + } + + public function getMountId(): ?int { + return null; + } + + public function getMountType(): string { + return 'dummy'; + } + + public function getMountProvider(): string { + return ''; + } +} diff --git a/lib/private/Files/Node/File.php b/lib/private/Files/Node/File.php index eb6411d7d137c..f7980005f1d05 100644 --- a/lib/private/Files/Node/File.php +++ b/lib/private/Files/Node/File.php @@ -8,27 +8,18 @@ namespace OC\Files\Node; use OCP\Files\GenericFileException; +use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; -use OCP\Lock\LockedException; +use Override; class File extends Node implements \OCP\Files\File { - /** - * Creates a Folder that represents a non-existing path - * - * @param string $path path - * @return NonExistingFile non-existing node - */ - protected function createNonExistingNode($path) { + #[Override] + protected function createNonExistingNode(string $path): NonExistingFile { return new NonExistingFile($this->root, $this->view, $path); } - /** - * @return string - * @throws NotPermittedException - * @throws GenericFileException - * @throws LockedException - */ - public function getContent() { + #[Override] + public function getContent(): string { if ($this->checkPermissions(\OCP\Constants::PERMISSION_READ)) { $content = $this->view->file_get_contents($this->path); if ($content === false) { @@ -40,13 +31,8 @@ public function getContent() { } } - /** - * @param string|resource $data - * @throws NotPermittedException - * @throws GenericFileException - * @throws LockedException - */ - public function putContent($data) { + #[Override] + public function putContent($data): void { if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) { $this->sendHooks(['preWrite']); if ($this->view->file_put_contents($this->path, $data) === false) { @@ -59,13 +45,8 @@ public function putContent($data) { } } - /** - * @param string $mode - * @return resource|false - * @throws NotPermittedException - * @throws LockedException - */ - public function fopen($mode) { + #[Override] + public function fopen(string $mode) { $preHooks = []; $postHooks = []; $requiredPermissions = \OCP\Constants::PERMISSION_READ; @@ -100,12 +81,8 @@ public function fopen($mode) { } } - /** - * @throws NotPermittedException - * @throws \OCP\Files\InvalidPathException - * @throws \OCP\Files\NotFoundException - */ - public function delete() { + #[Override] + public function delete(): void { if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) { $this->sendHooks(['preDelete']); $fileInfo = $this->getFileInfo(); @@ -118,22 +95,16 @@ public function delete() { } } - /** - * @param string $type - * @param bool $raw - * @return string - */ - public function hash($type, $raw = false) { - return $this->view->hash($type, $this->path, $raw); - } - - /** - * @inheritdoc - */ - public function getChecksum() { - return $this->getFileInfo()->getChecksum(); + #[Override] + public function hash(string $type, bool $raw = false): string { + $hash = $this->view->hash($type, $this->path, $raw); + if ($hash === false) { + throw new NotFoundException('Unable to compute hash of non-exiting file'); + } + return $hash; } + #[Override] public function getExtension(): string { return $this->getFileInfo()->getExtension(); } diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index fef0d634f0562..2052c9036a9d6 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -14,6 +14,7 @@ use OC\Files\Search\SearchQuery; use OC\Files\Utils\PathHelper; use OC\User\LazyUser; +use OCP\Constants; use OCP\Files\Cache\ICacheEntry; use OCP\Files\FileInfo; use OCP\Files\Folder as IFolder; @@ -26,31 +27,23 @@ use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchOrder; use OCP\Files\Search\ISearchQuery; +use OCP\IConfig; use OCP\IUserManager; +use OCP\Server; use Override; class Folder extends Node implements IFolder { private ?IUserManager $userManager = null; - private bool $wasDeleted = false; - /** - * Creates a Folder that represents a non-existing path - * - * @param string $path path - * @return NonExistingFolder non-existing node - */ - protected function createNonExistingNode($path) { + #[Override] + protected function createNonExistingNode(string $path): NonExistingFolder { return new NonExistingFolder($this->root, $this->view, $path); } - /** - * @param string $path path relative to the folder - * @return string - * @throws \OCP\Files\NotPermittedException - */ - public function getFullPath($path) { + #[Override] + public function getFullPath(string $path): string { $path = $this->normalizePath($path); if (!$this->isValidPath($path)) { throw new NotPermittedException('Invalid path "' . $path . '"'); @@ -58,31 +51,18 @@ public function getFullPath($path) { return $this->path . $path; } - /** - * @param string $path - * @return string|null - */ - public function getRelativePath($path) { + #[Override] + public function getRelativePath(string $path): ?string { return PathHelper::getRelativePath($this->getPath(), $path); } - /** - * check if a node is a (grand-)child of the folder - * - * @param \OC\Files\Node\Node $node - * @return bool - */ - public function isSubNode($node) { + #[Override] + public function isSubNode(INode $node): bool { return str_starts_with($node->getPath(), $this->path . '/'); } - /** - * get the content of this directory - * - * @return Node[] - * @throws \OCP\Files\NotFoundException - */ - public function getDirectoryListing() { + #[Override] + public function getDirectoryListing(): array { $folderContent = $this->view->getDirectoryContent($this->path, '', $this->getFileInfo(false)); return array_map(function (FileInfo $info) { @@ -108,11 +88,11 @@ protected function createNode(string $path, ?FileInfo $info = null, bool $infoHa } } - public function get($path) { + public function get(string $path): INode { return $this->root->get($this->getFullPath($path)); } - public function nodeExists($path) { + public function nodeExists(string $path): bool { try { $this->get($path); return true; @@ -121,13 +101,9 @@ public function nodeExists($path) { } } - /** - * @param string $path - * @return \OC\Files\Node\Folder - * @throws \OCP\Files\NotPermittedException - */ - public function newFolder($path) { - if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) { + #[Override] + public function newFolder(string $path): self { + if ($this->checkPermissions(Constants::PERMISSION_CREATE)) { $fullPath = $this->getFullPath($path); $nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath); $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]); @@ -156,18 +132,13 @@ public function newFolder($path) { } } - /** - * @param string $path - * @param string | resource | null $content - * @return \OC\Files\Node\File - * @throws \OCP\Files\NotPermittedException - */ - public function newFile($path, $content = null) { + #[Override] + public function newFile(string $path, $content = null): File { if ($path === '') { throw new NotPermittedException('Could not create as provided path is empty'); } $this->recreateIfNeeded(); - if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) { + if ($this->checkPermissions(Constants::PERMISSION_CREATE)) { $fullPath = $this->getFullPath($path); $nonExisting = new NonExistingFile($this->root, $this->view, $fullPath); $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]); @@ -190,20 +161,16 @@ private function queryFromOperator(ISearchOperator $operator, ?string $uid = nul if ($uid === null) { $user = null; } else { - /** @var IUserManager $userManager */ - $userManager = \OCP\Server::get(IUserManager::class); - $user = $userManager->get($uid); + if ($this->userManager === null) { + $this->userManager = Server::get(IUserManager::class); + } + $user = $this->userManager->get($uid); } return new SearchQuery($operator, $limit, $offset, [], $user); } - /** - * search for files with the name matching $query - * - * @param string|ISearchQuery $query - * @return \OC\Files\Node\Node[] - */ - public function search($query) { + #[Override] + public function search(string|ISearchQuery $query): array { if (is_string($query)) { $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%' . $query . '%')); } @@ -217,7 +184,7 @@ public function search($query) { } /** @var QuerySearchHelper $searchHelper */ - $searchHelper = \OC::$server->get(QuerySearchHelper::class); + $searchHelper = Server::get(QuerySearchHelper::class); [$caches, $mountByMountPoint] = $searchHelper->getCachesAndMountPointsForSearch($this->root, $this->path, $limitToHome); $resultsPerCache = $searchHelper->searchInCaches($query, $caches); @@ -264,7 +231,7 @@ private function cacheEntryToFileInfo(IMountPoint $mount, string $appendRoot, IC if ($ownerId !== false) { // Cache the user manager (for performance) if ($this->userManager === null) { - $this->userManager = \OCP\Server::get(IUserManager::class); + $this->userManager = Server::get(IUserManager::class); } $owner = new LazyUser($ownerId, $this->userManager); } @@ -279,13 +246,8 @@ private function cacheEntryToFileInfo(IMountPoint $mount, string $appendRoot, IC ); } - /** - * search for files by mimetype - * - * @param string $mimetype - * @return Node[] - */ - public function searchByMime($mimetype) { + #[Override] + public function searchByMime(string $mimetype): array { if (!str_contains($mimetype, '/')) { $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%')); } else { @@ -294,37 +256,30 @@ public function searchByMime($mimetype) { return $this->search($query); } - /** - * search for files by tag - * - * @param string|int $tag name or tag id - * @param string $userId owner of the tags - * @return Node[] - */ - public function searchByTag($tag, $userId) { + #[Override] + public function searchByTag(string|int $tag, string $userId): array { $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'tagname', $tag), $userId); return $this->search($query); } + #[Override] public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0): array { $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'systemtag', $tagName), $userId, $limit, $offset); return $this->search($query); } - /** - * @param int $id - * @return \OCP\Files\Node[] - */ - public function getById($id) { - return $this->root->getByIdInPath((int)$id, $this->getPath()); + #[Override] + public function getById(int $id): array { + return $this->root->getByIdInPath($id, $this->getPath()); } - public function getFirstNodeById(int $id): ?\OCP\Files\Node { + #[Override] + public function getFirstNodeById(int $id): ?INode { return $this->root->getFirstNodeByIdInPath($id, $this->getPath()); } public function getAppDataDirectoryName(): string { - $instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid'); + $instanceId = Server::get(IConfig::class)->getSystemValueString('instanceid'); return 'appdata_' . $instanceId; } @@ -336,9 +291,6 @@ public function getAppDataDirectoryName(): string { * So in that case we directly check the mount of the root if it contains * the id. If it does we check if the path is inside the path we are working * in. - * - * @param int $id - * @return array */ protected function getByIdInRootMount(int $id): array { if (!method_exists($this->root, 'createNode')) { @@ -371,12 +323,14 @@ protected function getByIdInRootMount(int $id): array { ))]; } - public function getFreeSpace() { + #[Override] + public function getFreeSpace(): float|int|false { return $this->view->free_space($this->path); } - public function delete() { - if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) { + #[Override] + public function delete(): void { + if ($this->checkPermissions(Constants::PERMISSION_DELETE)) { $this->sendHooks(['preDelete']); $fileInfo = $this->getFileInfo(); $this->view->rmdir($this->path); @@ -388,27 +342,21 @@ public function delete() { } } - /** - * Add a suffix to the name in case the file exists - * - * @param string $filename - * @return string - * @throws NotPermittedException - */ - public function getNonExistingName($filename) { + #[Override] + public function getNonExistingName(string $name): string { $path = $this->getPath(); if ($path === '/') { $path = ''; } - if ($pos = strrpos($filename, '.')) { - $name = substr($filename, 0, $pos); - $ext = substr($filename, $pos); + if ($pos = strrpos($name, '.')) { + $name = substr($name, 0, $pos); + $ext = substr($name, $pos); } else { - $name = $filename; + $name = $name; $ext = ''; } - $newpath = $path . '/' . $filename; + $newpath = $path . '/' . $name; if ($this->view->file_exists($newpath)) { if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) { /** @var array, array> $matches */ @@ -437,12 +385,8 @@ public function getNonExistingName($filename) { return trim($this->getRelativePath($newpath), '/'); } - /** - * @param int $limit - * @param int $offset - * @return INode[] - */ - public function getRecent($limit, $offset = 0) { + #[Override] + public function getRecent(int $limit, int $offset = 0): array { $filterOutNonEmptyFolder = new SearchBinaryOperator( // filter out non empty folders ISearchBinaryOperator::OPERATOR_OR, @@ -505,7 +449,8 @@ public function getRecent($limit, $offset = 0) { return $this->search($query); } - public function verifyPath($fileName, $readonly = false): void { + #[Override] + public function verifyPath(string $fileName, bool $readonly = false): void { $this->view->verifyPath( $this->getPath(), $fileName, diff --git a/lib/private/Files/Node/LazyFolder.php b/lib/private/Files/Node/LazyFolder.php index c23a7d03ada9e..4204bf97b1782 100644 --- a/lib/private/Files/Node/LazyFolder.php +++ b/lib/private/Files/Node/LazyFolder.php @@ -14,6 +14,9 @@ use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotPermittedException; +use OCP\Files\Search\ISearchQuery; +use OCP\Files\Storage\IStorage; +use OCP\IUser; use Override; /** @@ -32,7 +35,6 @@ class LazyFolder implements Folder { protected array $data; /** - * @param IRootFolder $rootFolder * @param \Closure(): Folder $folderClosure * @param array $data */ @@ -65,77 +67,8 @@ public function __call($method, $args) { return call_user_func_array([$this->getRealFolder(), $method], $args); } - /** - * @inheritDoc - */ - public function getUser() { - return $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @inheritDoc - */ - public function listen($scope, $method, callable $callback) { - $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @inheritDoc - */ - public function removeListener($scope = null, $method = null, ?callable $callback = null) { - $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @inheritDoc - */ - public function emit($scope, $method, $arguments = []) { - $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @inheritDoc - */ - public function mount($storage, $mountPoint, $arguments = []) { - $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @inheritDoc - */ - public function getMount(string $mountPoint): IMountPoint { - return $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @return IMountPoint[] - */ - public function getMountsIn(string $mountPoint): array { - return $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @inheritDoc - */ - public function getMountByStorageId($storageId) { - return $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @inheritDoc - */ - public function getMountByNumericStorageId($numericId) { - return $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @inheritDoc - */ - public function unMount($mount) { - $this->__call(__FUNCTION__, func_get_args()); - } - - public function get($path) { + #[Override] + public function get(string $path): \OCP\Files\Node { return $this->getRootFolder()->get($this->getFullPath($path)); } @@ -144,166 +77,128 @@ public function getOrCreateFolder(string $path, int $maxRetries = 5): Folder { return $this->getRootFolder()->getOrCreateFolder($this->getFullPath($path), $maxRetries); } - /** - * @inheritDoc - */ - public function rename($targetPath) { + #[Override] + public function move(string $targetPath): \OCP\Files\Node { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function delete() { - return $this->__call(__FUNCTION__, func_get_args()); + #[Override] + public function delete(): void { + $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function copy($targetPath) { + #[Override] + public function copy(string $targetPath): \OCP\Files\Node { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function touch($mtime = null) { + #[Override] + public function touch(?int $mtime = null): void { $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getStorage() { + #[Override] + public function getStorage(): IStorage { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getPath() { + #[Override] + public function getPath(): string { if (isset($this->data['path'])) { return $this->data['path']; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getInternalPath() { + #[Override] + public function getInternalPath(): string { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getId() { + #[Override] + public function getId(): int { if (isset($this->data['fileid'])) { return $this->data['fileid']; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function stat() { + #[Override] + public function stat(): array { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getMTime() { + #[Override] + public function getMTime(): int { if (isset($this->data['mtime'])) { return $this->data['mtime']; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getSize($includeMounts = true): int|float { + #[Override] + public function getSize(bool $includeMounts = true): int|float { if (isset($this->data['size'])) { return $this->data['size']; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getEtag() { + #[Override] + public function getEtag(): string { if (isset($this->data['etag'])) { return $this->data['etag']; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getPermissions() { + #[Override] + public function getPermissions(): int { if (isset($this->data['permissions'])) { return $this->data['permissions']; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function isReadable() { + #[Override] + public function isReadable(): bool { if (isset($this->data['permissions'])) { return ($this->data['permissions'] & Constants::PERMISSION_READ) == Constants::PERMISSION_READ; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function isUpdateable() { + #[Override] + public function isUpdateable(): bool { if (isset($this->data['permissions'])) { return ($this->data['permissions'] & Constants::PERMISSION_UPDATE) == Constants::PERMISSION_UPDATE; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function isDeletable() { + #[Override] + public function isDeletable(): bool { if (isset($this->data['permissions'])) { return ($this->data['permissions'] & Constants::PERMISSION_DELETE) == Constants::PERMISSION_DELETE; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function isShareable() { + #[Override] + public function isShareable(): bool { if (isset($this->data['permissions'])) { return ($this->data['permissions'] & Constants::PERMISSION_SHARE) == Constants::PERMISSION_SHARE; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getParent() { + #[Override] + public function getParent(): IRootFolder|\OCP\Files\Folder { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getName() { + #[Override] + public function getName(): string { if (isset($this->data['path'])) { return basename($this->data['path']); } @@ -313,13 +208,7 @@ public function getName() { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getUserFolder($userId) { - return $this->__call(__FUNCTION__, func_get_args()); - } - + #[Override] public function getMimetype(): string { if (isset($this->data['mimetype'])) { return $this->data['mimetype']; @@ -327,10 +216,8 @@ public function getMimetype(): string { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getMimePart() { + #[Override] + public function getMimePart(): string { if (isset($this->data['mimetype'])) { [$part,] = explode('/', $this->data['mimetype']); return $part; @@ -338,66 +225,51 @@ public function getMimePart() { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function isEncrypted() { + #[Override] + public function isEncrypted(): bool { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getType() { + #[Override] + public function getType(): string { if (isset($this->data['type'])) { return $this->data['type']; } return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function isShared() { + #[Override] + public function isShared(): bool { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function isMounted() { + #[Override] + public function isMounted(): bool { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getMountPoint() { + #[Override] + public function getMountPoint(): IMountPoint { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getOwner() { + #[Override] + public function getOwner(): ?IUser { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getChecksum() { + #[Override] + public function getChecksum(): string { return $this->__call(__FUNCTION__, func_get_args()); } + #[Override] public function getExtension(): string { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getFullPath($path) { + #[Override] + public function getFullPath(string $path): string { if (isset($this->data['path'])) { $path = PathHelper::normalizePath($path); if (!Filesystem::isValidPath($path)) { @@ -408,148 +280,112 @@ public function getFullPath($path) { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function isSubNode($node) { + #[Override] + public function isSubNode(\OCP\Files\Node $node): bool { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getDirectoryListing() { + #[Override] + public function getDirectoryListing(): array { return $this->__call(__FUNCTION__, func_get_args()); } - public function nodeExists($path) { + #[Override] + public function nodeExists(string $path): bool { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function newFolder($path) { + #[Override] + public function newFolder(string $path): \OCP\Files\Folder { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function newFile($path, $content = null) { + #[Override] + public function newFile(string $path, $content = null): \OCP\Files\File { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function search($query) { + #[Override] + public function search(string|ISearchQuery $query): array { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function searchByMime($mimetype) { + #[Override] + public function searchByMime(string $mimetype): array { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function searchByTag($tag, $userId) { + #[Override] + public function searchByTag(int|string $tag, string $userId): array { return $this->__call(__FUNCTION__, func_get_args()); } - public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0) { + #[Override] + public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0): array { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getById($id) { - return $this->getRootFolder()->getByIdInPath((int)$id, $this->getPath()); + #[Override] + public function getById(int $id): array { + return $this->getRootFolder()->getByIdInPath($id, $this->getPath()); } + #[Override] public function getFirstNodeById(int $id): ?\OCP\Files\Node { return $this->getRootFolder()->getFirstNodeByIdInPath($id, $this->getPath()); } - /** - * @inheritDoc - */ - public function getFreeSpace() { - return $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @inheritDoc - */ - public function isCreatable() { + #[Override] + public function getFreeSpace(): int|float|false { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getNonExistingName($filename) { + #[Override] + public function isCreatable(): bool { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function move($targetPath) { + #[Override] + public function getNonExistingName(string $name): string { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function lock($type) { - return $this->__call(__FUNCTION__, func_get_args()); + #[Override] + public function lock(int $type): void { + $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function changeLock($targetType) { - return $this->__call(__FUNCTION__, func_get_args()); + #[Override] + public function changeLock(int $targetType): void { + $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function unlock($type) { - return $this->__call(__FUNCTION__, func_get_args()); + #[Override] + public function unlock(int $type): void { + $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getRecent($limit, $offset = 0) { + #[Override] + public function getRecent(int $limit, int $offset = 0): array { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ + #[Override] public function getCreationTime(): int { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ + #[Override] public function getUploadTime(): int { return $this->__call(__FUNCTION__, func_get_args()); } - public function getRelativePath($path) { + #[Override] + public function getRelativePath(string $path): ?string { return PathHelper::getRelativePath($this->getPath(), $path); } + #[Override] public function getParentId(): int { if (isset($this->data['parent'])) { return $this->data['parent']; @@ -557,15 +393,13 @@ public function getParentId(): int { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - * @return array - */ + #[Override] public function getMetadata(): array { return $this->data['metadata'] ?? $this->__call(__FUNCTION__, func_get_args()); } - public function verifyPath($fileName, $readonly = false): void { + #[Override] + public function verifyPath(string $fileName, $readonly = false): void { $this->__call(__FUNCTION__, func_get_args()); } } diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php index bc3f3a2e80f38..5259f1ceabb6e 100644 --- a/lib/private/Files/Node/LazyRoot.php +++ b/lib/private/Files/Node/LazyRoot.php @@ -12,6 +12,7 @@ use OCP\Files\Mount\IMountPoint; use OCP\Files\Node; use OCP\Files\Node as INode; +use Override; /** * Class LazyRoot @@ -34,23 +35,48 @@ protected function getRootFolder(): IRootFolder { return $folder; } - public function getUserFolder($userId) { + #[Override] + public function listen($scope, $method, callable $callback): void { + $this->__call(__FUNCTION__, func_get_args()); + } + + #[Override] + public function removeListener($scope = null, $method = null, ?callable $callback = null): void { + $this->__call(__FUNCTION__, func_get_args()); + } + + #[Override] + public function getUserFolder(string $userId): \OCP\Files\Folder { return $this->__call(__FUNCTION__, func_get_args()); } - public function getByIdInPath(int $id, string $path) { + #[Override] + public function getByIdInPath(int $id, string $path): array { return $this->__call(__FUNCTION__, func_get_args()); } + #[Override] public function getFirstNodeByIdInPath(int $id, string $path): ?Node { return $this->__call(__FUNCTION__, func_get_args()); } + #[Override] public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode { return $this->getRootFolder()->getNodeFromCacheEntryAndMount($cacheEntry, $mountPoint); } + #[Override] public function getAppDataDirectoryName(): string { return $this->__call(__FUNCTION__, func_get_args()); } + + #[Override] + public function getMountsIn(string $mountPoint): array { + return $this->__call(__FUNCTION__, func_get_args()); + } + + #[Override] + public function getMount(string $mountPoint): IMountPoint { + return $this->__call(__FUNCTION__, func_get_args()); + } } diff --git a/lib/private/Files/Node/LazyUserFolder.php b/lib/private/Files/Node/LazyUserFolder.php index d7afb199b913f..9a878f8300d8d 100644 --- a/lib/private/Files/Node/LazyUserFolder.php +++ b/lib/private/Files/Node/LazyUserFolder.php @@ -14,6 +14,7 @@ use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountManager; +use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; use OCP\IUser; use Psr\Log\LoggerInterface; @@ -53,6 +54,7 @@ function () use ($user): Folder { ]); throw $e; } + /** @var Folder $node */ return $node; } catch (NotFoundException $e) { if (!$this->getRootFolder()->nodeExists('/' . $user->getUID())) { @@ -65,7 +67,7 @@ function () use ($user): Folder { ); } - public function getMountPoint() { + public function getMountPoint(): IMountPoint { if ($this->folder !== null) { return $this->folder->getMountPoint(); } diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index fd8d84883d964..c4d332d019e7b 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -10,75 +10,56 @@ use OC\Files\Filesystem; use OC\Files\Mount\MoveableMount; use OC\Files\Utils\PathHelper; +use OC\Files\View; use OCP\EventDispatcher\GenericEvent; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\FileInfo; use OCP\Files\InvalidPathException; use OCP\Files\IRootFolder; +use OCP\Files\Mount\IMountPoint; use OCP\Files\Node as INode; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; -use OCP\Lock\LockedException; +use OCP\Files\Storage\IStorage; +use OCP\IUser; use OCP\PreConditionNotMetException; +use OCP\Server; +use Override; -// FIXME: this class really should be abstract (+1) -class Node implements INode { +abstract class Node implements INode { /** - * @var \OC\Files\View $view + * @param string $path Absolute path to the node (e.g. /admin/files/folder/file) + * @throws PreConditionNotMetException */ - protected $view; - - protected IRootFolder $root; - - /** - * @var string $path Absolute path to the node (e.g. /admin/files/folder/file) - */ - protected $path; - - protected ?FileInfo $fileInfo; - - protected ?INode $parent; - - private bool $infoHasSubMountsIncluded; - - /** - * @param \OC\Files\View $view - * @param \OCP\Files\IRootFolder $root - * @param string $path - * @param FileInfo $fileInfo - */ - public function __construct(IRootFolder $root, $view, $path, $fileInfo = null, ?INode $parent = null, bool $infoHasSubMountsIncluded = true) { + public function __construct( + protected IRootFolder $root, + protected View $view, + protected string $path, + protected ?FileInfo $fileInfo = null, + protected ?\OCP\Files\Folder $parent = null, + protected bool $infoHasSubMountsIncluded = true, + ) { if (Filesystem::normalizePath($view->getRoot()) !== '/') { throw new PreConditionNotMetException('The view passed to the node should not have any fake root set'); } - $this->view = $view; - $this->root = $root; - $this->path = $path; - $this->fileInfo = $fileInfo; - $this->parent = $parent; - $this->infoHasSubMountsIncluded = $infoHasSubMountsIncluded; } /** - * Creates a Node of the same type that represents a non-existing path + * Creates a Node of the same type that represents a non-existing path. * - * @param string $path path - * @return Node non-existing node * @throws \Exception */ - protected function createNonExistingNode($path) { - throw new \Exception('Must be implemented by subclasses'); - } + abstract protected function createNonExistingNode(string $path): INode; /** - * Returns the matching file info + * Returns the matching file info. * - * @return FileInfo * @throws InvalidPathException * @throws NotFoundException */ - public function getFileInfo(bool $includeMountPoint = true) { - if (!$this->fileInfo) { + public function getFileInfo(bool $includeMountPoint = true): FileInfo { + $fileInfo = $this->fileInfo; + if (!$fileInfo) { if (!Filesystem::isValidPath($this->path)) { throw new InvalidPathException(); } @@ -95,16 +76,15 @@ public function getFileInfo(bool $includeMountPoint = true) { } $this->infoHasSubMountsIncluded = true; } - return $this->fileInfo; + return $fileInfo; } /** * @param string[] $hooks */ - protected function sendHooks($hooks, ?array $args = null) { + protected function sendHooks(array $hooks, ?array $args = null): void { $args = !empty($args) ? $args : [$this]; - /** @var IEventDispatcher $dispatcher */ - $dispatcher = \OC::$server->get(IEventDispatcher::class); + $dispatcher = Server::get(IEventDispatcher::class); foreach ($hooks as $hook) { if (method_exists($this->root, 'emit')) { $this->root->emit('\OC\Files', $hook, $args); @@ -121,30 +101,24 @@ protected function sendHooks($hooks, ?array $args = null) { } /** - * @param int $permissions - * @return bool * @throws InvalidPathException * @throws NotFoundException */ - protected function checkPermissions($permissions) { + protected function checkPermissions(int $permissions): bool { return ($this->getPermissions() & $permissions) === $permissions; } - public function delete() { + #[Override] + public function delete(): void { } - /** - * @param int $mtime - * @throws InvalidPathException - * @throws NotFoundException - * @throws NotPermittedException - */ - public function touch($mtime = null) { + #[Override] + public function touch(?int $mtime = null): void { if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) { $this->sendHooks(['preTouch']); $this->view->touch($this->path, $mtime); $this->sendHooks(['postTouch']); - if ($this->fileInfo) { + if ($this->fileInfo instanceof \OC\Files\FileInfo) { if (is_null($mtime)) { $mtime = time(); } @@ -155,7 +129,8 @@ public function touch($mtime = null) { } } - public function getStorage() { + #[Override] + public function getStorage(): IStorage { $storage = $this->getMountPoint()->getStorage(); if (!$storage) { throw new \Exception('No storage for node'); @@ -163,119 +138,77 @@ public function getStorage() { return $storage; } - /** - * @return string - */ - public function getPath() { + #[Override] + public function getPath(): string { return $this->path; } - /** - * @return string - */ - public function getInternalPath() { + #[Override] + public function getInternalPath(): string { return $this->getFileInfo(false)->getInternalPath(); } /** - * @return int * @throws InvalidPathException * @throws NotFoundException */ - public function getId() { - return $this->getFileInfo(false)->getId() ?? -1; + #[Override] + public function getId(): ?int { + return $this->getFileInfo(false)->getId(); } - /** - * @return array - */ - public function stat() { + #[Override] + public function stat(): array|false { return $this->view->stat($this->path); } - /** - * @return int - * @throws InvalidPathException - * @throws NotFoundException - */ - public function getMTime() { + #[Override] + public function getMTime(): int { return $this->getFileInfo()->getMTime(); } - /** - * @param bool $includeMounts - * @return int|float - * @throws InvalidPathException - * @throws NotFoundException - */ - public function getSize($includeMounts = true): int|float { + #[Override] + public function getSize(bool $includeMounts = true): int|float { return $this->getFileInfo()->getSize($includeMounts); } - /** - * @return string - * @throws InvalidPathException - * @throws NotFoundException - */ - public function getEtag() { + #[Override] + public function getEtag(): string { return $this->getFileInfo()->getEtag(); } - /** - * @return int - * @throws InvalidPathException - * @throws NotFoundException - */ - public function getPermissions() { + #[Override] + public function getPermissions(): int { return $this->getFileInfo(false)->getPermissions(); } - /** - * @return bool - * @throws InvalidPathException - * @throws NotFoundException - */ - public function isReadable() { + #[Override] + public function isReadable(): bool { return $this->getFileInfo(false)->isReadable(); } - /** - * @return bool - * @throws InvalidPathException - * @throws NotFoundException - */ - public function isUpdateable() { + #[Override] + public function isUpdateable(): bool { return $this->getFileInfo(false)->isUpdateable(); } - /** - * @return bool - * @throws InvalidPathException - * @throws NotFoundException - */ - public function isDeletable() { + #[Override] + public function isDeletable(): bool { return $this->getFileInfo(false)->isDeletable(); } - /** - * @return bool - * @throws InvalidPathException - * @throws NotFoundException - */ - public function isShareable() { + #[Override] + public function isShareable(): bool { return $this->getFileInfo(false)->isShareable(); } - /** - * @return bool - * @throws InvalidPathException - * @throws NotFoundException - */ - public function isCreatable() { + #[Override] + public function isCreatable(): bool { return $this->getFileInfo(false)->isCreatable(); } - public function getParent(): INode|IRootFolder { + #[Override] + public function getParent(): \OCP\Files\Folder { if ($this->parent === null) { $newPath = dirname($this->path); if ($newPath === '' || $newPath === '.' || $newPath === '/') { @@ -286,8 +219,9 @@ public function getParent(): INode|IRootFolder { try { $fileInfo = $this->getFileInfo(); } catch (NotFoundException) { - $this->parent = $this->root->get($newPath); - /** @var \OCP\Files\Folder $this->parent */ + /** @var \OCP\Files\Folder $parent */ + $parent = $this->root->get($newPath); + $this->parent = $parent; return $this->parent; } @@ -306,102 +240,84 @@ public function getParent(): INode|IRootFolder { return $this->parent; } - /** - * @return string - */ - public function getName() { + #[Override] + public function getName(): string { return basename($this->path); } - /** - * @param string $path - * @return string - */ - protected function normalizePath($path) { + protected function normalizePath(string $path): string { return PathHelper::normalizePath($path); } /** - * check if the requested path is valid - * - * @param string $path - * @return bool + * Check if the requested path is valid. */ - public function isValidPath($path) { + public function isValidPath(string $path): bool { return Filesystem::isValidPath($path); } - public function isMounted() { + #[Override] + public function isMounted(): bool { return $this->getFileInfo(false)->isMounted(); } - public function isShared() { + #[Override] + public function isShared(): bool { return $this->getFileInfo(false)->isShared(); } + #[Override] public function getMimeType(): string { return $this->getFileInfo(false)->getMimetype(); } - public function getMimePart() { + #[Override] + public function getMimePart(): string { return $this->getFileInfo(false)->getMimePart(); } - public function getType() { + #[Override] + public function getType(): string { return $this->getFileInfo(false)->getType(); } - public function isEncrypted() { + #[Override] + public function isEncrypted(): bool { return $this->getFileInfo(false)->isEncrypted(); } - public function getMountPoint() { + #[Override] + public function getMountPoint(): IMountPoint { return $this->getFileInfo(false)->getMountPoint(); } - public function getOwner() { + #[Override] + public function getOwner(): ?IUser { return $this->getFileInfo(false)->getOwner(); } - public function getChecksum() { - } - + #[Override] public function getExtension(): string { return $this->getFileInfo(false)->getExtension(); } - /** - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE - * @throws LockedException - */ - public function lock($type) { + #[Override] + public function lock(int $type): void { $this->view->lockFile($this->path, $type); } - /** - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE - * @throws LockedException - */ - public function changeLock($type) { - $this->view->changeLock($this->path, $type); + #[Override] + public function changeLock(int $targetType): void { + $this->view->changeLock($this->path, $targetType); } - /** - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE - * @throws LockedException - */ - public function unlock($type) { + #[Override] + public function unlock(int $type): void { $this->view->unlockFile($this->path, $type); } - /** - * @param string $targetPath - * @return INode - * @throws InvalidPathException - * @throws NotFoundException - * @throws NotPermittedException if copy not allowed or failed - */ - public function copy($targetPath) { + #[Override] + public function copy(string $targetPath): INode { $targetPath = $this->normalizePath($targetPath); $parent = $this->root->get(dirname($targetPath)); if ($parent instanceof Folder && $this->isValidPath($targetPath) && $parent->isCreatable()) { @@ -420,16 +336,10 @@ public function copy($targetPath) { } } - /** - * @param string $targetPath - * @return INode - * @throws InvalidPathException - * @throws NotFoundException - * @throws NotPermittedException if move not allowed or failed - * @throws LockedException - */ - public function move($targetPath) { + #[Override] + public function move(string $targetPath): INode { $targetPath = $this->normalizePath($targetPath); + $parent = $this->root->get(dirname($targetPath)); if ( ($parent instanceof Folder) @@ -450,12 +360,10 @@ public function move($targetPath) { } $mountPoint = $this->getMountPoint(); - if ($mountPoint) { - // update the cached fileinfo with the new (internal) path - /** @var \OC\Files\FileInfo $oldFileInfo */ - $oldFileInfo = $this->getFileInfo(); - $this->fileInfo = new \OC\Files\FileInfo($targetPath, $oldFileInfo->getStorage(), $mountPoint->getInternalPath($targetPath), $oldFileInfo->getData(), $mountPoint, $oldFileInfo->getOwner()); - } + // update the cached fileinfo with the new (internal) path + /** @var \OC\Files\FileInfo $oldFileInfo */ + $oldFileInfo = $this->getFileInfo(); + $this->fileInfo = new \OC\Files\FileInfo($targetPath, $oldFileInfo->getStorage(), $mountPoint->getInternalPath($targetPath), $oldFileInfo->getData(), $mountPoint, $oldFileInfo->getOwner()); $targetNode = $this->root->get($targetPath); $this->sendHooks(['postRename'], [$this, $targetNode]); @@ -467,23 +375,28 @@ public function move($targetPath) { } } + #[Override] public function getCreationTime(): int { return $this->getFileInfo()->getCreationTime(); } + #[Override] public function getUploadTime(): int { return $this->getFileInfo()->getUploadTime(); } + #[Override] public function getParentId(): int { return $this->fileInfo->getParentId(); } - /** - * @inheritDoc - * @return array - */ + #[Override] public function getMetadata(): array { return $this->fileInfo->getMetadata(); } + + #[Override] + public function getChecksum(): string { + return $this->getFileInfo()->getChecksum(); + } } diff --git a/lib/private/Files/Node/NonExistingFile.php b/lib/private/Files/Node/NonExistingFile.php index 7fb375b941adc..82b11e4049b2e 100644 --- a/lib/private/Files/Node/NonExistingFile.php +++ b/lib/private/Files/Node/NonExistingFile.php @@ -8,29 +8,31 @@ namespace OC\Files\Node; use OCP\Files\NotFoundException; +use Override; class NonExistingFile extends File { - /** - * @param string $newPath - * @throws \OCP\Files\NotFoundException - */ - public function rename($newPath) { + #[Override] + public function move(string $targetPath): \OCP\Files\Node { throw new NotFoundException(); } - public function delete() { + #[Override] + public function delete(): void { throw new NotFoundException(); } - public function copy($targetPath) { + #[Override] + public function copy(string $targetPath): \OCP\Files\Node { throw new NotFoundException(); } - public function touch($mtime = null) { + #[Override] + public function touch(?int $mtime = null): void { throw new NotFoundException(); } - public function getId() { + #[Override] + public function getId(): ?int { if ($this->fileInfo) { return parent::getId(); } else { @@ -38,7 +40,8 @@ public function getId() { } } - public function getInternalPath() { + #[Override] + public function getInternalPath(): string { if ($this->fileInfo) { return parent::getInternalPath(); } else { @@ -46,11 +49,13 @@ public function getInternalPath() { } } - public function stat() { + #[Override] + public function stat(): array { throw new NotFoundException(); } - public function getMTime() { + #[Override] + public function getMTime(): int { if ($this->fileInfo) { return parent::getMTime(); } else { @@ -58,7 +63,8 @@ public function getMTime() { } } - public function getSize($includeMounts = true): int|float { + #[Override] + public function getSize(bool $includeMounts = true): int|float { if ($this->fileInfo) { return parent::getSize($includeMounts); } else { @@ -66,7 +72,8 @@ public function getSize($includeMounts = true): int|float { } } - public function getEtag() { + #[Override] + public function getEtag(): string { if ($this->fileInfo) { return parent::getEtag(); } else { @@ -74,7 +81,8 @@ public function getEtag() { } } - public function getPermissions() { + #[Override] + public function getPermissions(): int { if ($this->fileInfo) { return parent::getPermissions(); } else { @@ -82,7 +90,8 @@ public function getPermissions() { } } - public function isReadable() { + #[Override] + public function isReadable(): bool { if ($this->fileInfo) { return parent::isReadable(); } else { @@ -90,7 +99,8 @@ public function isReadable() { } } - public function isUpdateable() { + #[Override] + public function isUpdateable(): bool { if ($this->fileInfo) { return parent::isUpdateable(); } else { @@ -98,7 +108,8 @@ public function isUpdateable() { } } - public function isDeletable() { + #[Override] + public function isDeletable(): bool { if ($this->fileInfo) { return parent::isDeletable(); } else { @@ -106,7 +117,8 @@ public function isDeletable() { } } - public function isShareable() { + #[Override] + public function isShareable(): bool { if ($this->fileInfo) { return parent::isShareable(); } else { @@ -114,14 +126,17 @@ public function isShareable() { } } - public function getContent() { + #[Override] + public function getContent(): string { throw new NotFoundException(); } - public function putContent($data) { + #[Override] + public function putContent($data): void { throw new NotFoundException(); } + #[Override] public function getMimeType(): string { if ($this->fileInfo) { return parent::getMimeType(); @@ -130,6 +145,7 @@ public function getMimeType(): string { } } + #[Override] public function fopen($mode) { throw new NotFoundException(); } diff --git a/lib/private/Files/Node/NonExistingFolder.php b/lib/private/Files/Node/NonExistingFolder.php index 4489fdaf01067..89ec2aa77e3a6 100644 --- a/lib/private/Files/Node/NonExistingFolder.php +++ b/lib/private/Files/Node/NonExistingFolder.php @@ -8,29 +8,32 @@ namespace OC\Files\Node; use OCP\Files\NotFoundException; +use OCP\Files\Search\ISearchQuery; +use Override; class NonExistingFolder extends Folder { - /** - * @param string $newPath - * @throws \OCP\Files\NotFoundException - */ - public function rename($newPath) { + #[Override] + public function move(string $targetPath): \OCP\Files\Node { throw new NotFoundException(); } - public function delete() { + #[Override] + public function delete(): void { throw new NotFoundException(); } - public function copy($targetPath) { + #[Override] + public function copy(string $targetPath): \OCP\Files\Node { throw new NotFoundException(); } - public function touch($mtime = null) { + #[Override] + public function touch(?int $mtime = null): void { throw new NotFoundException(); } - public function getId() { + #[Override] + public function getId(): ?int { if ($this->fileInfo) { return parent::getId(); } else { @@ -38,7 +41,8 @@ public function getId() { } } - public function getInternalPath() { + #[Override] + public function getInternalPath(): string { if ($this->fileInfo) { return parent::getInternalPath(); } else { @@ -46,11 +50,13 @@ public function getInternalPath() { } } - public function stat() { + #[Override] + public function stat(): array|false { throw new NotFoundException(); } - public function getMTime() { + #[Override] + public function getMTime(): int { if ($this->fileInfo) { return parent::getMTime(); } else { @@ -58,7 +64,8 @@ public function getMTime() { } } - public function getSize($includeMounts = true): int|float { + #[Override] + public function getSize(bool $includeMounts = true): int|float { if ($this->fileInfo) { return parent::getSize($includeMounts); } else { @@ -66,7 +73,8 @@ public function getSize($includeMounts = true): int|float { } } - public function getEtag() { + #[Override] + public function getEtag(): string { if ($this->fileInfo) { return parent::getEtag(); } else { @@ -74,7 +82,8 @@ public function getEtag() { } } - public function getPermissions() { + #[Override] + public function getPermissions(): int { if ($this->fileInfo) { return parent::getPermissions(); } else { @@ -82,7 +91,8 @@ public function getPermissions() { } } - public function isReadable() { + #[Override] + public function isReadable(): bool { if ($this->fileInfo) { return parent::isReadable(); } else { @@ -90,7 +100,7 @@ public function isReadable() { } } - public function isUpdateable() { + public function isUpdateable(): bool { if ($this->fileInfo) { return parent::isUpdateable(); } else { @@ -98,7 +108,8 @@ public function isUpdateable() { } } - public function isDeletable() { + #[Override] + public function isDeletable(): bool { if ($this->fileInfo) { return parent::isDeletable(); } else { @@ -106,7 +117,8 @@ public function isDeletable() { } } - public function isShareable() { + #[Override] + public function isShareable(): bool { if ($this->fileInfo) { return parent::isShareable(); } else { @@ -114,55 +126,68 @@ public function isShareable() { } } - public function get($path) { + #[Override] + public function get(string $path): \OCP\Files\Node { throw new NotFoundException(); } - public function getDirectoryListing() { + #[Override] + public function getDirectoryListing(): array { throw new NotFoundException(); } - public function nodeExists($path) { + #[Override] + public function nodeExists(string $path): bool { return false; } - public function newFolder($path) { + #[Override] + public function newFolder(string $path): Folder { throw new NotFoundException(); } - public function newFile($path, $content = null) { + #[Override] + public function newFile(string $path, $content = null): File { throw new NotFoundException(); } - public function search($query) { + #[Override] + public function search(string|ISearchQuery $query): array { throw new NotFoundException(); } - public function searchByMime($mimetype) { + #[Override] + public function searchByMime(string $mimetype): array { throw new NotFoundException(); } - public function searchByTag($tag, $userId) { + #[Override] + public function searchByTag(int|string $tag, string $userId): array { throw new NotFoundException(); } + #[Override] public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0): array { throw new NotFoundException(); } - public function getById($id) { + #[Override] + public function getById(int $id): array { throw new NotFoundException(); } + #[Override] public function getFirstNodeById(int $id): ?\OCP\Files\Node { throw new NotFoundException(); } - public function getFreeSpace() { + #[Override] + public function getFreeSpace(): float|int|false { throw new NotFoundException(); } - public function isCreatable() { + #[Override] + public function isCreatable(): bool { if ($this->fileInfo) { return parent::isCreatable(); } else { diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index 9b0a48fa296fd..3c496d9e016c4 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -26,12 +26,14 @@ use OCP\Files\Node as INode; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; +use OCP\Files\Storage\IStorage; use OCP\IAppConfig; use OCP\ICache; use OCP\ICacheFactory; use OCP\IUser; use OCP\IUserManager; use OCP\Server; +use Override; use Psr\Log\LoggerInterface; /** @@ -81,87 +83,42 @@ public function __construct( } /** - * Get the user for which the filesystem is setup - * - * @return \OC\User\User + * @internal Only used in unit tests */ - public function getUser() { + public function getUser(): ?IUser { return $this->user; } - /** - * @param string $scope - * @param string $method - * @param callable $callback - */ + #[Override] public function listen($scope, $method, callable $callback) { $this->emitter->listen($scope, $method, $callback); } - /** - * @param string $scope optional - * @param string $method optional - * @param callable $callback optional - */ + #[Override] public function removeListener($scope = null, $method = null, ?callable $callback = null) { $this->emitter->removeListener($scope, $method, $callback); } - /** - * @param string $scope - * @param string $method - * @param Node[] $arguments - */ - public function emit($scope, $method, $arguments = []) { + public function emit(string $scope, string $method, array $arguments = []) { $this->emitter->emit($scope, $method, $arguments); } - /** - * @param \OC\Files\Storage\Storage $storage - * @param string $mountPoint - * @param array $arguments - */ - public function mount($storage, $mountPoint, $arguments = []) { + public function mount(IStorage $storage, string $mountPoint, array $arguments = []) { $mount = new MountPoint($storage, $mountPoint, $arguments); $this->mountManager->addMount($mount); } + #[Override] public function getMount(string $mountPoint): IMountPoint { return $this->mountManager->find($mountPoint); } - /** - * @param string $mountPoint - * @return \OC\Files\Mount\MountPoint[] - */ + #[Override] public function getMountsIn(string $mountPoint): array { return $this->mountManager->findIn($mountPoint); } - /** - * @param string $storageId - * @return \OC\Files\Mount\MountPoint[] - */ - public function getMountByStorageId($storageId) { - return $this->mountManager->findByStorageId($storageId); - } - - /** - * @param int $numericId - * @return MountPoint[] - */ - public function getMountByNumericStorageId($numericId) { - return $this->mountManager->findByNumericId($numericId); - } - - /** - * @param \OC\Files\Mount\MountPoint $mount - */ - public function unMount($mount) { - $this->mountManager->remove($mount); - } - - public function get($path) { + public function get(string $path): \OCP\Files\Node { $path = $this->normalizePath($path); if ($this->isValidPath($path)) { $fullPath = $this->getFullPath($path); @@ -176,154 +133,105 @@ public function get($path) { } } - //most operations can't be done on the root + // most operations can't be done on the root - /** - * @param string $targetPath - * @return Node - * @throws \OCP\Files\NotPermittedException - */ - public function rename($targetPath) { + #[Override] + public function move(string $targetPath): \OCP\Files\Node { throw new NotPermittedException(); } - public function delete() { + #[Override] + public function delete(): void { throw new NotPermittedException(); } - /** - * @param string $targetPath - * @return Node - * @throws \OCP\Files\NotPermittedException - */ - public function copy($targetPath) { + #[Override] + public function copy(string $targetPath): \OCP\Files\Node { throw new NotPermittedException(); } - /** - * @param int $mtime - * @throws \OCP\Files\NotPermittedException - */ - public function touch($mtime = null) { + #[Override] + public function touch(?int $mtime = null): void { throw new NotPermittedException(); } - /** - * @return \OC\Files\Storage\Storage - * @throws \OCP\Files\NotFoundException - */ - public function getStorage() { + #[Override] + public function getStorage(): IStorage { throw new NotFoundException(); } - /** - * @return string - */ - public function getPath() { + #[Override] + public function getPath(): string { return '/'; } - /** - * @return string - */ - public function getInternalPath() { + #[Override] + public function getInternalPath(): string { return ''; } - /** - * @return int - */ - public function getId() { + #[Override] + public function getId(): ?int { return 0; } - /** - * @return array - */ - public function stat() { + #[Override] + public function stat(): array { return []; } - /** - * @return int - */ - public function getMTime() { + #[Override] + public function getMTime(): int { return 0; } - /** - * @param bool $includeMounts - * @return int|float - */ - public function getSize($includeMounts = true): int|float { + #[Override] + public function getSize(bool $includeMounts = true): int|float { return 0; } - /** - * @return string - */ - public function getEtag() { + #[Override] + public function getEtag(): string { return ''; } - /** - * @return int - */ - public function getPermissions() { + #[Override] + public function getPermissions(): int { return \OCP\Constants::PERMISSION_CREATE; } - /** - * @return bool - */ - public function isReadable() { + #[Override] + public function isReadable(): bool { return false; } - /** - * @return bool - */ - public function isUpdateable() { + #[Override] + public function isUpdateable(): bool { return false; } - /** - * @return bool - */ - public function isDeletable() { + #[Override] + public function isDeletable(): bool { return false; } - /** - * @return bool - */ - public function isShareable() { + #[Override] + public function isShareable(): bool { return false; } - /** - * @throws \OCP\Files\NotFoundException - */ - public function getParent(): INode|IRootFolder { + #[Override] + public function getParent(): \OCP\Files\Folder { throw new NotFoundException(); } - /** - * @return string - */ - public function getName() { + #[Override] + public function getName(): string { return ''; } - /** - * Returns a view to user's files folder - * - * @param string $userId user ID - * @return \OCP\Files\Folder - * @throws NoUserException - * @throws NotPermittedException - */ - public function getUserFolder($userId) { + #[Override] + public function getUserFolder(string $userId): \OCP\Files\Folder { $userObject = $this->userManager->get($userId); if (is_null($userObject)) { @@ -366,10 +274,11 @@ public function getUserFolder($userId) { return $this->userFolderCache->get($userId); } - public function getUserMountCache() { + public function getUserMountCache(): IUserMountCache { return $this->userMountCache; } + #[Override] public function getFirstNodeByIdInPath(int $id, string $path): ?INode { // scope the cache by user, so we don't return nodes for different users if ($this->user) { @@ -380,7 +289,7 @@ public function getFirstNodeByIdInPath(int $id, string $path): ?INode { $node = $this->get($cachedPath); // by validating that the cached path still has the requested fileid we can work around the need to invalidate the cached path // if the cached path is invalid or a different file now we fall back to the uncached logic - if ($node && $node->getId() === $id) { + if ($node->getId() === $id) { return $node; } } catch (NotFoundException|NotPermittedException) { @@ -399,10 +308,7 @@ public function getFirstNodeByIdInPath(int $id, string $path): ?INode { return $node; } - /** - * @param int $id - * @return Node[] - */ + #[Override] public function getByIdInPath(int $id, string $path): array { $mountCache = $this->getUserMountCache(); if ($path !== '' && strpos($path, '/', 1) > 0) { @@ -497,6 +403,7 @@ public function getByIdInPath(int $id, string $path): array { return $folders; } + #[Override] public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode { $path = $cacheEntry->getPath(); $fullPath = $mountPoint->getMountPoint() . $path; diff --git a/lib/private/Files/SimpleFS/NewSimpleFile.php b/lib/private/Files/SimpleFS/NewSimpleFile.php index d0986592c0349..b9f13155b2985 100644 --- a/lib/private/Files/SimpleFS/NewSimpleFile.php +++ b/lib/private/Files/SimpleFS/NewSimpleFile.php @@ -14,6 +14,7 @@ use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; +use Override; class NewSimpleFile implements ISimpleFile { private Folder $parentFolder; @@ -68,21 +69,10 @@ public function getMTime(): int { } } - /** - * Get the content - * - * @throws NotFoundException - * @throws NotPermittedException - */ + #[Override] public function getContent(): string { if ($this->file) { - $result = $this->file->getContent(); - - if ($result === false) { - $this->checkFile(); - } - - return $result; + return $this->file->getContent(); } else { return ''; } diff --git a/lib/private/Files/SimpleFS/SimpleFile.php b/lib/private/Files/SimpleFS/SimpleFile.php index d9c1b47d2f19f..b0aadd9e5ee79 100644 --- a/lib/private/Files/SimpleFS/SimpleFile.php +++ b/lib/private/Files/SimpleFS/SimpleFile.php @@ -12,6 +12,7 @@ use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Lock\LockedException; +use Override; class SimpleFile implements ISimpleFile { private File $file; @@ -48,22 +49,9 @@ public function getMTime(): int { return $this->file->getMTime(); } - /** - * Get the content - * - * @throws GenericFileException - * @throws LockedException - * @throws NotFoundException - * @throws NotPermittedException - */ + #[Override] public function getContent(): string { - $result = $this->file->getContent(); - - if ($result === false) { - $this->checkFile(); - } - - return $result; + return $this->file->getContent(); } /** @@ -158,6 +146,6 @@ public function write() { } public function getId(): int { - return $this->file->getId(); + return $this->file->getId() ?? -1; } } diff --git a/lib/private/Files/Utils/PathHelper.php b/lib/private/Files/Utils/PathHelper.php index c1a8d0d1a6653..724ce425a0ec1 100644 --- a/lib/private/Files/Utils/PathHelper.php +++ b/lib/private/Files/Utils/PathHelper.php @@ -29,10 +29,6 @@ public static function getRelativePath(string $root, string $path) { } } - /** - * @param string $path - * @return string - */ public static function normalizePath(string $path): string { if ($path === '' || $path === '/') { return '/'; diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index f731da58b677d..8f7b0516aca3d 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -338,7 +338,7 @@ public function is_file($path) { * @param string $path * @return mixed */ - public function stat($path) { + public function stat($path): array|false { return $this->basicOperation('stat', $path); } @@ -1119,12 +1119,7 @@ public function getMimeType($path) { return $this->basicOperation('getMimeType', $path); } - /** - * @param string $type - * @param string $path - * @param bool $raw - */ - public function hash($type, $path, $raw = false): string|bool { + public function hash(string $type, string $path, bool $raw = false): string|false { $postFix = (substr($path, -1) === '/') ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path)) { @@ -1149,11 +1144,9 @@ public function hash($type, $path, $raw = false): string|bool { } /** - * @param string $path - * @return mixed * @throws InvalidPathException */ - public function free_space($path = '/') { + public function free_space(string $path = '/'): float|int|false { $this->assertPathLength($path); $result = $this->basicOperation('free_space', $path); if ($result === null) { diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php index a64bd11518110..f5d3dec5c8f9b 100644 --- a/lib/private/Preview/Generator.php +++ b/lib/private/Preview/Generator.php @@ -97,7 +97,8 @@ public function getPreview( */ public function generatePreviews(File $file, array $specifications, ?string $mimeType = null, bool $cacheResult = true): ISimpleFile { //Make sure that we can read the file - if (!$file->isReadable()) { + $id = $file->getId(); + if ($id === null || !$file->isReadable()) { $this->logger->warning('Cannot read file: {path}, skipping preview generation.', ['path' => $file->getPath()]); throw new NotFoundException('Cannot read file'); } @@ -106,7 +107,7 @@ public function generatePreviews(File $file, array $specifications, ?string $mim $mimeType = $file->getMimeType(); } - [$file->getId() => $previews] = $this->previewMapper->getAvailablePreviews([$file->getId()]); + [$id => $previews] = $this->previewMapper->getAvailablePreviews([$id]); $previewVersion = null; if ($file instanceof IVersionedPreviewFile) { diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php index 9684ad240e174..3cc50b7f1dfa8 100644 --- a/lib/private/PreviewManager.php +++ b/lib/private/PreviewManager.php @@ -216,7 +216,7 @@ public function isAvailable(\OCP\Files\FileInfo $file, ?string $mimeType = null) } $mount = $file->getMountPoint(); - if ($mount && !$mount->getOption('previews', true)) { + if (!$mount->getOption('previews', true)) { return false; } diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index d086c2739724b..f36e1aceb499d 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -653,9 +653,7 @@ private function getSharesInFolderInternal(?string $userId, Folder $node, ?bool $childMountNodes = array_filter($node->getDirectoryListing(), function (Node $node): bool { return $node->getInternalPath() === ''; }); - $childMountRootIds = array_map(function (Node $node): int { - return $node->getId(); - }, $childMountNodes); + $childMountRootIds = array_map(fn (Node $node): int => $node->getId() ?? -1, $childMountNodes); $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')); $qb->andWhere( diff --git a/lib/private/SpeechToText/SpeechToTextManager.php b/lib/private/SpeechToText/SpeechToTextManager.php index d69add2d80bea..c831e4b456502 100644 --- a/lib/private/SpeechToText/SpeechToTextManager.php +++ b/lib/private/SpeechToText/SpeechToTextManager.php @@ -123,7 +123,7 @@ public function transcribeFile(File $file, ?string $userId = null, string $appId if (isset($this->taskProcessingManager->getAvailableTaskTypes()['core:audio2text'])) { $taskProcessingTask = new Task( AudioToText::ID, - ['input' => $file->getId()], + ['input' => $file->getId() ?? -1], $appId, $userId, 'from-SpeechToTextManager||' . $file->getId() . '||' . ($userId ?? '') . '||' . $appId, diff --git a/lib/public/Files/File.php b/lib/public/Files/File.php index 67b7a7d0d2ad7..be98e76d88110 100644 --- a/lib/public/Files/File.php +++ b/lib/public/Files/File.php @@ -1,12 +1,12 @@ + * @throws InvalidPathException + * @throws NotFoundException * @since 7.0.0 - namespace of constants has changed in 8.0.0 */ - public function getPermissions(); + public function getPermissions(): int; /** * Check whether this is a file or a folder * - * @return string \OCP\Files\FileInfo::TYPE_FILE|\OCP\Files\FileInfo::TYPE_FOLDER + * @return FileInfo::TYPE_FILE|FileInfo::TYPE_FOLDER * @since 7.0.0 */ - public function getType(); + public function getType(): string; /** - * Check if the file or folder is readable + * Check if the file or folder is readable. * - * @return bool + * @throws NotFoundException + * @throws InvalidPathException * @since 7.0.0 */ - public function isReadable(); + public function isReadable(): bool; /** - * Check if a file is writable + * Check if the file or folder is writable. * - * @return bool + * @throws InvalidPathException + * @throws NotFoundException * @since 7.0.0 */ - public function isUpdateable(); + public function isUpdateable(): bool; /** * Check whether new files or folders can be created inside this folder * - * @return bool + * @throws InvalidPathException + * @throws NotFoundException * @since 8.0.0 */ - public function isCreatable(); + public function isCreatable(): bool; /** * Check if a file or folder can be deleted * - * @return bool + * @throws InvalidPathException + * @throws NotFoundException * @since 7.0.0 */ - public function isDeletable(); + public function isDeletable(): bool; /** - * Check if a file or folder can be shared + * Check if the file or folder is shareable. * - * @return bool + * @throws InvalidPathException + * @throws NotFoundException * @since 7.0.0 */ - public function isShareable(); + public function isShareable(): bool; /** - * Check if a file or folder is shared + * Check if a file or folder is shared. * - * @return bool * @since 7.0.0 */ - public function isShared(); + public function isShared(): bool; /** * Check if a file or folder is mounted * - * @return bool * @since 7.0.0 */ - public function isMounted(); + public function isMounted(): bool; /** - * Get the mountpoint the file belongs to + * Get the mountpoint the file belongs to. * - * @return \OCP\Files\Mount\IMountPoint * @since 8.0.0 */ - public function getMountPoint(); + public function getMountPoint(): IMountPoint; /** - * Get the owner of the file + * Get the owner of the file. * - * @return ?\OCP\IUser * @since 9.0.0 */ - public function getOwner(); + public function getOwner(): ?IUser; /** - * Get the stored checksum(s) for this file + * Get the stored checksum(s) for this file. * * Checksums are stored in the format TYPE:CHECKSUM, here may be multiple checksums separated by a single space * e.g. MD5:d3b07384d113edec49eaa6238ad5ff00 SHA1:f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 * - * @return string + * @note This will return an empty string if no checksum is currently stored. + * * @since 9.0.0 */ - public function getChecksum(); + public function getChecksum(): string; /** * Get the extension of the file @@ -275,7 +288,7 @@ public function getCreationTime(): int; * If the upload time is not known, 0 will be returned * * Upload time will be set automatically by the server for files uploaded over DAV - * files created by Nextcloud apps generally do not have an the upload time set + * files created by Nextcloud apps generally do not have the upload time set. * * @return int * @since 18.0.0 @@ -283,16 +296,15 @@ public function getCreationTime(): int; public function getUploadTime(): int; /** - * Get the fileid or the parent folder - * or -1 if this item has no parent folder (because it is the root) + * Get the fileId or the parent folder or -1 if this item has no parent folder + * (because it is the root). * - * @return int * @since 28.0.0 */ public function getParentId(): int; /** - * Get the metadata, if available + * Get the metadata, if available. * * @return array * @since 28.0.0 diff --git a/lib/public/Files/Folder.php b/lib/public/Files/Folder.php index ba23b872abd40..1d70eba9f2a44 100644 --- a/lib/public/Files/Folder.php +++ b/lib/public/Files/Folder.php @@ -10,60 +10,63 @@ namespace OCP\Files; +use OCP\AppFramework\Attribute\Consumable; use OCP\Files\Search\ISearchQuery; /** + * Folder interface. + * + * Represents a container node that can hold files, subfolders, + * or other nodes in a hierarchical structure. + * * @since 6.0.0 */ +#[Consumable(since: '6.0.0')] interface Folder extends Node { /** * Get the full path of an item in the folder within owncloud's filesystem * * @param string $path relative path of an item in the folder * @return string - * @throws \OCP\Files\NotPermittedException + * @throws NotPermittedException * @since 6.0.0 */ - public function getFullPath($path); + public function getFullPath(string $path): string; /** * Get the path of an item in the folder relative to the folder * * @param string $path absolute path of an item in the folder - * @throws \OCP\Files\NotFoundException - * @return string|null + * @throws NotFoundException * @since 6.0.0 */ - public function getRelativePath($path); + public function getRelativePath(string $path): ?string; /** - * check if a node is a (grand-)child of the folder + * Check if a node is a (grand-)child of the folder. * - * @param \OCP\Files\Node $node - * @return bool * @since 6.0.0 */ - public function isSubNode($node); + public function isSubNode(Node $node): bool; /** - * get the content of this directory + * Get the content of this directory. * - * @throws \OCP\Files\NotFoundException - * @return \OCP\Files\Node[] + * @return Node[] + * @throws NotFoundException * @since 6.0.0 */ - public function getDirectoryListing(); + public function getDirectoryListing(): array; /** - * Get the node at $path + * Get the node at $path. * * @param string $path relative path of the file or folder - * @return \OCP\Files\Node - * @throws \OCP\Files\NotFoundException - * @throws \OCP\Files\NotPermittedException + * @throws NotFoundException + * @throws NotPermittedException * @since 6.0.0 */ - public function get($path); + public function get(string $path): Node; /** * Get or create new folder if the folder does not already exist. @@ -78,87 +81,81 @@ public function getOrCreateFolder(string $path, int $maxRetries = 5): Folder; * Check if a file or folder exists in the folder * * @param string $path relative path of the file or folder - * @return bool * @since 6.0.0 */ - public function nodeExists($path); + public function nodeExists(string $path): bool; /** * Create a new folder * * @param string $path relative path of the new folder - * @return \OCP\Files\Folder - * @throws \OCP\Files\NotPermittedException + * @throws NotPermittedException * @since 6.0.0 */ - public function newFolder($path); + public function newFolder(string $path): Folder; /** * Create a new file * * @param string $path relative path of the new file * @param string|resource|null $content content for the new file, since 19.0.0 - * @return \OCP\Files\File - * @throws \OCP\Files\NotPermittedException + * @throws NotPermittedException * @since 6.0.0 */ - public function newFile($path, $content = null); + public function newFile(string $path, $content = null): File; /** - * search for files with the name matching $query + * Search for files with the name matching $query. * - * @param string|ISearchQuery $query - * @return \OCP\Files\Node[] + * @return Node[] * @since 6.0.0 */ - public function search($query); + public function search(string|ISearchQuery $query): array; /** - * search for files by mimetype - * $mimetype can either be a full mimetype (image/png) or a wildcard mimetype (image) + * Search for files by mimetype. * - * @param string $mimetype - * @return \OCP\Files\Node[] + * @param string $mimetype can either be a full mimetype (image/png) or a wildcard mimetype (image) + * @return Node[] * @since 6.0.0 */ - public function searchByMime($mimetype); + public function searchByMime(string $mimetype): array; /** - * search for files by tag + * Search for files by tag. * * @param string|int $tag tag name or tag id * @param string $userId owner of the tags - * @return \OCP\Files\Node[] + * @return Node[] * @since 8.0.0 */ - public function searchByTag($tag, $userId); + public function searchByTag(string|int $tag, string $userId): array; /** - * search for files by system tag + * Search for files by system tag. * - * @param string|int $tag tag name + * @param string $tag tag name * @param string $userId user id to ensure access on returned nodes - * @return \OCP\Files\Node[] + * @return Node[] * @since 28.0.0 */ - public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0); + public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0): array; /** - * get a file or folder inside the folder by its internal id + * Get a file or folder inside the folder by its internal id. * * This method could return multiple entries. For example once the file/folder * is shared or mounted (files_external) to the user multiple times. * * Note that the different entries can have different permissions. * - * @param int $id - * @return \OCP\Files\Node[] + * @return Node[] * @since 6.0.0 */ - public function getById($id); + public function getById(int $id): array; /** - * get a file or folder inside the folder by its internal id + * Get a file or folder inside the folder by its internal id. * * Unlike getById, this method only returns a single node even if the user has * access to the file with the requested id multiple times. @@ -169,54 +166,47 @@ public function getById($id); * Apps that require accurate information about the users access to the file should use getById * instead of pick the correct node out of the result. * - * @param int $id - * @return Node|null * @since 29.0.0 */ public function getFirstNodeById(int $id): ?Node; /** - * Get the amount of free space inside the folder + * Get the amount of free space inside the folder. * - * @return int * @since 6.0.0 */ - public function getFreeSpace(); + public function getFreeSpace(): int|float|false; /** - * Check if new files or folders can be created within the folder + * Check if new files or folders can be created within the folder. * - * @return bool * @since 6.0.0 */ - public function isCreatable(); + public function isCreatable(): bool; /** - * Add a suffix to the name in case the file exists + * Add a suffix to the name in case the file exists. * - * @param string $filename - * @return string * @throws NotPermittedException * @since 8.1.0 */ - public function getNonExistingName($filename); + public function getNonExistingName(string $name): string; /** - * @param int $limit - * @param int $offset - * @return \OCP\Files\Node[] + * Get recent files and folders. + * + * @return Node[] * @since 9.1.0 */ - public function getRecent($limit, $offset = 0); + public function getRecent(int $limit, int $offset = 0): array; /** - * Verify if the given path is valid and allowed from this folder. + * Verify if the given fileName is valid and allowed from this folder. * - * @param string $path the path from this folder * @param string $fileName * @param bool $readonly Check only if the path is allowed for read-only access * @throws InvalidPathException * @since 32.0.0 */ - public function verifyPath($fileName, $readonly = false): void; + public function verifyPath(string $fileName, bool $readonly = false): void; } diff --git a/lib/public/Files/IRootFolder.php b/lib/public/Files/IRootFolder.php index fb8532f8c8153..de5b768756999 100644 --- a/lib/public/Files/IRootFolder.php +++ b/lib/public/Files/IRootFolder.php @@ -1,5 +1,7 @@ $this->templateId, 'basename' => $this->file->getName(), 'etag' => $this->file->getEtag(), - 'fileid' => $this->file->getId(), + 'fileid' => $this->file->getId() ?? -1, 'filename' => $this->templateId, 'lastmod' => $this->file->getMTime(), 'mime' => $this->file->getMimetype(), diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index 9abaff980f27f..28d9f6f7693d7 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -257,10 +257,10 @@ public function userDeletedFromGroup(string $uid, string $gid): void; * @param bool $currentAccess Should the user have currently access to the file * @return ($currentAccess is true * ? array{ - * users?: array, - * remote?: array, + * users?: array, + * remote?: array, * public?: bool, - * mail?: array + * mail?: array * } * : array{users?: list, remote?: bool, public?: bool, mail?: list}) * @since 12.0.0 diff --git a/tests/lib/Encryption/DecryptAllTest.php b/tests/lib/Encryption/DecryptAllTest.php index f7a6497e011be..1a774bd7304fe 100644 --- a/tests/lib/Encryption/DecryptAllTest.php +++ b/tests/lib/Encryption/DecryptAllTest.php @@ -15,6 +15,7 @@ use OC\Encryption\Manager; use OC\Files\FileInfo; use OC\Files\View; +use OCP\Files\Mount\IMountPoint; use OCP\Files\Storage\IStorage; use OCP\IUserManager; use OCP\UserInterface; @@ -250,15 +251,15 @@ public function testDecryptUsersFiles(): void { [ '/user1/files', '', null, [ - new FileInfo('path', $storage, 'intPath', ['name' => 'foo', 'type' => 'dir'], null), - new FileInfo('path', $storage, 'intPath', ['name' => 'bar', 'type' => 'file', 'encrypted' => true], null), - new FileInfo('path', $sharedStorage, 'intPath', ['name' => 'shared', 'type' => 'file', 'encrypted' => true], null), + new FileInfo('path', $storage, 'intPath', ['name' => 'foo', 'type' => 'dir'], $this->createMock(IMountPoint::class)), + new FileInfo('path', $storage, 'intPath', ['name' => 'bar', 'type' => 'file', 'encrypted' => true], $this->createMock(IMountPoint::class)), + new FileInfo('path', $sharedStorage, 'intPath', ['name' => 'shared', 'type' => 'file', 'encrypted' => true], $this->createMock(IMountPoint::class)), ], ], [ '/user1/files/foo', '', null, [ - new FileInfo('path', $storage, 'intPath', ['name' => 'subfile', 'type' => 'file', 'encrypted' => true], null) + new FileInfo('path', $storage, 'intPath', ['name' => 'subfile', 'type' => 'file', 'encrypted' => true], $this->createMock(IMountPoint::class)) ], ], ]); diff --git a/tests/lib/Files/Node/FileTest.php b/tests/lib/Files/Node/FileTest.php index 36a410887cc67..450fad9eba588 100644 --- a/tests/lib/Files/Node/FileTest.php +++ b/tests/lib/Files/Node/FileTest.php @@ -15,6 +15,7 @@ use OCP\Constants; use OCP\Files\IRootFolder; use OCP\Files\NotPermittedException; +use OCP\Files\NotPermittedException; use OCP\Files\Storage\IStorage; use PHPUnit\Framework\MockObject\MockObject; diff --git a/tests/lib/Files/Node/FolderTest.php b/tests/lib/Files/Node/FolderTest.php index ad60cb535558c..29594dae9d5c9 100644 --- a/tests/lib/Files/Node/FolderTest.php +++ b/tests/lib/Files/Node/FolderTest.php @@ -85,8 +85,8 @@ public function testGetDirectoryContent(): void { ->method('getDirectoryContent') ->with('/bar/foo') ->willReturn([ - new FileInfo('/bar/foo/asd', null, 'foo/asd', ['fileid' => 2, 'path' => '/bar/foo/asd', 'name' => 'asd', 'size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'], null), - new FileInfo('/bar/foo/qwerty', null, 'foo/qwerty', ['fileid' => 3, 'path' => '/bar/foo/qwerty', 'name' => 'qwerty', 'size' => 200, 'mtime' => 55, 'mimetype' => 'httpd/unix-directory'], null), + new FileInfo('/bar/foo/asd', $this->createMock(IStorage::class), 'foo/asd', ['fileid' => 2, 'path' => '/bar/foo/asd', 'name' => 'asd', 'size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'], $this->createMock(IMountPoint::class)), + new FileInfo('/bar/foo/qwerty', $this->createMock(IStorage::class), 'foo/qwerty', ['fileid' => 3, 'path' => '/bar/foo/qwerty', 'name' => 'qwerty', 'size' => 200, 'mtime' => 55, 'mimetype' => 'httpd/unix-directory'], $this->createMock(IMountPoint::class)), ]); $this->view->method('getFileInfo') ->willReturn($this->createMock(FileInfo::class)); @@ -489,12 +489,12 @@ public function testSearchSubStorages(): void { public function testIsSubNode(): void { $rootFolderMock = $this->createMock(IRootFolder::class); - $file = new Node($rootFolderMock, $this->view, '/foo/bar'); + $file = new File($rootFolderMock, $this->view, '/foo/bar'); $folder = new Folder($rootFolderMock, $this->view, '/foo'); $this->assertTrue($folder->isSubNode($file)); $this->assertFalse($folder->isSubNode($folder)); - $file = new Node($rootFolderMock, $this->view, '/foobar'); + $file = new File($rootFolderMock, $this->view, '/foobar'); $this->assertFalse($folder->isSubNode($file)); } diff --git a/tests/lib/Files/Node/NodeTestCase.php b/tests/lib/Files/Node/NodeTestCase.php index 56a20c3d7148b..8e3fa0908995b 100644 --- a/tests/lib/Files/Node/NodeTestCase.php +++ b/tests/lib/Files/Node/NodeTestCase.php @@ -119,10 +119,12 @@ protected function getMockStorage(): IStorage&MockObject { return $storage; } - protected function getFileInfo($data, $internalPath = '', ?IStorage $storage = null) { + protected function getFileInfo(array $data, string $internalPath = '', ?IStorage $storage = null): FileInfo { $mount = $this->createMock(IMountPoint::class); $mount->method('getStorage') ->willReturn($storage); + $mount->method('getInternalPath') + ->willReturnArgument(0); return new FileInfo('', $this->getMockStorage(), $internalPath, $data, $mount); } diff --git a/tests/lib/Files/Node/RootTest.php b/tests/lib/Files/Node/RootTest.php index 3821b6943b15b..1b9e0bfe2a161 100644 --- a/tests/lib/Files/Node/RootTest.php +++ b/tests/lib/Files/Node/RootTest.php @@ -19,8 +19,10 @@ use OCP\Cache\CappedMemoryCache; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\IUserMountCache; +use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; +use OCP\Files\Storage\IStorage; use OCP\IAppConfig; use OCP\ICacheFactory; use OCP\IUser; @@ -75,8 +77,8 @@ protected function getRootViewMock() { return $view; } - protected function getFileInfo($data) { - return new FileInfo('', null, '', $data, null); + protected function getFileInfo($data): FileInfo { + return new FileInfo('', $this->createMock(IStorage::class), '', $data, $this->createMock(IMountPoint::class)); } public function testGet(): void { diff --git a/tests/lib/Files/SimpleFS/SimpleFileTest.php b/tests/lib/Files/SimpleFS/SimpleFileTest.php index 6ce5ddad351a0..9b064a5a3142d 100644 --- a/tests/lib/Files/SimpleFS/SimpleFileTest.php +++ b/tests/lib/Files/SimpleFS/SimpleFileTest.php @@ -91,7 +91,7 @@ public function testGetMimeType(): void { public function testGetContentInvalidAppData(): void { $this->file->method('getContent') - ->willReturn(false); + ->willThrowException($this->createMock(NotFoundException::class)); $this->file->method('stat')->willReturn(false); $parent = $this->createMock(Folder::class); diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php index 16562cee7d2af..25732dc6a5504 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -905,7 +905,7 @@ public function testPartFileInfo(): void { $info = $view->getFileInfo('test.part'); $this->assertInstanceOf('\OCP\Files\FileInfo', $info); - $this->assertNull($info->getId()); + $this->assertEquals(null, $info->getId()); $this->assertEquals(6, $sizeWritten); $this->assertEquals(6, $info->getSize()); $this->assertEquals('foobar', $view->file_get_contents('test.part')); diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php index df950b699cd0b..55d76d0e5cb93 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -779,7 +779,7 @@ public function testVerifyPasswordHookFails(): void { } public function createShare($id, $type, $node, $sharedWith, $sharedBy, $shareOwner, - $permissions, $expireDate = null, $password = null, $attributes = null) { + $permissions, $expireDate = null, $password = null, $attributes = null): IShare&MockObject { $share = $this->createMock(IShare::class); $share->method('getShareType')->willReturn($type); @@ -807,6 +807,7 @@ public static function dataGeneralChecks(): array { File::class, [ 'getId' => 108, + 'getPath' => 'path', ], 'default', ]; @@ -815,6 +816,7 @@ public static function dataGeneralChecks(): array { Node::class, [ 'getId' => 108, + 'getPath' => 'path', ], 'default', ]; @@ -854,6 +856,7 @@ public static function dataGeneralChecks(): array { 'getPath' => 'path', 'getName' => 'name', 'getOwner' => $user0, + 'getInternalPath' => 'not-null', ], 'default', ]; @@ -871,6 +874,7 @@ public static function dataGeneralChecks(): array { 'getPath' => 'path', 'getName' => 'name', 'getOwner' => $user0, + 'getInternalPath' => 'not-null', ], 'default', ]; @@ -908,6 +912,8 @@ public static function dataGeneralChecks(): array { 'isShareable' => true, 'getPermissions' => Constants::PERMISSION_ALL, 'getId' => 42, + 'getPath' => 'path', + 'getInternalPath' => 'not-null', ], 'none', ]; @@ -923,6 +929,8 @@ public static function dataGeneralChecks(): array { 'getPermissions' => Constants::PERMISSION_ALL, 'getId' => 187, 'getOwner' => $user0, + 'getPath' => 'path', + 'getInternalPath' => 'not-null', ], 'default', ]; @@ -939,6 +947,8 @@ public static function dataGeneralChecks(): array { 'getPermissions' => Constants::PERMISSION_ALL, 'getId' => 108, 'getOwner' => $user0, + 'getPath' => 'path', + 'getInternalPath' => 'not-null', ], 'default', ]; @@ -963,13 +973,15 @@ public static function dataGeneralChecks(): array { 'getPermissions' => Constants::PERMISSION_READ ^ Constants::PERMISSION_UPDATE, 'getId' => 108, 'getOwner' => $user0, + 'getPath' => 'path', + 'getInternalPath' => 'not-null', ], 'remote', ]; $data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 1, null, null], null, false]; $data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 3, null, null], null, false]; - $data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 31, null, null], 'Cannot increase permissions of ', true]; + $data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 31, null, null], 'Cannot increase permissions of path', true]; return $data; } @@ -983,7 +995,7 @@ private function createNodeMock(string $class, array $methods, string $storageTy $return->method('getUID') ->willReturn($uid); } elseif ($methodName === 'getMountPoint') { - $return = $this->createMock($return); + $return = $this->createMockForIntersectionOfInterfaces([IMountPoint::class, $return]); } $mock->method($methodName)->willReturn($return); } @@ -2945,7 +2957,7 @@ public function testGetSharesBy(): void { } public function testGetSharesByOwnerless(): void { - $mount = $this->createMock(IShareOwnerlessMount::class); + $mount = $this->createMockForIntersectionOfInterfaces([IMountPoint::class, IShareOwnerlessMount::class]); $node = $this->createMock(Folder::class); $node @@ -4668,7 +4680,7 @@ public function testGetSharesInFolderOwnerless(): void { $share1 = $this->createMock(IShare::class); $share2 = $this->createMock(IShare::class); - $mount = $this->createMock(IShareOwnerlessMount::class); + $mount = $this->createMockForIntersectionOfInterfaces([IMountPoint::class, IShareOwnerlessMount::class]); $folder = $this->createMock(Folder::class); $folder diff --git a/tests/lib/Share20/ShareHelperTest.php b/tests/lib/Share20/ShareHelperTest.php index 3928843cf7def..465ab1b6bcd0f 100644 --- a/tests/lib/Share20/ShareHelperTest.php +++ b/tests/lib/Share20/ShareHelperTest.php @@ -8,6 +8,7 @@ namespace Test\Share20; use OC\Share20\ShareHelper; +use OCP\Files\Folder; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Share\IManager; @@ -109,8 +110,7 @@ public static function dataGetPathsForUsers(): array { public function testGetPathsForUsers(array $users, array $nodes, array $expected): void { $lastNode = null; foreach ($nodes as $nodeId => $nodeName) { - /** @var Node|\PHPUnit\Framework\MockObject\MockObject $node */ - $node = $this->createMock(Node::class); + $node = $this->createMock(Folder::class); $node->expects($this->any()) ->method('getId') ->willReturn($nodeId); @@ -166,8 +166,7 @@ public static function dataGetPathsForRemotes(): array { public function testGetPathsForRemotes(array $remotes, array $nodes, array $expected): void { $lastNode = null; foreach ($nodes as $nodeId => $nodePath) { - /** @var Node|\PHPUnit\Framework\MockObject\MockObject $node */ - $node = $this->createMock(Node::class); + $node = $this->createMock(Folder::class); $node->expects($this->any()) ->method('getId') ->willReturn($nodeId);