From 5cf486150ab297c166ccd4cdead2294890be713a Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Tue, 23 Sep 2025 16:59:09 +0200 Subject: [PATCH] refactor: Fix psalm issues - Add typing for most of the services, controllers and mappers - Add api doc for mappers - Use vendor-bin for psalm - Use attributes for controllers - Fix upload of attachments Signed-off-by: Carl Schwan --- composer.json | 18 +- composer.lock | 94 +- lib/Activity/ActivityManager.php | 2 +- lib/Activity/SettingComment.php | 3 + lib/Controller/AttachmentApiController.php | 74 +- lib/Controller/AttachmentController.php | 83 +- lib/Controller/BoardApiController.php | 89 +- lib/Controller/BoardController.php | 69 +- lib/Controller/BoardImportApiController.php | 27 +- lib/Controller/CardApiController.php | 89 +- lib/Controller/CardController.php | 137 +- lib/Controller/CommentsApiController.php | 14 +- lib/Controller/ConfigController.php | 16 +- lib/Controller/LabelApiController.php | 44 +- lib/Controller/LabelController.php | 30 +- lib/Controller/OverviewApiController.php | 5 +- lib/Controller/SearchController.php | 5 +- lib/Controller/StackApiController.php | 77 +- lib/Controller/StackController.php | 64 +- lib/Db/Acl.php | 34 +- lib/Db/AclMapper.php | 18 +- lib/Db/AssignmentMapper.php | 9 +- lib/Db/AttachmentMapper.php | 28 +- lib/Db/Board.php | 10 + lib/Db/BoardMapper.php | 6 +- lib/Db/Card.php | 9 +- lib/Db/CardMapper.php | 81 +- lib/Db/DeckMapper.php | 5 +- lib/Db/IPermissionMapper.php | 13 +- lib/Db/Label.php | 13 +- lib/Db/LabelMapper.php | 48 +- lib/Db/Stack.php | 8 +- lib/Db/StackMapper.php | 30 +- .../BeforeTemplateRenderedListener.php | 4 + lib/Listeners/ResourceListener.php | 16 +- lib/Model/BoardSummary.php | 5 +- lib/Model/CardDetails.php | 1 + lib/Notification/NotificationHelper.php | 2 +- lib/Service/AssignmentService.php | 11 +- lib/Service/AttachmentService.php | 56 +- lib/Service/BoardService.php | 114 +- lib/Service/CardService.php | 83 +- lib/Service/CirclesService.php | 2 +- lib/Service/CommentService.php | 43 +- lib/Service/ConfigService.php | 39 +- lib/Service/FullTextSearchService.php | 19 +- lib/Service/Importer/ABoardImportService.php | 37 +- lib/Service/Importer/BoardImportService.php | 36 +- .../Importer/Systems/DeckJsonService.php | 2 + .../Importer/Systems/TrelloApiService.php | 4 +- .../Importer/Systems/TrelloJsonService.php | 1 + lib/Service/LabelService.php | 23 +- lib/Service/OverviewService.php | 35 +- lib/Service/PermissionService.php | 6 +- lib/Service/StackService.php | 77 +- lib/Sharing/DeckShareProvider.php | 52 +- psalm.xml | 10 +- tests/data/deck.json | 52 +- tests/integration/base-query-count.txt | 2 +- tests/psalm-baseline.xml | 142 +- tests/unit/Activity/ActivityManagerTest.php | 4 + tests/unit/Db/AttachmentMapperTest.php | 4 +- tests/unit/Db/CardTest.php | 12 +- tests/unit/Service/AttachmentServiceTest.php | 8 +- tests/unit/Service/BoardServiceTest.php | 15 +- tests/unit/Service/CardServiceTest.php | 9 +- .../unit/Service/DefaultBoardServiceTest.php | 3 +- .../Importer/BoardImportServiceTest.php | 7 +- tests/unit/Service/LabelServiceTest.php | 13 +- tests/unit/Service/StackServiceTest.php | 10 +- tests/unit/controller/BoardControllerTest.php | 93 +- .../unit/controller/CardApiControllerTest.php | 23 +- tests/unit/controller/CardControllerTest.php | 47 +- tests/unit/controller/LabelControllerTest.php | 22 +- tests/unit/controller/StackControllerTest.php | 27 +- vendor-bin/psalm/composer.json | 10 + vendor-bin/psalm/composer.lock | 3217 +++++++++++++++++ 77 files changed, 4257 insertions(+), 1393 deletions(-) create mode 100644 vendor-bin/psalm/composer.json create mode 100644 vendor-bin/psalm/composer.lock diff --git a/composer.json b/composer.json index f359d91b5..ea8dff878 100644 --- a/composer.json +++ b/composer.json @@ -9,31 +9,35 @@ } ], "require": { - "justinrainbow/json-schema": "^6.0" + "justinrainbow/json-schema": "^6.0", + "bamarni/composer-bin-plugin": "^1.8" }, "require-dev": { "roave/security-advisories": "dev-master", "phpunit/phpunit": "^9", "nextcloud/coding-standard": "^1.1", - "nextcloud/ocp": "dev-master", - "psalm/phar": "^5.13" + "nextcloud/ocp": "dev-master" }, "config": { "optimize-autoloader": true, "allow-plugins": { - "composer/package-versions-deprecated": true + "composer/package-versions-deprecated": true, + "bamarni/composer-bin-plugin": true }, "platform": { "php": "8.1" } }, "scripts": { + "post-install-cmd": [ + "@composer bin all install --ansi" + ], "lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l", "cs:check": "php-cs-fixer fix --dry-run --diff", "cs:fix": "php-cs-fixer fix", - "psalm": "psalm.phar", - "psalm:update-baseline": "psalm.phar --update-baseline", - "psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MismatchingDocblockParamType,MismatchingDocblockReturnType,MissingParamType,InvalidFalsableReturnType", + "psalm": "psalm", + "psalm:update-baseline": "psalm --threads=$(nproc) --no-cache --update-baseline", + "psalm:fix": "psalm --alter --issues=InvalidReturnType,InvalidNullableReturnType,MismatchingDocblockParamType,MismatchingDocblockReturnType,MissingParamType,InvalidFalsableReturnType", "test": [ "@test:unit", "@test:integration" diff --git a/composer.lock b/composer.lock index d99198565..31d8d0be3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,65 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6950663d9d213151028e780637480220", + "content-hash": "263f9ff9e6a13d50ab09bc9f4e06b749", "packages": [ + { + "name": "bamarni/composer-bin-plugin", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/bamarni/composer-bin-plugin.git", + "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", + "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "ext-json": "*", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.5", + "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin" + }, + "autoload": { + "psr-4": { + "Bamarni\\Composer\\Bin\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "No conflicts for your bin dependencies", + "keywords": [ + "composer", + "conflict", + "dependency", + "executable", + "isolation", + "tool" + ], + "support": { + "issues": "https://github.com/bamarni/composer-bin-plugin/issues", + "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2" + }, + "time": "2022-10-31T08:38:03+00:00" + }, { "name": "justinrainbow/json-schema", "version": "6.4.2", @@ -1073,41 +1130,6 @@ ], "time": "2024-12-05T13:48:26+00:00" }, - { - "name": "psalm/phar", - "version": "5.26.1", - "source": { - "type": "git", - "url": "https://github.com/psalm/phar.git", - "reference": "8a38e7ad04499a0ccd2c506fd1da6fc01fff4547" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/psalm/phar/zipball/8a38e7ad04499a0ccd2c506fd1da6fc01fff4547", - "reference": "8a38e7ad04499a0ccd2c506fd1da6fc01fff4547", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "vimeo/psalm": "*" - }, - "bin": [ - "psalm.phar" - ], - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Composer-based Psalm Phar", - "support": { - "issues": "https://github.com/psalm/phar/issues", - "source": "https://github.com/psalm/phar/tree/5.26.1" - }, - "time": "2024-09-09T16:22:43+00:00" - }, { "name": "psr/clock", "version": "1.0.0", diff --git a/lib/Activity/ActivityManager.php b/lib/Activity/ActivityManager.php index 55e2747a2..ad180c91f 100644 --- a/lib/Activity/ActivityManager.php +++ b/lib/Activity/ActivityManager.php @@ -516,7 +516,7 @@ class ActivityManager { ]; } - private function findDetailsForCard($cardId, $subject = null) { + private function findDetailsForCard(int $cardId, ?string $subject = null): array { $card = $this->cardMapper->find($cardId); $stack = $this->stackMapper->find($card->getStackId()); $board = $this->boardMapper->find($stack->getBoardId()); diff --git a/lib/Activity/SettingComment.php b/lib/Activity/SettingComment.php index 57def3b39..0ab595906 100644 --- a/lib/Activity/SettingComment.php +++ b/lib/Activity/SettingComment.php @@ -7,6 +7,9 @@ namespace OCA\Deck\Activity; +/** + * @psalm-api SettingComment + */ class SettingComment extends SettingBase { /** diff --git a/lib/Controller/AttachmentApiController.php b/lib/Controller/AttachmentApiController.php index 300f421a1..00879fc9b 100644 --- a/lib/Controller/AttachmentApiController.php +++ b/lib/Controller/AttachmentApiController.php @@ -6,9 +6,13 @@ */ namespace OCA\Deck\Controller; +use OCA\Deck\Db\Attachment; use OCA\Deck\Service\AttachmentService; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\CORS; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; @@ -21,72 +25,52 @@ class AttachmentApiController extends ApiController { parent::__construct($appName, $request); } - /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - */ - public function getAll($apiVersion) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function getAll(string $apiVersion): DataResponse { $attachment = $this->attachmentService->findAll($this->request->getParam('cardId'), true); if ($apiVersion === '1.0') { - $attachment = array_filter($attachment, function ($attachment) { - return $attachment->getType() === 'deck_file'; - }); + $attachment = array_filter($attachment, fn (Attachment $attachment): bool => $attachment->getType() === 'deck_file'); } return new DataResponse($attachment, HTTP::STATUS_OK); } - /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - */ - public function display($cardId, $attachmentId, $type = 'deck_file') { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function display(int $cardId, int $attachmentId, string $type = 'deck_file') { return $this->attachmentService->display($cardId, $attachmentId, $type); } - /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - */ - public function create($cardId, $type, $data) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function create(int $cardId, string $type, string $data): DataResponse { $attachment = $this->attachmentService->create($cardId, $type, $data); return new DataResponse($attachment, HTTP::STATUS_OK); } - /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - */ - public function update($cardId, $attachmentId, $data, $type = 'deck_file') { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function update(int $cardId, int $attachmentId, string $data, string $type = 'deck_file'): DataResponse { $attachment = $this->attachmentService->update($cardId, $attachmentId, $data, $type); return new DataResponse($attachment, HTTP::STATUS_OK); } - /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - */ - public function delete($cardId, $attachmentId, $type = 'deck_file') { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function delete(int $cardId, int $attachmentId, string $type = 'deck_file'): DataResponse { $attachment = $this->attachmentService->delete($cardId, $attachmentId, $type); return new DataResponse($attachment, HTTP::STATUS_OK); } - /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - */ - public function restore($cardId, $attachmentId, $type = 'deck_file') { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function restore(int $cardId, int $attachmentId, string $type = 'deck_file'): DataResponse { $attachment = $this->attachmentService->restore($cardId, $attachmentId, $type); return new DataResponse($attachment, HTTP::STATUS_OK); } diff --git a/lib/Controller/AttachmentController.php b/lib/Controller/AttachmentController.php index d76ed4bdf..fa49cb3c2 100644 --- a/lib/Controller/AttachmentController.php +++ b/lib/Controller/AttachmentController.php @@ -7,8 +7,13 @@ namespace OCA\Deck\Controller; +use OCA\Deck\BadRequestException; +use OCA\Deck\Db\Attachment; use OCA\Deck\Service\AttachmentService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Response; use OCP\IRequest; class AttachmentController extends Controller { @@ -20,74 +25,66 @@ class AttachmentController extends Controller { parent::__construct($appName, $request); } - /** - * @NoAdminRequired - */ - public function getAll($cardId) { + #[NoAdminRequired] + public function getAll(int $cardId): array { return $this->attachmentService->findAll($cardId, true); } /** - * @param $cardId - * @param $attachmentId - * @NoCSRFRequired - * @NoAdminRequired - * @return \OCP\AppFramework\Http\Response * @throws \OCA\Deck\NotFoundException */ - public function display($cardId, $attachmentId) { - if (!str_contains($attachmentId, ':')) { - $type = 'deck_file'; - } else { - [$type, $attachmentId] = explode(':', $attachmentId); - } + #[NoAdminRequired] + #[NoCSRFRequired] + public function display(int $cardId, string $attachmentId): Response { + ['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId); return $this->attachmentService->display($cardId, $attachmentId, $type); } - /** - * @NoAdminRequired - */ - public function create($cardId) { + #[NoAdminRequired] + public function create(int $cardId): Attachment { return $this->attachmentService->create( $cardId, $this->request->getParam('type'), - $this->request->getParam('data') + $this->request->getParam('data') ?? '', ); } - /** - * @NoAdminRequired - */ - public function update($cardId, $attachmentId) { - if (!str_contains($attachmentId, ':')) { - $type = 'deck_file'; - } else { - [$type, $attachmentId] = explode(':', $attachmentId); - } - return $this->attachmentService->update($cardId, $attachmentId, $this->request->getParam('data'), $type); + #[NoAdminRequired] + public function update(int $cardId, string $attachmentId): Attachment { + ['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId); + return $this->attachmentService->update($cardId, $attachmentId, $this->request->getParam('data') ?? '', $type); } - /** - * @NoAdminRequired - */ - public function delete($cardId, $attachmentId) { - if (!str_contains($attachmentId, ':')) { - $type = 'deck_file'; - } else { - [$type, $attachmentId] = explode(':', $attachmentId); - } + #[NoAdminRequired] + public function delete(int $cardId, string $attachmentId): Attachment { + ['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId); return $this->attachmentService->delete($cardId, $attachmentId, $type); } + #[NoAdminRequired] + public function restore(int $cardId, string $attachmentId): Attachment { + ['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId); + return $this->attachmentService->restore($cardId, $attachmentId, $type); + } + /** - * @NoAdminRequired + * @return array{type: string, attachmentId: int} + * @throws BadRequestException */ - public function restore($cardId, $attachmentId) { + private function extractTypeAndAttachmentId(string $attachmentId): array { if (!str_contains($attachmentId, ':')) { $type = 'deck_file'; } else { - [$type, $attachmentId] = explode(':', $attachmentId); + [$type, $attachmentId] = [...explode(':', $attachmentId), '', '']; } - return $this->attachmentService->restore($cardId, $attachmentId, $type); + + if ($type === '' || !is_numeric($attachmentId)) { + throw new BadRequestException('Invalid attachment id'); + } + + return [ + 'type' => $type, + 'attachmentId' => (int)$attachmentId, + ]; } } diff --git a/lib/Controller/BoardApiController.php b/lib/Controller/BoardApiController.php index 574656af6..f6c948a22 100644 --- a/lib/Controller/BoardApiController.php +++ b/lib/Controller/BoardApiController.php @@ -12,10 +12,13 @@ use OCA\Deck\Service\BoardService; use OCA\Deck\StatusException; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\CORS; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; - use OCP\IRequest; -use Sabre\HTTP\Util; + +use function Sabre\HTTP\parseDate; /** * Class BoardApiController @@ -36,21 +39,18 @@ class BoardApiController extends ApiController { } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * Return all of the boards that the current user has access to. - * - * @param bool $details + * Return all the boards that the current user has access to. * @throws StatusException */ - public function index(bool $details = false) { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function index(bool $details = false): DataResponse { $modified = $this->request->getHeader('If-Modified-Since'); - if ($modified === null || $modified === '') { + if ($modified === '') { $boards = $this->boardService->findAll(0, $details === true); } else { - $date = Util::parseHTTPDate($modified); + $date = parseDate($modified); if (!$date) { throw new StatusException('Invalid If-Modified-Since header provided.'); } @@ -64,14 +64,12 @@ class BoardApiController extends ApiController { } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * * Return the board specified by $this->request->getParam('boardId'). */ - public function get() { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function get(): DataResponse { $board = $this->boardService->find($this->request->getParam('boardId')); $response = new DataResponse($board, HTTP::STATUS_OK); $response->setETag($board->getEtag()); @@ -79,68 +77,53 @@ class BoardApiController extends ApiController { } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * @params $title - * @params $color - * * Create a board with the specified title and color. */ - public function create($title, $color) { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function create(string $title, string $color): DataResponse { $board = $this->boardService->create($title, $this->userId, $color); return new DataResponse($board, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * @params $title - * @params $color - * @params $archived - * * Update a board with the specified boardId, title and color, and archived state. */ - public function update($title, $color, $archived = false) { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function update(string $title, string $color, bool $archived = false): DataResponse { $board = $this->boardService->update($this->request->getParam('boardId'), $title, $color, $archived); return new DataResponse($board, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * * Delete the board specified by $boardId. Return the board that was deleted. */ - public function delete() { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function delete(): DataResponse { $board = $this->boardService->delete($this->request->getParam('boardId')); return new DataResponse($board, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * * Undo the deletion of the board specified by $boardId. */ - public function undoDelete() { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function undoDelete(): DataResponse { $board = $this->boardService->deleteUndo($this->request->getParam('boardId')); return new DataResponse($board, HTTP::STATUS_OK); } - /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - */ - public function addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage) { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function addAcl(int $boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage) { $acl = $this->boardService->addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage); return new DataResponse($acl, HTTP::STATUS_OK); } diff --git a/lib/Controller/BoardController.php b/lib/Controller/BoardController.php index ebd856d4d..1b7326f80 100644 --- a/lib/Controller/BoardController.php +++ b/lib/Controller/BoardController.php @@ -14,6 +14,7 @@ use OCA\Deck\Service\Importer\BoardImportService; use OCA\Deck\Service\PermissionService; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\IL10N; use OCP\IRequest; @@ -31,68 +32,38 @@ class BoardController extends ApiController { parent::__construct($appName, $request); } - /** - * @NoAdminRequired - */ + #[NoAdminRequired] public function index() { return $this->boardService->findAll(); } - /** - * @NoAdminRequired - * @param $boardId - * @return \OCP\AppFramework\Db\Entity - */ - public function read(int $boardId) { + #[NoAdminRequired] + public function read(int $boardId): Board { return $this->boardService->find($boardId); } - /** - * @NoAdminRequired - * @param $title - * @param $color - * @return \OCP\AppFramework\Db\Entity - */ - public function create($title, $color) { + #[NoAdminRequired] + public function create(string $title, string $color): Board { return $this->boardService->create($title, $this->userId, $color); } - /** - * @NoAdminRequired - * @param $id - * @param $title - * @param $color - * @param $archived - * @return \OCP\AppFramework\Db\Entity - */ - public function update($id, $title, $color, $archived) { + #[NoAdminRequired] + public function update(int $id, string $title, string $color, bool $archived): Board { return $this->boardService->update($id, $title, $color, $archived); } - /** - * @NoAdminRequired - * @param $boardId - * @return \OCP\AppFramework\Db\Entity - */ - public function delete($boardId) { + #[NoAdminRequired] + public function delete(int $boardId): Board { return $this->boardService->delete($boardId); } - /** - * @NoAdminRequired - * @param $boardId - * @return \OCP\AppFramework\Db\Entity - */ - public function deleteUndo($boardId) { + + #[NoAdminRequired] + public function deleteUndo(int $boardId): Board { return $this->boardService->deleteUndo($boardId); } - /** - * @NoAdminRequired - * @param $boardId - * @return array|bool - * @internal param $userId - */ - public function getUserPermissions($boardId) { + #[NoAdminRequired] + public function getUserPermissions(int $boardId): array { $permissions = $this->permissionService->getPermissions($boardId); return [ 'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ], @@ -103,16 +74,10 @@ class BoardController extends ApiController { } /** - * @NoAdminRequired - * @param $boardId - * @param $type * @param $participant - * @param $permissionEdit - * @param $permissionShare - * @param $permissionManage - * @return \OCP\AppFramework\Db\Entity */ - public function addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage) { + #[NoAdminRequired] + public function addAcl(int $boardId, int $type, $participant, bool $permissionEdit, bool $permissionShare, bool $permissionManage): Acl { return $this->boardService->addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage); } diff --git a/lib/Controller/BoardImportApiController.php b/lib/Controller/BoardImportApiController.php index 2163a8c95..c5207b018 100644 --- a/lib/Controller/BoardImportApiController.php +++ b/lib/Controller/BoardImportApiController.php @@ -9,6 +9,9 @@ namespace OCA\Deck\Controller; use OCA\Deck\Service\Importer\BoardImportService; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\CORS; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; @@ -23,11 +26,9 @@ class BoardImportApiController extends OCSController { parent::__construct($appName, $request); } - /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - */ + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] public function import(string $system, array $config, array $data): DataResponse { $this->boardImportService->setSystem($system); $config = json_decode(json_encode($config)); @@ -38,21 +39,17 @@ class BoardImportApiController extends OCSController { return new DataResponse($this->boardImportService->getBoard(), Http::STATUS_OK); } - /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - */ + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] public function getAllowedSystems(): DataResponse { $allowedSystems = $this->boardImportService->getAllowedImportSystems(); return new DataResponse($allowedSystems, Http::STATUS_OK); } - /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - */ + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] public function getConfigSchema(string $name): DataResponse { $this->boardImportService->setSystem($name); $this->boardImportService->validateSystem(); diff --git a/lib/Controller/CardApiController.php b/lib/Controller/CardApiController.php index 276892f4c..bc1359250 100644 --- a/lib/Controller/CardApiController.php +++ b/lib/Controller/CardApiController.php @@ -12,6 +12,9 @@ use OCA\Deck\Service\AssignmentService; use OCA\Deck\Service\CardService; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\CORS; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; @@ -27,7 +30,7 @@ class CardApiController extends ApiController { * @param IRequest $request * @param CardService $cardService * @param AssignmentService $assignmentService - * @param $userId + * @param string $userId */ public function __construct( string $appName, @@ -80,112 +83,102 @@ class CardApiController extends ApiController { } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * * Update a card */ - public function update($title, $type, $owner, $description = '', $order = 0, $duedate = null, $archived = null) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function update(string $title, $type, string $owner, string $description = '', int $order = 0, $duedate = null, $archived = null): DataResponse { $done = array_key_exists('done', $this->request->getParams()) ? new OptionalNullableValue($this->request->getParam('done', null)) : null; $card = $this->cardService->update($this->request->getParam('cardId'), $title, $this->request->getParam('stackId'), $type, $owner, $description, $order, $duedate, 0, $archived, $done); return new DataResponse($card, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Delete a specific card. */ - public function delete() { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function delete(): DataResponse { $card = $this->cardService->delete($this->request->getParam('cardId')); return new DataResponse($card, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Assign a label to a card. */ - public function assignLabel($labelId) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function assignLabel(int $labelId): DataResponse { $card = $this->cardService->assignLabel($this->request->getParam('cardId'), $labelId); return new DataResponse($card, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Assign a label to a card. */ - public function removeLabel($labelId) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function removeLabel(int $labelId): DataResponse { $card = $this->cardService->removeLabel($this->request->getParam('cardId'), $labelId); return new DataResponse($card, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Assign a user to a card */ - public function assignUser($cardId, $userId, $type = 0) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function assignUser(int $cardId, string $userId, int $type = 0): DataResponse { $card = $this->assignmentService->assignUser($cardId, $userId, $type); return new DataResponse($card, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Unassign a user from a card */ - public function unassignUser($cardId, $userId, $type = 0) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function unassignUser(int $cardId, string $userId, int $type = 0): DataResponse { $card = $this->assignmentService->unassignUser($cardId, $userId, $type); return new DataResponse($card, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Archive card */ - public function archive($cardId) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function archive(int $cardId): DataResponse { $card = $this->cardService->archive($cardId); return new DataResponse($card, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Unarchive card */ - public function unarchive($cardId) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function unarchive(int $cardId): DataResponse { $card = $this->cardService->unarchive($cardId); return new DataResponse($card, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Reorder cards */ - public function reorder($stackId, $order) { - $card = $this->cardService->reorder((int)$this->request->getParam('cardId'), (int)$stackId, (int)$order); + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function reorder(int $stackId, int $order): DataResponse { + $card = $this->cardService->reorder((int)$this->request->getParam('cardId'), $stackId, $order); return new DataResponse($card, HTTP::STATUS_OK); } } diff --git a/lib/Controller/CardController.php b/lib/Controller/CardController.php index 6328fb5cc..9aabdee16 100644 --- a/lib/Controller/CardController.php +++ b/lib/Controller/CardController.php @@ -7,9 +7,12 @@ namespace OCA\Deck\Controller; +use OCA\Deck\Db\Assignment; +use OCA\Deck\Db\Card; use OCA\Deck\Service\AssignmentService; use OCA\Deck\Service\CardService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\IRequest; class CardController extends Controller { @@ -23,45 +26,26 @@ class CardController extends Controller { parent::__construct($appName, $request); } - /** - * @NoAdminRequired - * @param $cardId - * @return \OCP\AppFramework\Db\Entity - */ - public function read($cardId) { + #[NoAdminRequired] + public function read(int $cardId): Card { return $this->cardService->find($cardId); } /** - * @NoAdminRequired - * @param $cardId - * @param $stackId - * @param $order - * @return array + * @return Card[] */ - public function reorder($cardId, $stackId, $order) { - return $this->cardService->reorder((int)$cardId, (int)$stackId, (int)$order); + #[NoAdminRequired] + public function reorder(int $cardId, int $stackId, int $order): array { + return $this->cardService->reorder($cardId, $stackId, $order); } - /** - * @NoAdminRequired - * @param $cardId - * @param $title - * @return \OCP\AppFramework\Db\Entity - */ - public function rename($cardId, $title) { + #[NoAdminRequired] + public function rename(int $cardId, string $title): Card { return $this->cardService->rename($cardId, $title); } - /** - * @NoAdminRequired - * @param $title - * @param $stackId - * @param $type - * @param int $order - * @return \OCP\AppFramework\Db\Entity - */ - public function create($title, $stackId, $type = 'plain', $order = 999, string $description = '', $duedate = null, $labels = [], $users = []) { + #[NoAdminRequired] + public function create(string $title, int $stackId, string $type = 'plain', int $order = 999, string $description = '', $duedate = null, array $labels = [], array $users = []): Card { $card = $this->cardService->create($title, $stackId, $type, $order, $this->userId, $description, $duedate); foreach ($labels as $label) { @@ -76,113 +60,68 @@ class CardController extends Controller { } /** - * @NoAdminRequired - * @param $id - * @param $title - * @param $stackId - * @param $type - * @param $order - * @param $description * @param $duedate - * @param $deletedAt - * @return \OCP\AppFramework\Db\Entity */ - public function update($id, $title, $stackId, $type, $order, $description, $duedate, $deletedAt) { + #[NoAdminRequired] + public function update(int $id, string $title, int $stackId, string $type, int $order, string $description, $duedate, $deletedAt): Card { return $this->cardService->update($id, $title, $stackId, $type, $this->userId, $description, $order, $duedate, $deletedAt); } - /** - * @NoAdminRequired - * @param $cardId - * @param $targetStackId - * @return \OCP\AppFramework\Db\Entity - */ - public function clone(int $cardId, ?int $targetStackId = null) { + + #[NoAdminRequired] + public function clone(int $cardId, ?int $targetStackId = null): Card { return $this->cardService->cloneCard($cardId, $targetStackId); } - /** - * @NoAdminRequired - * @param $cardId - * @return \OCP\AppFramework\Db\Entity - */ - public function delete($cardId) { + #[NoAdminRequired] + public function delete(int $cardId): Card { return $this->cardService->delete($cardId); } /** - * @NoAdminRequired - * @param $boardId - * @return \OCP\AppFramework\Db\Entity + * @return Card[] */ - public function deleted($boardId) { + #[NoAdminRequired] + public function deleted(int $boardId): array { return $this->cardService->fetchDeleted($boardId); } - /** - * @NoAdminRequired - * @param $cardId - * @return \OCP\AppFramework\Db\Entity - */ + #[NoAdminRequired] public function archive($cardId) { return $this->cardService->archive($cardId); } - /** - * @NoAdminRequired - * @param $cardId - * @return \OCP\AppFramework\Db\Entity - */ - public function unarchive($cardId) { + #[NoAdminRequired] + public function unarchive(int $cardId): Card { return $this->cardService->unarchive($cardId); } - /** - * @NoAdminRequired - * @param $cardId - * @return \OCP\AppFramework\Db\Entity - */ - public function done(int $cardId) { + #[NoAdminRequired] + public function done(int $cardId): Card { return $this->cardService->done($cardId); } - /** - * @NoAdminRequired - * @param $cardId - * @return \OCP\AppFramework\Db\Entity - */ - public function undone(int $cardId) { + #[NoAdminRequired] + public function undone(int $cardId): Card { return $this->cardService->undone($cardId); } - /** - * @NoAdminRequired - * @param $cardId - * @param $labelId - */ - public function assignLabel($cardId, $labelId) { + #[NoAdminRequired] + public function assignLabel(int $cardId, int $labelId): void { $this->cardService->assignLabel($cardId, $labelId); } - /** - * @NoAdminRequired - * @param $cardId - * @param $labelId - */ - public function removeLabel($cardId, $labelId) { + #[NoAdminRequired] + public function removeLabel(int $cardId, int $labelId): void { $this->cardService->removeLabel($cardId, $labelId); } - /** - * @NoAdminRequired - */ - public function assignUser($cardId, $userId, $type = 0) { + #[NoAdminRequired] + public function assignUser(int $cardId, string $userId, int $type = 0): Assignment { return $this->assignmentService->assignUser($cardId, $userId, $type); } - /** - * @NoAdminRequired - */ - public function unassignUser($cardId, $userId, $type = 0) { + #[NoAdminRequired] + public function unassignUser(int $cardId, string $userId, int $type = 0): Assignment { return $this->assignmentService->unassignUser($cardId, $userId, $type); } } diff --git a/lib/Controller/CommentsApiController.php b/lib/Controller/CommentsApiController.php index c05f54c13..eddc224d3 100644 --- a/lib/Controller/CommentsApiController.php +++ b/lib/Controller/CommentsApiController.php @@ -9,11 +9,15 @@ namespace OCA\Deck\Controller; use OCA\Deck\Service\CommentService; use OCA\Deck\StatusException; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; +/** + * @psalm-api + */ class CommentsApiController extends OCSController { public function __construct( string $appName, @@ -27,33 +31,33 @@ class CommentsApiController extends OCSController { } /** - * @NoAdminRequired * @throws StatusException */ - public function list(string $cardId, int $limit = 20, int $offset = 0): DataResponse { + #[NoAdminRequired] + public function list(int $cardId, int $limit = 20, int $offset = 0): DataResponse { return $this->commentService->list($cardId, $limit, $offset); } /** - * @NoAdminRequired * @throws StatusException */ + #[NoAdminRequired] public function create(int $cardId, string $message, int $parentId = 0): DataResponse { return $this->commentService->create($cardId, $message, $parentId); } /** - * @NoAdminRequired * @throws StatusException */ + #[NoAdminRequired] public function update(int $cardId, int $commentId, string $message): DataResponse { return $this->commentService->update($cardId, $commentId, $message); } /** - * @NoAdminRequired * @throws StatusException */ + #[NoAdminRequired] public function delete(int $cardId, int $commentId): DataResponse { return $this->commentService->delete($cardId, $commentId); } diff --git a/lib/Controller/ConfigController.php b/lib/Controller/ConfigController.php index a41601bb9..d32b87656 100644 --- a/lib/Controller/ConfigController.php +++ b/lib/Controller/ConfigController.php @@ -8,6 +8,8 @@ namespace OCA\Deck\Controller; use OCA\Deck\Service\ConfigService; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\OCSController; @@ -22,19 +24,15 @@ class ConfigController extends OCSController { parent::__construct($AppName, $request); } - /** - * @NoCSRFRequired - * @NoAdminRequired - */ + #[NoAdminRequired] + #[NoCSRFRequired] public function get(): DataResponse { return new DataResponse($this->configService->getAll()); } - /** - * @NoCSRFRequired - * @NoAdminRequired - */ - public function setValue(string $key, $value) { + #[NoAdminRequired] + #[NoCSRFRequired] + public function setValue(string $key, mixed $value): DataResponse|NotFoundResponse { $result = $this->configService->set($key, $value); if ($result === null) { return new NotFoundResponse(); diff --git a/lib/Controller/LabelApiController.php b/lib/Controller/LabelApiController.php index f4e4b3457..5e9b2d665 100644 --- a/lib/Controller/LabelApiController.php +++ b/lib/Controller/LabelApiController.php @@ -10,6 +10,9 @@ namespace OCA\Deck\Controller; use OCA\Deck\Service\LabelService; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\CORS; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; @@ -26,59 +29,50 @@ class LabelApiController extends ApiController { $appName, IRequest $request, private LabelService $labelService, - private $userId, ) { parent::__construct($appName, $request); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Get a specific label. */ - public function get() { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function get(): DataResponse { $label = $this->labelService->find($this->request->getParam('labelId')); return new DataResponse($label, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * @params $title - * @params $color * Create a new label */ - public function create($title, $color) { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function create(string $title, string $color): DataResponse { $label = $this->labelService->create($title, $color, $this->request->getParam('boardId')); return new DataResponse($label, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * @params $title - * @params $color * Update a specific label */ - public function update($title, $color) { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function update(string $title, string $color): DataResponse { $label = $this->labelService->update($this->request->getParam('labelId'), $title, $color); return new DataResponse($label, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Delete a specific label */ - public function delete() { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function delete(): DataResponse { $label = $this->labelService->delete($this->request->getParam('labelId')); return new DataResponse($label, HTTP::STATUS_OK); } diff --git a/lib/Controller/LabelController.php b/lib/Controller/LabelController.php index 83eae9f4f..c5c368caa 100644 --- a/lib/Controller/LabelController.php +++ b/lib/Controller/LabelController.php @@ -7,8 +7,10 @@ namespace OCA\Deck\Controller; +use OCA\Deck\Db\Label; use OCA\Deck\Service\LabelService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\IRequest; class LabelController extends Controller { @@ -20,34 +22,18 @@ class LabelController extends Controller { parent::__construct($appName, $request); } - /** - * @NoAdminRequired - * @param $title - * @param $color - * @param $boardId - * @return \OCP\AppFramework\Db\Entity - */ - public function create($title, $color, $boardId) { + #[NoAdminRequired] + public function create(string $title, string $color, int $boardId): Label { return $this->labelService->create($title, $color, $boardId); } - /** - * @NoAdminRequired - * @param $id - * @param $title - * @param $color - * @return \OCP\AppFramework\Db\Entity - */ - public function update($id, $title, $color) { + #[NoAdminRequired] + public function update(int $id, string $title, string $color): Label { return $this->labelService->update($id, $title, $color); } - /** - * @NoAdminRequired - * @param $labelId - * @return \OCP\AppFramework\Db\Entity - */ - public function delete($labelId) { + #[NoAdminRequired] + public function delete(int $labelId): Label { return $this->labelService->delete($labelId); } } diff --git a/lib/Controller/OverviewApiController.php b/lib/Controller/OverviewApiController.php index 3567f33dd..937442531 100644 --- a/lib/Controller/OverviewApiController.php +++ b/lib/Controller/OverviewApiController.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace OCA\Deck\Controller; use OCA\Deck\Service\OverviewService; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; @@ -24,9 +25,7 @@ class OverviewApiController extends OCSController { parent::__construct($appName, $request); } - /** - * @NoAdminRequired - */ + #[NoAdminRequired] public function upcomingCards(): DataResponse { return new DataResponse($this->dashboardService->findUpcomingCards($this->userId)); } diff --git a/lib/Controller/SearchController.php b/lib/Controller/SearchController.php index 32f93e120..d3989e78a 100644 --- a/lib/Controller/SearchController.php +++ b/lib/Controller/SearchController.php @@ -13,6 +13,7 @@ namespace OCA\Deck\Controller; use OCA\Deck\Db\Card; use OCA\Deck\Model\CardDetails; use OCA\Deck\Service\SearchService; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; @@ -26,9 +27,7 @@ class SearchController extends OCSController { parent::__construct($appName, $request); } - /** - * @NoAdminRequired - */ + #[NoAdminRequired] public function search(string $term, ?int $limit = null, ?int $cursor = null): DataResponse { $cards = $this->searchService->searchCards($term, $limit, $cursor); return new DataResponse(array_map(function (Card $card) { diff --git a/lib/Controller/StackApiController.php b/lib/Controller/StackApiController.php index a964c982e..06570dc8b 100644 --- a/lib/Controller/StackApiController.php +++ b/lib/Controller/StackApiController.php @@ -7,14 +7,16 @@ namespace OCA\Deck\Controller; -use OCA\Deck\Service\BoardService; use OCA\Deck\Service\StackService; use OCA\Deck\StatusException; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\CORS; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; -use Sabre\HTTP\Util; +use function Sabre\HTTP\parseDate; /** * Class StackApiController @@ -29,23 +31,21 @@ class StackApiController extends ApiController { $appName, IRequest $request, private StackService $stackService, - private BoardService $boardService, ) { parent::__construct($appName, $request); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * Return all of the stacks in the specified board. + * Return all the stacks in the specified board. */ - public function index() { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function index(): DataResponse { $since = 0; $modified = $this->request->getHeader('If-Modified-Since'); - if ($modified !== null && $modified !== '') { - $date = Util::parseHTTPDate($modified); + if ($modified !== '') { + $date = parseDate($modified); if (!$date) { throw new StatusException('Invalid If-Modified-Since header provided.'); } @@ -56,13 +56,12 @@ class StackApiController extends ApiController { } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * Return all of the stacks in the specified board. + * Return all the stacks in the specified board. */ - public function get() { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function get(): DataResponse { $stack = $this->stackService->find($this->request->getParam('stackId')); $response = new DataResponse($stack, HTTP::STATUS_OK); $response->setETag($stack->getETag()); @@ -70,55 +69,45 @@ class StackApiController extends ApiController { } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * @params $title - * @params $order - * * Create a stack with the specified title and order. */ - public function create($title, $order) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function create(string $title, int $order): DataResponse { $stack = $this->stackService->create($title, $this->request->getParam('boardId'), $order); return new DataResponse($stack, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * @params $title - * @params $order - * * Update a stack by the specified stackId and boardId with the values that were put. */ - public function update($title, $order) { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function update(string $title, int $order) { $stack = $this->stackService->update($this->request->getParam('stackId'), $title, $this->request->getParam('boardId'), $order, 0); return new DataResponse($stack, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * * Delete the stack specified by $this->request->getParam('stackId'). */ - public function delete() { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function delete(): DataResponse { $stack = $this->stackService->delete($this->request->getParam('stackId')); return new DataResponse($stack, HTTP::STATUS_OK); } /** - * @NoAdminRequired - * @CORS - * @NoCSRFRequired - * - * get the stacks that have been archived. + * Get the stacks that have been archived. */ - public function getArchived() { + #[NoAdminRequired] + #[CORS] + #[NoCSRFRequired] + public function getArchived(): DataResponse { $stacks = $this->stackService->findAllArchived($this->request->getParam('boardId')); return new DataResponse($stacks, HTTP::STATUS_OK); } diff --git a/lib/Controller/StackController.php b/lib/Controller/StackController.php index baead328c..712eacffd 100644 --- a/lib/Controller/StackController.php +++ b/lib/Controller/StackController.php @@ -7,10 +7,12 @@ namespace OCA\Deck\Controller; +use OCA\Deck\Db\Stack; use OCA\Deck\Service\StackService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\IRequest; class StackController extends Controller { @@ -18,78 +20,54 @@ class StackController extends Controller { string $appName, IRequest $request, private StackService $stackService, - private $userId, ) { parent::__construct($appName, $request); } /** - * @NoAdminRequired - * @param $boardId - * @return array + * @return Stack[] */ - public function index($boardId) { + #[NoAdminRequired] + public function index(int $boardId): array { return $this->stackService->findAll($boardId); } /** - * @NoAdminRequired - * @param $boardId - * @return array + * @return Stack[] */ - public function archived($boardId) { + #[NoAdminRequired] + public function archived(int $boardId): array { return $this->stackService->findAllArchived($boardId); } - /** - * @NoAdminRequired - * @param $title - * @param $boardId - * @param int $order - * @return \OCP\AppFramework\Db\Entity - */ - public function create($title, $boardId, $order = 999) { + #[NoAdminRequired] + public function create(string $title, int $boardId, int $order = 999): Stack { return $this->stackService->create($title, $boardId, $order); } - /** - * @NoAdminRequired - * @param $id - * @param $title - * @param $boardId - * @param $order - * @param $deletedAt - * @return \OCP\AppFramework\Db\Entity - */ - public function update($id, $title, $boardId, $order, $deletedAt) { + #[NoAdminRequired] + public function update(int $id, string $title, int $boardId, int $order, ?int $deletedAt = null): Stack { return $this->stackService->update($id, $title, $boardId, $order, $deletedAt); } /** - * @NoAdminRequired - * @param $stackId - * @param $order - * @return array + * @return array */ - public function reorder($stackId, $order) { - return $this->stackService->reorder((int)$stackId, (int)$order); + #[NoAdminRequired] + public function reorder(int $stackId, int $order): array { + return $this->stackService->reorder($stackId, $order); } - /** - * @NoAdminRequired - * @param $stackId - * @return \OCP\AppFramework\Db\Entity - */ - public function delete($stackId) { + #[NoAdminRequired] + public function delete(int $stackId): Stack { return $this->stackService->delete($stackId); } /** - * @NoAdminRequired - * @param $boardId - * @return \OCP\AppFramework\Db\Entity + * @return Stack[] */ - public function deleted($boardId) { + #[NoAdminRequired] + public function deleted(int $boardId): array { return $this->stackService->fetchDeleted($boardId); } } diff --git a/lib/Db/Acl.php b/lib/Db/Acl.php index 41f0cfaf1..77c210169 100644 --- a/lib/Db/Acl.php +++ b/lib/Db/Acl.php @@ -7,6 +7,20 @@ namespace OCA\Deck\Db; +/** + * @method int getBoardId() + * @method bool isPermissionEdit() + * @method void setPermissionEdit(bool $permissionEdit) + * @method bool isPermissionShare() + * @method void setPermissionShare(bool $permissionShare) + * @method bool isPermissionManage() + * @method void setPermissionManage(bool $permissionManage) + * @method int getType() + * @method void setType(int $type) + * @method bool isOwner() + * @method void setOwner(int $owner) + * + */ class Acl extends RelationalEntity { public const PERMISSION_READ = 0; public const PERMISSION_EDIT = 1; @@ -37,17 +51,13 @@ class Acl extends RelationalEntity { $this->addResolvable('participant'); } - public function getPermission($permission) { - switch ($permission) { - case self::PERMISSION_READ: - return true; - case self::PERMISSION_EDIT: - return $this->getPermissionEdit(); - case self::PERMISSION_SHARE: - return $this->getPermissionShare(); - case self::PERMISSION_MANAGE: - return $this->getPermissionManage(); - } - return false; + public function getPermission(int $permission): bool { + return match ($permission) { + self::PERMISSION_READ => true, + self::PERMISSION_EDIT => $this->getPermissionEdit(), + self::PERMISSION_SHARE => $this->getPermissionShare(), + self::PERMISSION_MANAGE => $this->getPermissionManage(), + default => false, + }; } } diff --git a/lib/Db/AclMapper.php b/lib/Db/AclMapper.php index 656a231e8..52c087dd5 100644 --- a/lib/Db/AclMapper.php +++ b/lib/Db/AclMapper.php @@ -19,13 +19,10 @@ class AclMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $boardId - * @param int|null $limit - * @param int|null $offset * @return Acl[] * @throws \OCP\DB\Exception */ - public function findAll($boardId, $limit = null, $offset = null) { + public function findAll(int $boardId, ?int $limit = null, ?int $offset = null) { $qb = $this->db->getQueryBuilder(); $qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage') ->from('deck_board_acl') @@ -51,12 +48,9 @@ class AclMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $userId - * @param numeric $id - * @return bool * @throws \OCP\DB\Exception */ - public function isOwner($userId, $id): bool { + public function isOwner(string $userId, int $id): bool { $aclId = $id; $qb = $this->db->getQueryBuilder(); $qb->select('acl.id') @@ -68,11 +62,7 @@ class AclMapper extends DeckMapper implements IPermissionMapper { return count($qb->executeQuery()->fetchAll()) > 0; } - /** - * @param numeric $id - * @return int|null - */ - public function findBoardId($id): ?int { + public function findBoardId(int $id): ?int { try { $entity = $this->find($id); return $entity->getBoardId(); @@ -87,7 +77,7 @@ class AclMapper extends DeckMapper implements IPermissionMapper { * @return Acl[] * @throws \OCP\DB\Exception */ - public function findByParticipant($type, $participant): array { + public function findByParticipant(int $type, string $participant): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') diff --git a/lib/Db/AssignmentMapper.php b/lib/Db/AssignmentMapper.php index 0d15d6d51..da1af6405 100644 --- a/lib/Db/AssignmentMapper.php +++ b/lib/Db/AssignmentMapper.php @@ -107,11 +107,11 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper { } - public function isOwner($userId, $id): bool { + public function isOwner(string $userId, int $id): bool { return $this->cardMapper->isOwner($userId, $id); } - public function findBoardId($id): ?int { + public function findBoardId(int $id): ?int { return $this->cardMapper->findBoardId($id); } @@ -123,6 +123,9 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper { * @throws NotFoundException */ public function insert(Entity $entity): Entity { + if (!($entity instanceof Assignment)) { + throw new \LogicException('Trying to insert a ' . get_class($entity) . ' in the assignment mapper'); + } $origin = $this->getOrigin($entity); if ($origin === null) { throw new NotFoundException('No origin found for assignment'); @@ -141,7 +144,7 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper { }); } - public function isUserAssigned($cardId, $userId): bool { + public function isUserAssigned(int $cardId, string $userId): bool { $assignments = $this->findAll($cardId); foreach ($assignments as $assignment) { $origin = $this->getOrigin($assignment); diff --git a/lib/Db/AttachmentMapper.php b/lib/Db/AttachmentMapper.php index 52864a3c8..36d947c61 100644 --- a/lib/Db/AttachmentMapper.php +++ b/lib/Db/AttachmentMapper.php @@ -36,13 +36,11 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper { } /** - * @param int $id - * @return Attachment * @throws DoesNotExistException * @throws MultipleObjectsReturnedException * @throws \OCP\DB\Exception */ - public function find($id) { + public function find(int $id): Attachment { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->getTableName()) @@ -52,14 +50,11 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper { } /** - * @param int $cardId - * @param string $data - * @return Attachment * @throws DoesNotExistException * @throws MultipleObjectsReturnedException * @throws \OCP\DB\Exception */ - public function findByData($cardId, $data) { + public function findByData(int $cardId, string $data): Attachment { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->getTableName()) @@ -70,11 +65,10 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper { } /** - * @param $cardId * @return Entity[] * @throws \OCP\DB\Exception */ - public function findAll($cardId) { + public function findAll(int $cardId): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->getTableName()) @@ -86,11 +80,9 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper { } /** - * @param null $cardId - * @param bool $withOffset - * @return array + * @return Attachment[] */ - public function findToDelete($cardId = null, $withOffset = true) { + public function findToDelete(?int $cardId = null, bool $withOffset = true): array { // add buffer of 5 min $timeLimit = time() - (60 * 5); $qb = $this->db->getQueryBuilder(); @@ -112,12 +104,8 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper { /** * Check if $userId is owner of Entity with $id - * - * @param $userId string userId - * @param $id int|string unique entity identifier - * @return boolean */ - public function isOwner($userId, $id): bool { + public function isOwner(string $userId, int $id): bool { try { $attachment = $this->find($id); return $this->cardMapper->isOwner($userId, $attachment->getCardId()); @@ -130,10 +118,10 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper { /** * Query boardId for Entity of given $id * - * @param $id int|string unique entity identifier + * @param $id int unique entity identifier * @return int|null id of Board */ - public function findBoardId($id): ?int { + public function findBoardId(int $id): ?int { try { $attachment = $this->find($id); } catch (\Exception $e) { diff --git a/lib/Db/Board.php b/lib/Db/Board.php index 82c56f265..500eb59b9 100644 --- a/lib/Db/Board.php +++ b/lib/Db/Board.php @@ -10,10 +10,20 @@ namespace OCA\Deck\Db; /** * @method int getId() * @method string getTitle() + * @method void setTitle(string $title) * @method int getShared() + * @method void setShared(int $shared) + * @method bool isArchived() * @method bool getArchived() + * @method void setArchived(bool $archived) * @method int getDeletedAt() + * @method void setDeletedAt(int $deletedAt) * @method int getLastModified() + * @method void setLastModified(int $lastModified) + * @method string getOwner() + * @method void setOwner(string $owner) + * @method string getColor() + * @method void setColor(string $color) */ class Board extends RelationalEntity { protected $title; diff --git a/lib/Db/BoardMapper.php b/lib/Db/BoardMapper.php index 13cc3f276..ebf4a672e 100644 --- a/lib/Db/BoardMapper.php +++ b/lib/Db/BoardMapper.php @@ -469,16 +469,16 @@ class BoardMapper extends QBMapper implements IPermissionMapper { return parent::delete($entity); } - public function isOwner($userId, $id): bool { + public function isOwner(string $userId, int $id): bool { $board = $this->find($id); return ($board->getOwner() === $userId); } - public function findBoardId($id): ?int { + public function findBoardId(int $id): ?int { return $id; } - public function mapAcl(Acl &$acl) { + public function mapAcl(Acl &$acl): void { $acl->resolveRelation('participant', function ($participant) use (&$acl) { if ($acl->getType() === Acl::PERMISSION_TYPE_USER) { if ($this->userManager->userExists($acl->getParticipant())) { diff --git a/lib/Db/Card.php b/lib/Db/Card.php index e5e0b87ae..cd22f1330 100644 --- a/lib/Db/Card.php +++ b/lib/Db/Card.php @@ -15,13 +15,18 @@ use Sabre\VObject\Component\VCalendar; /** * @method string getTitle() + * @method void setTitle(string $title) * @method string getDescription() * @method string getDescriptionPrev() * @method int getStackId() + * @method void setStackId(int $stackId) * @method int getOrder() + * @method void setOrder(int $order) * @method int getLastModified() * @method int getCreatedAt() * @method bool getArchived() + * @method string getType() + * @method void setType(string $type) * @method int getDeletedAt() * @method void setDeletedAt(int $deletedAt) * @method bool getNotified() @@ -68,8 +73,8 @@ class Card extends RelationalEntity { protected $createdAt; protected $labels; protected $assignedUsers; - protected $attachments; - protected $attachmentCount; + protected array $attachments = []; + protected int $attachmentCount = 0; protected $owner; protected $order; protected $archived = false; diff --git a/lib/Db/CardMapper.php b/lib/Db/CardMapper.php index d4f521d2f..f47bea2d9 100644 --- a/lib/Db/CardMapper.php +++ b/lib/Db/CardMapper.php @@ -86,16 +86,15 @@ class CardMapper extends QBMapper implements IPermissionMapper { $updatedFields = $entity->getUpdatedFields(); if (isset($updatedFields['duedate']) && $updatedFields['duedate']) { try { - /** @var Card $existing */ $existing = $this->find($entity->getId()); - if ($existing && $entity->getDuedate() !== $existing->getDuedate()) { + if ($entity->getDueDate() !== $existing->getDueDate()) { $entity->setNotified(false); } // remove pending notifications $notification = $this->notificationManager->createNotification(); $notification ->setApp('deck') - ->setObject('card', $entity->getId()); + ->setObject('card', (string)$entity->getId()); $this->notificationManager->markProcessed($notification); } catch (Exception $e) { } @@ -135,7 +134,7 @@ class CardMapper extends QBMapper implements IPermissionMapper { * @return Card[] * @throws \OCP\DB\Exception */ - public function findAll($stackId, ?int $limit = null, ?int $offset = null, int $since = -1) { + public function findAll($stackId, ?int $limit = null, int $offset = 0, int $since = -1) { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from('deck_cards') @@ -155,7 +154,7 @@ class CardMapper extends QBMapper implements IPermissionMapper { * @return array * @throws \OCP\DB\Exception */ - public function findAllForStacks(array $stackIds, ?int $limit = null, ?int $offset = null, int $since = -1): array { + public function findAllForStacks(array $stackIds, ?int $limit = null, int $offset = 0, int $since = -1): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from('deck_cards') @@ -194,7 +193,10 @@ class CardMapper extends QBMapper implements IPermissionMapper { return $qb; } - public function findToDelete($timeLimit, $limit = null) { + /** + * @return Card[] + */ + public function findToDelete(int $timeLimit, ?int $limit = null): array { $qb = $this->db->getQueryBuilder(); $qb->select('id', 'title', 'owner', 'archived', 'deleted_at', 'last_modified') ->from('deck_cards') @@ -205,7 +207,10 @@ class CardMapper extends QBMapper implements IPermissionMapper { return $this->findEntities($qb); } - public function findDeleted($boardId, $limit = null, $offset = null) { + /** + * @return Card[] + */ + public function findDeleted(int $boardId, ?int $limit = null, int $offset = 0): array { $qb = $this->queryCardsByBoard($boardId); $qb->andWhere($qb->expr()->neq('c.deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT))) ->setMaxResults($limit) @@ -215,7 +220,10 @@ class CardMapper extends QBMapper implements IPermissionMapper { return $this->findEntities($qb); } - public function findCalendarEntries($boardId, $limit = null, $offset = null) { + /** + * @return Card[] + */ + public function findCalendarEntries(int $boardId, ?int $limit = null, $offset = 0): array { $qb = $this->db->getQueryBuilder(); $qb->select('c.*') ->from('deck_cards', 'c') @@ -270,7 +278,11 @@ class CardMapper extends QBMapper implements IPermissionMapper { return $this->findEntities($qb); } - public function findAllWithDue(array $boardIds) { + /** + * @param int[] $boardIds + * @return Card[] + */ + public function findAllWithDue(array $boardIds): array { $qb = $this->db->getQueryBuilder(); $qb->select('c.*') ->from('deck_cards', 'c') @@ -287,7 +299,11 @@ class CardMapper extends QBMapper implements IPermissionMapper { return $this->findEntities($qb); } - public function findToMeOrNotAssignedCards(array $boardIds, string $username) { + /** + * @param int[] $boardIds + * @return Card[] + */ + public function findToMeOrNotAssignedCards(array $boardIds, string $username): array { $qb = $this->db->getQueryBuilder(); $qb->select('c.*') ->from('deck_cards', 'c') @@ -309,7 +325,10 @@ class CardMapper extends QBMapper implements IPermissionMapper { return $this->findEntities($qb); } - public function findOverdue() { + /** + * @return Card[] + */ + public function findOverdue(): array { $qb = $this->db->getQueryBuilder(); $qb->select('id', 'title', 'duedate', 'notified') ->from('deck_cards') @@ -321,6 +340,9 @@ class CardMapper extends QBMapper implements IPermissionMapper { return $this->findEntities($qb); } + /** + * @return Card[] + */ public function findUnexposedDescriptionChances() { $qb = $this->db->getQueryBuilder(); $qb->select('id', 'title', 'duedate', 'notified', 'description_prev', 'last_editor', 'description') @@ -329,6 +351,9 @@ class CardMapper extends QBMapper implements IPermissionMapper { return $this->findEntities($qb); } + /** + * @return Card[] + */ public function search(array $boardIds, SearchQuery $query, ?int $limit = null, ?int $offset = null): array { $qb = $this->queryCardsByBoards($boardIds); $this->extendQueryByFilter($qb, $query); @@ -363,7 +388,7 @@ class CardMapper extends QBMapper implements IPermissionMapper { $qb->andWhere($qb->expr()->lt('c.last_modified', $qb->createNamedParameter($offset, IQueryBuilder::PARAM_INT))); } - $result = $qb->execute(); + $result = $qb->executeQuery(); $entities = []; while ($row = $result->fetch()) { $entities[] = Card::fromRow($row); @@ -406,7 +431,7 @@ class CardMapper extends QBMapper implements IPermissionMapper { $qb->andWhere($qb->expr()->lt('comments.id', $qb->createNamedParameter($offset, IQueryBuilder::PARAM_INT))); } - $result = $qb->execute(); + $result = $qb->executeQuery(); $entities = $result->fetchAll(); $result->closeCursor(); return $entities; @@ -502,7 +527,7 @@ class CardMapper extends QBMapper implements IPermissionMapper { }); $groups = $this->groupManager->search($assignment->getValue()); foreach ($searchUsers as $user) { - $groups = array_merge($groups, $this->groupManager->getUserIdGroups($user->getUID())); + $groups = array_merge($groups, $this->groupManager->getUserGroups($user)); } $assignmentSearches = []; @@ -555,7 +580,7 @@ class CardMapper extends QBMapper implements IPermissionMapper { if ($offset !== null) { $qb->setFirstResult($offset); } - $result = $qb->execute(); + $result = $qb->executeQuery(); $all = $result->fetchAll(); $result->closeCursor(); return $all; @@ -567,32 +592,32 @@ class CardMapper extends QBMapper implements IPermissionMapper { return parent::delete($entity); } - public function deleteByStack($stackId) { + public function deleteByStack($stackId): void { $cards = $this->findAllByStack($stackId); foreach ($cards as $card) { $this->delete($card); } } - public function assignLabel($card, $label) { + public function assignLabel(int $card, int $label): void { $qb = $this->db->getQueryBuilder(); $qb->insert('deck_assigned_labels') ->values([ 'label_id' => $qb->createNamedParameter($label, IQueryBuilder::PARAM_INT), 'card_id' => $qb->createNamedParameter($card, IQueryBuilder::PARAM_INT), ]); - $qb->execute(); + $qb->executeStatement(); } - public function removeLabel($card, $label) { + public function removeLabel(int $card, int $label): void { $qb = $this->db->getQueryBuilder(); $qb->delete('deck_assigned_labels') ->where($qb->expr()->eq('card_id', $qb->createNamedParameter($card, IQueryBuilder::PARAM_INT))) ->andWhere($qb->expr()->eq('label_id', $qb->createNamedParameter($label, IQueryBuilder::PARAM_INT))); - $qb->execute(); + $qb->executeStatement(); } - public function isOwner($userId, $id): bool { + public function isOwner(string $userId, int $id): bool { $qb = $this->db->getQueryBuilder(); $qb->select('c.id') ->from($this->getTableName(), 'c') @@ -604,7 +629,7 @@ class CardMapper extends QBMapper implements IPermissionMapper { return count($qb->executeQuery()->fetchAll()) > 0; } - public function findBoardId($id): ?int { + public function findBoardId(int $id): ?int { $result = $this->cache->get('findBoardId:' . $id); if ($result === null) { try { @@ -634,13 +659,11 @@ class CardMapper extends QBMapper implements IPermissionMapper { } public function transferOwnership(string $ownerId, string $newOwnerId, ?int $boardId = null): void { - $params = [ - 'owner' => $ownerId, - 'newOwner' => $newOwnerId - ]; - $sql = "UPDATE `*PREFIX*{$this->tableName}` SET `owner` = :newOwner WHERE `owner` = :owner"; - $stmt = $this->db->executeQuery($sql, $params); - $stmt->closeCursor(); + $qb = $this->db->getQueryBuilder(); + $qb->update($this->getTableName()) + ->set('owner', $qb->createNamedParameter($newOwnerId, IQueryBuilder::PARAM_STR)) + ->where('owner', $qb->createNamedParameter($ownerId, IQueryBuilder::PARAM_STR)) + ->executeStatement(); } public function remapCardOwner(int $boardId, string $userId, string $newUserId): void { diff --git a/lib/Db/DeckMapper.php b/lib/Db/DeckMapper.php index ec2d0fb88..b059ee32e 100644 --- a/lib/Db/DeckMapper.php +++ b/lib/Db/DeckMapper.php @@ -19,12 +19,11 @@ use OCP\DB\QueryBuilder\IQueryBuilder; abstract class DeckMapper extends QBMapper { /** - * @param $id * @return T * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws \OCP\AppFramework\Db\DoesNotExistException */ - public function find($id) { + public function find(int $id): Entity { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->getTableName()) @@ -37,7 +36,7 @@ abstract class DeckMapper extends QBMapper { * Helper function to split passed array into chunks of 1000 elements and * call a given callback for fetching query results * - * Can be useful to limit to 1000 results per query for oracle compatiblity + * Can be useful to limit to 1000 results per query for oracle compatibility * but still iterate over all results */ public function chunkQuery(array $ids, callable $callback): Generator { diff --git a/lib/Db/IPermissionMapper.php b/lib/Db/IPermissionMapper.php index 8794fc5a2..caab97473 100644 --- a/lib/Db/IPermissionMapper.php +++ b/lib/Db/IPermissionMapper.php @@ -8,22 +8,25 @@ namespace OCA\Deck\Db; +/** + * + */ interface IPermissionMapper { /** * Check if $userId is owner of Entity with $id * * @param $userId string userId - * @param $id int|string unique entity identifier + * @param $id int unique entity identifier * @return boolean */ - public function isOwner($userId, $id): bool; + public function isOwner(string $userId, int $id): bool; /** * Query boardId for Entity of given $id * - * @param $id int|string unique entity identifier - * @return int|null id of Board + * @param $id int unique entity identifier + * @return ?int id of Board */ - public function findBoardId($id): ?int; + public function findBoardId(int $id): ?int; } diff --git a/lib/Db/Label.php b/lib/Db/Label.php index f5d737dad..02d68d729 100644 --- a/lib/Db/Label.php +++ b/lib/Db/Label.php @@ -8,7 +8,16 @@ namespace OCA\Deck\Db; /** - * @method getTitle(): string + * @method string getTitle() + * @method void setTitle(string $title) + * @method string getColor() + * @method void setColor(string $color) + * @method int getBoardId() + * @method void setBoardId(int $boardId) + * @method int getCardId() + * @method void setCardId(int $cardId) + * @method int getLastModified() + * @method void setLastModified(int $lastModified) */ class Label extends RelationalEntity { protected $title; @@ -24,7 +33,7 @@ class Label extends RelationalEntity { $this->addType('lastModified', 'integer'); } - public function getETag() { + public function getETag(): string { return md5((string)$this->getLastModified()); } } diff --git a/lib/Db/LabelMapper.php b/lib/Db/LabelMapper.php index f0b32413c..01031acd2 100644 --- a/lib/Db/LabelMapper.php +++ b/lib/Db/LabelMapper.php @@ -20,13 +20,10 @@ class LabelMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $boardId - * @param int|null $limit - * @param int|null $offset * @return Label[] * @throws \OCP\DB\Exception */ - public function findAll($boardId, $limit = null, $offset = null): array { + public function findAll(int $boardId, ?int $limit = null, int $offset = 0): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->getTableName()) @@ -44,13 +41,10 @@ class LabelMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $cardId - * @param int|null $limit - * @param int|null $offset * @return Label[] * @throws \OCP\DB\Exception */ - public function findAssignedLabelsForCard($cardId, $limit = null, $offset = null): array { + public function findAssignedLabelsForCard(int $cardId, ?int $limit = null, int $offset = 0): array { $qb = $this->db->getQueryBuilder(); $qb->select('l.*', 'card_id') ->from($this->getTableName(), 'l') @@ -63,7 +57,7 @@ class LabelMapper extends DeckMapper implements IPermissionMapper { return $this->findEntities($qb); } - public function findAssignedLabelsForCards($cardIds, $limit = null, $offset = null): array { + public function findAssignedLabelsForCards(array $cardIds, ?int $limit = null, int $offset = 0): array { $qb = $this->db->getQueryBuilder(); $qb->select('l.*', 'card_id') ->from($this->getTableName(), 'l') @@ -77,13 +71,10 @@ class LabelMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $boardId - * @param int|null $limit - * @param int|null $offset * @return Label[] * @throws \OCP\DB\Exception */ - public function findAssignedLabelsForBoard($boardId, $limit = null, $offset = null): array { + public function findAssignedLabelsForBoard(int $boardId, ?int $limit = null, int $offset = 0): array { $qb = $this->db->getQueryBuilder(); $qb->select('l.id as id', 'l.title as title', 'l.color as color') ->selectAlias('c.id', 'card_id') @@ -113,11 +104,10 @@ class LabelMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $boardId - * @return array + * @return array> * @throws \OCP\DB\Exception */ - public function getAssignedLabelsForBoard($boardId) { + public function getAssignedLabelsForBoard(int $boardId): array { $labels = $this->findAssignedLabelsForBoard($boardId); $result = []; foreach ($labels as $label) { @@ -130,11 +120,9 @@ class LabelMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $labelId - * @return void * @throws \OCP\DB\Exception */ - public function deleteLabelAssignments($labelId) { + public function deleteLabelAssignments(int $labelId): void { $qb = $this->db->getQueryBuilder(); $qb->delete('deck_assigned_labels') ->where($qb->expr()->eq('label_id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT))); @@ -142,11 +130,9 @@ class LabelMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $cardId - * @return void * @throws \OCP\DB\Exception */ - public function deleteLabelAssignmentsForCard($cardId) { + public function deleteLabelAssignmentsForCard(int $cardId): void { $qb = $this->db->getQueryBuilder(); $qb->delete('deck_assigned_labels') ->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT))); @@ -154,33 +140,25 @@ class LabelMapper extends DeckMapper implements IPermissionMapper { } /** - * @param string $userId - * @param numeric $labelId - * @return bool * @throws \OCP\DB\Exception */ - public function isOwner($userId, $labelId): bool { + public function isOwner(string $userId, int $id): bool { $qb = $this->db->getQueryBuilder(); $qb->select('l.id') ->from($this->getTableName(), 'l') ->innerJoin('l', 'deck_boards', 'b', 'l.board_id = b.id') - ->where($qb->expr()->eq('l.id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT))) + ->where($qb->expr()->eq('l.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))) ->andWhere($qb->expr()->eq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR))); return count($qb->executeQuery()->fetchAll()) > 0; } - /** - * @param numeric $id - * @return int|null - */ - public function findBoardId($id): ?int { + public function findBoardId(int $id): ?int { try { $entity = $this->find($id); return $entity->getBoardId(); - } catch (DoesNotExistException $e) { - } catch (MultipleObjectsReturnedException $e) { + } catch (DoesNotExistException|MultipleObjectsReturnedException) { + return null; } - return null; } } diff --git a/lib/Db/Stack.php b/lib/Db/Stack.php index 42c21cedd..482e72d34 100644 --- a/lib/Db/Stack.php +++ b/lib/Db/Stack.php @@ -11,10 +11,16 @@ use Sabre\VObject\Component\VCalendar; /** * @method int getId() + * @method string getTitle() + * @method void setTitle(string $title) * @method int getBoardId() + * @method void setBoardId(int $boardId) * @method int getDeletedAt() + * @method void setDeletedAt(int $deletedAt) * @method int getLastModified() - * @method int getOrder() + * @method void setLastModified(int $lastModified) + * @method \int getOrder() + * @method void setOrder(int $order) * @method Card[] getCards() */ class Stack extends RelationalEntity { diff --git a/lib/Db/StackMapper.php b/lib/Db/StackMapper.php index e9e10647f..ad27680d1 100644 --- a/lib/Db/StackMapper.php +++ b/lib/Db/StackMapper.php @@ -35,13 +35,11 @@ class StackMapper extends DeckMapper implements IPermissionMapper { /** - * @param numeric $id - * @return Stack * @throws DoesNotExistException * @throws MultipleObjectsReturnedException * @throws \OCP\DB\Exception */ - public function find($id): Stack { + public function find(int $id): Stack { if (isset($this->stackCache[(string)$id])) { return $this->stackCache[(string)$id]; } @@ -56,11 +54,9 @@ class StackMapper extends DeckMapper implements IPermissionMapper { } /** - * @param $cardId - * @return Stack|null * @throws \OCP\DB\Exception */ - public function findStackFromCardId($cardId): ?Stack { + public function findStackFromCardId(int $cardId): ?Stack { $qb = $this->db->getQueryBuilder(); $qb->select('s.*') ->from($this->getTableName(), 's') @@ -76,11 +72,10 @@ class StackMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $boardId * @return Stack[] * @throws \OCP\DB\Exception */ - public function findAll($boardId, ?int $limit = null, ?int $offset = null): array { + public function findAll(int $boardId, ?int $limit = null, int $offset = 0): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->getTableName()) @@ -93,13 +88,9 @@ class StackMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $boardId - * @param int|null $limit - * @param int|null $offset - * @return Stack[] * @throws \OCP\DB\Exception */ - public function findDeleted($boardId, $limit = null, $offset = null) { + public function findDeleted(int $boardId, ?int $limit = null, int $offset = 0): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->getTableName()) @@ -125,12 +116,9 @@ class StackMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $userId - * @param numeric $stackId - * @return bool * @throws \OCP\DB\Exception */ - public function isOwner($userId, $id): bool { + public function isOwner(string $userId, int $id): bool { $qb = $this->db->getQueryBuilder(); $qb->select('s.id') ->from($this->getTableName(), 's') @@ -142,11 +130,9 @@ class StackMapper extends DeckMapper implements IPermissionMapper { } /** - * @param numeric $id - * @return int|null * @throws \OCP\DB\Exception */ - public function findBoardId($id): ?int { + public function findBoardId(int $id): ?int { $result = $this->cache->get('findBoardId:' . $id); if ($result !== null) { return $result !== false ? $result : null; @@ -163,6 +149,10 @@ class StackMapper extends DeckMapper implements IPermissionMapper { return $result !== false ? $result : null; } + /** + * @return array + * @throws \OCP\DB\Exception + */ public function findToDelete(): array { // add buffer of 5 min $timeLimit = time() - (60 * 5); diff --git a/lib/Listeners/BeforeTemplateRenderedListener.php b/lib/Listeners/BeforeTemplateRenderedListener.php index 9981b9f65..531d8c37f 100644 --- a/lib/Listeners/BeforeTemplateRenderedListener.php +++ b/lib/Listeners/BeforeTemplateRenderedListener.php @@ -35,6 +35,10 @@ class BeforeTemplateRenderedListener implements IEventListener { Util::addStyle('deck', 'deck'); $pathInfo = $this->request->getPathInfo(); + if (!$pathInfo) { + return; + } + if (str_starts_with($pathInfo, '/apps/calendar')) { Util::addScript('deck', 'deck-calendar'); } diff --git a/lib/Listeners/ResourceListener.php b/lib/Listeners/ResourceListener.php index ab3434c44..c1497c330 100644 --- a/lib/Listeners/ResourceListener.php +++ b/lib/Listeners/ResourceListener.php @@ -18,14 +18,10 @@ use OCP\EventDispatcher\IEventListener; /** @template-implements IEventListener */ class ResourceListener implements IEventListener { - /** @var IManager */ - private $resourceManager; - /** @var ResourceProviderCard */ - private $resourceProviderCard; - - public function __construct(IManager $resourceManager, ResourceProviderCard $resourceProviderCard) { - $this->resourceManager = $resourceManager; - $this->resourceProviderCard = $resourceProviderCard; + public function __construct( + private readonly IManager $resourceManager, + private readonly ResourceProviderCard $resourceProviderCard, + ) { } public function handle(Event $event): void { @@ -38,10 +34,10 @@ class ResourceListener implements IEventListener { $this->resourceManager->invalidateAccessCacheForProvider($this->resourceProviderCard); try { - $resource = $this->resourceManager->getResourceForUser(ResourceProvider::RESOURCE_TYPE, $boardId, null); + $resource = $this->resourceManager->getResourceForUser(ResourceProvider::RESOURCE_TYPE, (string)$boardId, null); $this->resourceManager->invalidateAccessCacheForResource($resource); } catch (ResourceException $e) { - // If there is no resource we don't need to invalidate anything, but this should not happen anyways + // If there is no resource we don't need to invalidate anything, but this should not happen anyway } } } diff --git a/lib/Model/BoardSummary.php b/lib/Model/BoardSummary.php index 5b84d9491..8d14f274e 100644 --- a/lib/Model/BoardSummary.php +++ b/lib/Model/BoardSummary.php @@ -11,6 +11,7 @@ use OCA\Deck\Db\Board; class BoardSummary extends Board { private Board $board; + /** @psalm-suppress ConstructorSignatureMismatch */ public function __construct(Board $board) { parent::__construct(); $this->board = $board; @@ -27,7 +28,7 @@ class BoardSummary extends Board { return $this->board->getter($name); } - public function __call($name, $arguments) { - return $this->board->__call($name, $arguments); + public function __call($methodName, $args) { + return $this->board->__call($methodName, $args); } } diff --git a/lib/Model/CardDetails.php b/lib/Model/CardDetails.php index 39e8cf59b..d0eb842f4 100644 --- a/lib/Model/CardDetails.php +++ b/lib/Model/CardDetails.php @@ -15,6 +15,7 @@ class CardDetails extends Card { private ?Board $board; private ?Reference $referenceData = null; + /** @psalm-suppress ConstructorSignatureMismatch */ public function __construct(Card $card, ?Board $board = null) { parent::__construct(); $this->card = $card; diff --git a/lib/Notification/NotificationHelper.php b/lib/Notification/NotificationHelper.php index bba2dcf30..275a3f57f 100644 --- a/lib/Notification/NotificationHelper.php +++ b/lib/Notification/NotificationHelper.php @@ -95,7 +95,7 @@ class NotificationHelper { $shouldNotify = $notificationSetting === ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ALL; - if ($user->getUID() === $board->getOwner() && count($board->getAcl()) === 0) { + if ($user->getUID() === $board->getOwner() && count($board->getAcl() ?? []) === 0) { // Notify if all or assigned is configured for unshared boards $shouldNotify = true; } elseif ($notificationSetting === ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED && $this->assignmentMapper->isUserAssigned($card->getId(), $user->getUID())) { diff --git a/lib/Service/AssignmentService.php b/lib/Service/AssignmentService.php index e6dc0b9c2..97d1be700 100644 --- a/lib/Service/AssignmentService.php +++ b/lib/Service/AssignmentService.php @@ -21,7 +21,6 @@ use OCA\Deck\NotFoundException; use OCA\Deck\Notification\NotificationHelper; use OCA\Deck\Validators\AssignmentServiceValidator; use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\EventDispatcher\IEventDispatcher; @@ -92,15 +91,12 @@ class AssignmentService { } /** - * @param $cardId - * @param $userId - * @return bool|null|Entity * @throws BadRequestException * @throws NoPermissionException * @throws MultipleObjectsReturnedException * @throws DoesNotExistException */ - public function assignUser($cardId, $userId, int $type = Assignment::TYPE_USER) { + public function assignUser(int $cardId, string $userId, int $type = Assignment::TYPE_USER): Assignment { $this->assignmentServiceValidator->check(compact('cardId', 'userId')); if ($type !== Assignment::TYPE_USER && $type !== Assignment::TYPE_GROUP) { @@ -144,16 +140,13 @@ class AssignmentService { } /** - * @param $cardId - * @param $userId - * @return Entity * @throws BadRequestException * @throws NotFoundException * @throws NoPermissionException * @throws DoesNotExistException * @throws MultipleObjectsReturnedException */ - public function unassignUser($cardId, $userId, $type = 0) { + public function unassignUser(int $cardId, string $userId, int $type = 0): Assignment { $this->assignmentServiceValidator->check(compact('cardId', 'userId')); $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT); diff --git a/lib/Service/AttachmentService.php b/lib/Service/AttachmentService.php index e284ed666..a987ddb3a 100644 --- a/lib/Service/AttachmentService.php +++ b/lib/Service/AttachmentService.php @@ -25,6 +25,7 @@ use OCP\AppFramework\Db\IMapperException; use OCP\AppFramework\Http\Response; use OCP\IL10N; use OCP\IUserManager; +use Psr\Container\ContainerExceptionInterface; class AttachmentService { private $attachmentMapper; @@ -80,20 +81,16 @@ class AttachmentService { } /** - * @param string $type - * @param string $class - * @throws \OCP\AppFramework\QueryException + * @throws ContainerExceptionInterface */ - public function registerAttachmentService($type, $class) { - $this->services[$type] = $this->application->getContainer()->query($class); + public function registerAttachmentService(string $type, string $class): void { + $this->services[$type] = $this->application->getContainer()->get($class); } /** - * @param string $type - * @return IAttachmentService * @throws InvalidAttachmentType */ - public function getService($type) { + public function getService(string $type): IAttachmentService { if (isset($this->services[$type])) { return $this->services[$type]; } @@ -101,16 +98,11 @@ class AttachmentService { } /** - * @param $cardId - * @return array + * @return Attachment[] * @throws \OCA\Deck\NoPermissionException * @throws BadRequestException */ - public function findAll($cardId, $withDeleted = false) { - if (is_numeric($cardId) === false) { - throw new BadRequestException('card id must be a number'); - } - + public function findAll(int $cardId, bool $withDeleted = false): array { $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ); $attachments = $this->attachmentMapper->findAll($cardId); @@ -122,7 +114,7 @@ class AttachmentService { /** @var IAttachmentService $service */ $service = $this->getService($attachmentType); if ($service instanceof ICustomAttachmentService) { - $attachments = array_merge($attachments, $service->listAttachments((int)$cardId)); + $attachments = array_merge($attachments, $service->listAttachments($cardId)); } } @@ -140,49 +132,40 @@ class AttachmentService { } /** - * @param $cardId - * @return int|mixed * @throws BadRequestException * @throws InvalidAttachmentType * @throws \OCP\DB\Exception */ - public function count($cardId) { - if (is_numeric($cardId) === false) { - throw new BadRequestException('card id must be a number'); - } - - $count = $this->attachmentCacheHelper->getAttachmentCount((int)$cardId); + public function count(int $cardId): int { + $count = $this->attachmentCacheHelper->getAttachmentCount($cardId); if ($count === null) { $count = count($this->attachmentMapper->findAll($cardId)); foreach (array_keys($this->services) as $attachmentType) { $service = $this->getService($attachmentType); if ($service instanceof ICustomAttachmentService) { - $count += $service->getAttachmentCount((int)$cardId); + $count += $service->getAttachmentCount($cardId); } } - $this->attachmentCacheHelper->setAttachmentCount((int)$cardId, $count); + $this->attachmentCacheHelper->setAttachmentCount($cardId, $count); } return $count; } /** - * @param $cardId - * @param $type - * @param $data * @return Attachment|\OCP\AppFramework\Db\Entity * @throws NoPermissionException * @throws StatusException * @throws BadRequestException */ - public function create($cardId, $type, $data) { + public function create(int $cardId, string $type, string $data) { $this->attachmentServiceValidator->check(compact('cardId', 'type')); $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT); - $this->attachmentCacheHelper->clearAttachmentCount((int)$cardId); + $this->attachmentCacheHelper->clearAttachmentCount($cardId); $attachment = new Attachment(); $attachment->setCardId($cardId); $attachment->setType($type); @@ -218,12 +201,10 @@ class AttachmentService { /** * Display the attachment * - * @param $attachmentId - * @return Response * @throws NoPermissionException * @throws NotFoundException */ - public function display($cardId, $attachmentId, $type = 'deck_file') { + public function display(int $cardId, int $attachmentId, string $type = 'deck_file'): Response { try { $service = $this->getService($type); } catch (InvalidAttachmentType $e) { @@ -257,13 +238,10 @@ class AttachmentService { /** * Update an attachment with custom data * - * @param $attachmentId - * @param $data - * @return mixed * @throws BadRequestException * @throws NoPermissionException */ - public function update($cardId, $attachmentId, $data, $type = 'deck_file') { + public function update(int $cardId, int $attachmentId, string $data, string $type = 'deck_file'): Attachment { $this->attachmentServiceValidator->check(compact('cardId', 'type', 'data')); try { @@ -384,8 +362,6 @@ class AttachmentService { } /** - * @param Attachment $attachment - * @return Attachment * @throws \ReflectionException */ private function addCreator(Attachment $attachment): Attachment { diff --git a/lib/Service/BoardService.php b/lib/Service/BoardService.php index b88111add..be3092016 100644 --- a/lib/Service/BoardService.php +++ b/lib/Service/BoardService.php @@ -75,8 +75,6 @@ class BoardService { /** * Set a different user than the current one, e.g. when no user is available in occ - * - * @param string $userId */ public function setUserId(string $userId): void { $this->userId = $userId; @@ -117,22 +115,18 @@ class BoardService { } $this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ); - /** @var Board $board */ $board = $this->boardMapper->find($boardId, true, true, $allowDeleted); [$board] = $this->enrichBoards([$board], $fullDetails); return $board; } /** - * @param $mapper - * @param $id - * @return bool * @throws DoesNotExistException * @throws NoPermissionException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function isArchived($mapper, $id) { + public function isArchived($mapper, int $id): bool { $this->boardServiceValidator->check(compact('id')); try { @@ -151,15 +145,12 @@ class BoardService { } /** - * @param $mapper - * @param $id - * @return bool * @throws DoesNotExistException * @throws NoPermissionException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function isDeleted($mapper, $id) { + public function isDeleted($mapper, int $id): bool { $this->boardServiceValidator->check(compact('mapper', 'id')); try { @@ -179,13 +170,9 @@ class BoardService { /** - * @param $title - * @param $userId - * @param $color - * @return \OCP\AppFramework\Db\Entity * @throws BadRequestException */ - public function create($title, $userId, $color) { + public function create(string $title, string $userId, string $color): Board { $this->boardServiceValidator->check(compact('title', 'userId', 'color')); if (!$this->permissionService->canCreate()) { @@ -196,7 +183,8 @@ class BoardService { $board->setTitle($title); $board->setOwner($userId); $board->setColor($color); - $new_board = $this->boardMapper->insert($board); + /** @var Board $board */ + $board = $this->boardMapper->insert($board); // create new labels $default_labels = [ @@ -210,33 +198,31 @@ class BoardService { $label = new Label(); $label->setColor($labelColor); $label->setTitle($labelTitle); - $label->setBoardId($new_board->getId()); + $label->setBoardId($board->getId()); $labels[] = $this->labelMapper->insert($label); } - $new_board->setLabels($labels); - $this->boardMapper->mapOwner($new_board); - $permissions = $this->permissionService->matchPermissions($new_board); - $new_board->setPermissions([ + $board->setLabels($labels); + $this->boardMapper->mapOwner($board); + $permissions = $this->permissionService->matchPermissions($board); + $board->setPermissions([ 'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ] ?? false, 'PERMISSION_EDIT' => $permissions[Acl::PERMISSION_EDIT] ?? false, 'PERMISSION_MANAGE' => $permissions[Acl::PERMISSION_MANAGE] ?? false, 'PERMISSION_SHARE' => $permissions[Acl::PERMISSION_SHARE] ?? false ]); - $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $new_board, ActivityManager::SUBJECT_BOARD_CREATE, [], $userId); - $this->changeHelper->boardChanged($new_board->getId()); + $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $board, ActivityManager::SUBJECT_BOARD_CREATE, [], $userId); + $this->changeHelper->boardChanged($board->getId()); - return $new_board; + return $board; } /** - * @param $id - * @return Board * @throws DoesNotExistException * @throws NoPermissionException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function delete($id) { + public function delete(int $id): Board { $this->boardServiceValidator->check(compact('id')); $this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE); @@ -253,13 +239,11 @@ class BoardService { } /** - * @param $id - * @return \OCP\AppFramework\Db\Entity * @throws DoesNotExistException * @throws NoPermissionException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException */ - public function deleteUndo($id) { + public function deleteUndo(int $id): Board { $this->boardServiceValidator->check(compact('id')); $this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE); @@ -273,14 +257,12 @@ class BoardService { } /** - * @param $id - * @return \OCP\AppFramework\Db\Entity * @throws DoesNotExistException * @throws NoPermissionException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function deleteForce($id) { + public function deleteForce(int $id): Board { $this->boardServiceValidator->check(compact('id')); $this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE); @@ -291,17 +273,12 @@ class BoardService { } /** - * @param $id - * @param $title - * @param $color - * @param $archived - * @return \OCP\AppFramework\Db\Entity * @throws DoesNotExistException * @throws NoPermissionException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function update($id, $title, $color, $archived) { + public function update(int $id, string $title, string $color, bool $archived): Board { $this->boardServiceValidator->check(compact('id', 'title', 'color', 'archived')); $this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE); @@ -320,7 +297,7 @@ class BoardService { return $board; } - private function applyPermissions($boardId, $edit, $share, $manage, $oldAcl = null) { + private function applyPermissions(int $boardId, bool $edit, bool $share, bool $manage, ?Acl $oldAcl = null): array { try { $this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_MANAGE); } catch (NoPermissionException $e) { @@ -332,7 +309,7 @@ class BoardService { return [$edit, $share, $manage]; } - public function enrichWithBoardSettings(Board $board) { + public function enrichWithBoardSettings(Board $board): void { $globalCalendarConfig = (bool)$this->config->getUserValue($this->userId, Application::APP_ID, 'calendar', true); $settings = [ 'notify-due' => $this->config->getUserValue($this->userId, Application::APP_ID, 'board:' . $board->getId() . ':notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED), @@ -341,7 +318,7 @@ class BoardService { $board->setSettings($settings); } - public function enrichWithActiveSessions(Board $board) { + public function enrichWithActiveSessions(Board $board): void { $sessions = $this->sessionMapper->findAllActive($board->getId()); $board->setActiveSessions(array_values( @@ -354,17 +331,11 @@ class BoardService { } /** - * @param $boardId - * @param $type - * @param $participant - * @param $edit - * @param $share - * @param $manage - * @return \OCP\AppFramework\Db\Entity + * @param Acl::PERMISSION_TYPE_* $type * @throws BadRequestException * @throws NoPermissionException */ - public function addAcl($boardId, $type, $participant, $edit, $share, $manage) { + public function addAcl(int $boardId, int $type, $participant, bool $edit, bool $share, bool $manage): Acl { $this->boardServiceValidator->check(compact('boardId', 'type', 'participant', 'edit', 'share', 'manage')); $this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_SHARE); @@ -400,17 +371,12 @@ class BoardService { } /** - * @param $id - * @param $edit - * @param $share - * @param $manage - * @return \OCP\AppFramework\Db\Entity * @throws DoesNotExistException * @throws NoPermissionException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function updateAcl($id, $edit, $share, $manage) { + public function updateAcl(int $id, bool $edit, bool $share, bool $manage): Acl { $this->boardServiceValidator->check(compact('id', 'edit', 'share', 'manage')); $this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE); @@ -422,12 +388,12 @@ class BoardService { $acl->setPermissionShare($share); $acl->setPermissionManage($manage); $this->boardMapper->mapAcl($acl); - $board = $this->aclMapper->update($acl); + $acl = $this->aclMapper->update($acl); $this->changeHelper->boardChanged($acl->getBoardId()); $this->eventDispatcher->dispatchTyped(new AclUpdatedEvent($acl)); - return $board; + return $acl; } /** @@ -579,27 +545,23 @@ class BoardService { } /** - * @param $id - * @return Board * @throws DoesNotExistException * @throws NoPermissionException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException - * @throws BadRequestException */ - public function export($id) : Board { - if (is_numeric($id) === false) { - throw new BadRequestException('board id must be a number'); - } - + public function export(int $id): Board { $this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_READ); - $board = $this->boardMapper->find((int)$id); + $board = $this->boardMapper->find($id); $this->enrichWithCards($board); $this->enrichWithLabels($board); return $board; } - /** @param Board[] $boards */ + /** + * @param Board[] $boards + * @return Board[] + */ private function enrichBoards(array $boards, bool $fullDetails = true): array { $result = []; foreach ($boards as $board) { @@ -715,8 +677,8 @@ class BoardService { } } - private function enrichWithStacks($board, $since = -1) { - $stacks = $this->stackMapper->findAll($board->getId(), null, null, $since); + private function enrichWithStacks(Board $board): void { + $stacks = $this->stackMapper->findAll($board->getId()); if (\count($stacks) === 0) { return; @@ -725,8 +687,8 @@ class BoardService { $board->setStacks($stacks); } - private function enrichWithLabels($board, $since = -1) { - $labels = $this->labelMapper->findAll($board->getId(), null, null, $since); + private function enrichWithLabels(Board $board): void { + $labels = $this->labelMapper->findAll($board->getId()); if (\count($labels) === 0) { return; @@ -735,7 +697,7 @@ class BoardService { $board->setLabels($labels); } - private function enrichWithUsers($board, $since = -1) { + private function enrichWithUsers(Board $board): void { $boardUsers = $this->permissionService->findUsers($board->getId()); if ($boardUsers === null || \count($boardUsers) === 0) { return; @@ -746,7 +708,7 @@ class BoardService { /** * Clean a given board data from the Cache */ - private function clearBoardFromCache(Board $board) { + private function clearBoardFromCache(Board $board): void { $boardId = $board->getId(); $boardOwnerId = $board->getOwner(); @@ -755,7 +717,7 @@ class BoardService { unset($this->boardsCachePartial[$boardId]); } - private function enrichWithCards($board) { + private function enrichWithCards(Board $board): void { $stacks = $this->stackMapper->findAll($board->getId()); foreach ($stacks as $stack) { $cards = $this->cardMapper->findAllByStack($stack->getId()); diff --git a/lib/Service/CardService.php b/lib/Service/CardService.php index 1c84771a8..f5995874f 100644 --- a/lib/Service/CardService.php +++ b/lib/Service/CardService.php @@ -64,10 +64,14 @@ class CardService { ) { } - public function enrichCards($cards) { + /** + * @param Card[] $cards + * @return CardDetails[] + */ + public function enrichCards(array $cards): array { $user = $this->userManager->get($this->userId); - $cardIds = array_map(function (Card $card) use ($user) { + $cardIds = array_map(function (Card $card) use ($user): int { // Everything done in here might be heavy as it is executed for every card $cardId = $card->getId(); $this->cardMapper->mapOwner($card); @@ -120,7 +124,8 @@ class CardService { ); } - public function fetchDeleted($boardId) { + /** @return Card[] */ + public function fetchDeleted($boardId): array { $this->cardServiceValidator->check(compact('boardId')); $this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ); $cards = $this->cardMapper->findDeleted($boardId); @@ -129,13 +134,12 @@ class CardService { } /** - * @return \OCA\Deck\Db\RelationalEntity * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function find(int $cardId) { + public function find(int $cardId): Card { $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ); $card = $this->cardMapper->find($cardId); [$card] = $this->enrichCards([$card]); @@ -152,7 +156,10 @@ class CardService { return $card; } - public function findCalendarEntries($boardId) { + /** + * @return Card[] + */ + public function findCalendarEntries(int $boardId): array { try { $this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ); } catch (NoPermissionException $e) { @@ -163,20 +170,13 @@ class CardService { } /** - * @param $title - * @param $stackId - * @param $type - * @param integer $order - * @param $description - * @param $owner - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadrequestException */ - public function create($title, $stackId, $type, $order, $owner, $description = '', $duedate = null) { + public function create(string $title, int $stackId, string $type, int $order, string $owner, string $description = '', $duedate = null): Card { $this->cardServiceValidator->check(compact('title', 'stackId', 'type', 'order', 'owner')); $this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_EDIT); @@ -203,19 +203,13 @@ class CardService { } /** - * @param $id - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function delete($id) { - if (is_numeric($id) === false) { - throw new BadRequestException('card id must be a number'); - } - + public function delete(int $id): Card { $this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT); if ($this->boardService->isArchived($this->cardMapper, $id)) { throw new StatusException('Operation not allowed. This board is archived.'); @@ -233,25 +227,13 @@ class CardService { } /** - * @param $id - * @param $title - * @param $stackId - * @param $type - * @param $owner - * @param $description - * @param $order - * @param $duedate - * @param $deletedAt - * @param $archived - * @param $done - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function update($id, $title, $stackId, $type, $owner, $description = '', $order = 0, $duedate = null, $deletedAt = null, $archived = null, ?OptionalNullableValue $done = null) { + public function update(int $id, string $title, int $stackId, string $type, string $owner, string $description = '', int $order = 0, ?string $duedate = null, ?int $deletedAt = null, ?bool $archived = null, ?OptionalNullableValue $done = null): Card { $this->cardServiceValidator->check(compact('id', 'title', 'stackId', 'type', 'owner', 'order')); $this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT, allowDeletedCard: true); @@ -398,16 +380,13 @@ class CardService { } /** - * @param $id - * @param $title - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function rename($id, $title) { + public function rename(int $id, string $title): Card { $this->cardServiceValidator->check(compact('id', 'title')); $this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT); @@ -429,17 +408,14 @@ class CardService { } /** - * @param $id - * @param $stackId - * @param $order - * @return array + * @return list * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function reorder(int $id, int $stackId, int $order) { + public function reorder(int $id, int $stackId, int $order): array { $this->cardServiceValidator->check(compact('id', 'stackId', 'order')); $this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT); @@ -488,15 +464,13 @@ class CardService { } /** - * @param $id - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function archive($id) { + public function archive(int $id): Card { $this->cardServiceValidator->check(compact('id')); $this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT); @@ -517,15 +491,13 @@ class CardService { } /** - * @param $id - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function unarchive($id) { + public function unarchive(int $id): Card { $this->cardServiceValidator->check(compact('id')); @@ -546,8 +518,6 @@ class CardService { } /** - * @param $id - * @return \OCA\Deck\Db\Card * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException @@ -599,15 +569,13 @@ class CardService { } /** - * @param $cardId - * @param $labelId * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function assignLabel($cardId, $labelId) { + public function assignLabel(int $cardId, int $labelId): Card { $this->cardServiceValidator->check(compact('cardId', 'labelId')); $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT); @@ -629,18 +597,16 @@ class CardService { $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_ASSIGN, ['label' => $label]); $this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card)); + return $card; } /** - * @param $cardId - * @param $labelId - * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function removeLabel($cardId, $labelId) { + public function removeLabel(int $cardId, int $labelId): Card { $this->cardServiceValidator->check(compact('cardId', 'labelId')); @@ -660,6 +626,7 @@ class CardService { $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_UNASSING, ['label' => $label]); $this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card)); + return $card; } public function getCardUrl(int $cardId): string { diff --git a/lib/Service/CirclesService.php b/lib/Service/CirclesService.php index fffcf0eb0..e604a7869 100644 --- a/lib/Service/CirclesService.php +++ b/lib/Service/CirclesService.php @@ -66,7 +66,7 @@ class CirclesService { $circlesManager->startSession($federatedUser); $circle = $circlesManager->getCircle($circleId); $member = $circle->getInitiator(); - $isUserInCircle = $member !== null && $member->getLevel() >= Member::LEVEL_MEMBER; + $isUserInCircle = $member->getLevel() >= Member::LEVEL_MEMBER; if (!isset($this->userCircleCache[$circleId])) { $this->userCircleCache[$circleId] = []; diff --git a/lib/Service/CommentService.php b/lib/Service/CommentService.php index dee7ae1da..72a48c66c 100644 --- a/lib/Service/CommentService.php +++ b/lib/Service/CommentService.php @@ -22,8 +22,6 @@ use OCP\IUserManager; use OutOfBoundsException; use Psr\Log\LoggerInterface; -use function is_numeric; - class CommentService { public function __construct( @@ -36,12 +34,9 @@ class CommentService { ) { } - public function list(string $cardId, int $limit = 20, int $offset = 0): DataResponse { - if (!is_numeric($cardId)) { - throw new BadRequestException('A valid card id must be provided'); - } + public function list(int $cardId, int $limit = 20, int $offset = 0): DataResponse { $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ); - $comments = $this->commentsManager->getForObject(Application::COMMENT_ENTITY_TYPE, $cardId, $limit, $offset); + $comments = $this->commentsManager->getForObject(Application::COMMENT_ENTITY_TYPE, (string)$cardId, $limit, $offset); $result = []; foreach ($comments as $comment) { $formattedComment = $this->formatComment($comment); @@ -96,13 +91,13 @@ class CommentService { * @throws BadRequestException * @throws NotFoundException|NoPermissionException */ - public function create(int $cardId, string $message, string $replyTo = '0'): DataResponse { + public function create(int $cardId, string $message, int $replyTo = 0): DataResponse { $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ); // Check if parent is a comment on the same card - if ($replyTo !== '0') { + if ($replyTo !== 0) { try { - $comment = $this->commentsManager->get($replyTo); + $comment = $this->commentsManager->get((string)$replyTo); if ($comment->getObjectType() !== Application::COMMENT_ENTITY_TYPE || (int)$comment->getObjectId() !== $cardId) { throw new CommentNotFoundException(); } @@ -115,7 +110,7 @@ class CommentService { $comment = $this->commentsManager->create('users', $this->userId, Application::COMMENT_ENTITY_TYPE, (string)$cardId); $comment->setMessage($message); $comment->setVerb('comment'); - $comment->setParentId($replyTo); + $comment->setParentId((string)$replyTo); $this->commentsManager->save($comment); return new DataResponse($this->formatComment($comment, true)); } catch (\InvalidArgumentException $e) { @@ -128,14 +123,8 @@ class CommentService { } } - public function update(string $cardId, string $commentId, string $message): DataResponse { - if (!is_numeric($cardId)) { - throw new BadRequestException('A valid card id must be provided'); - } - if (!is_numeric($commentId)) { - throw new BadRequestException('A valid comment id must be provided'); - } - $comment = $this->get((int)$cardId, (int)$commentId); + public function update(int $cardId, int $commentId, string $message): DataResponse { + $comment = $this->get($cardId, $commentId); if ($comment->getActorType() !== 'users' || $comment->getActorId() !== $this->userId) { throw new NoPermissionException('Only authors are allowed to edit their comment.'); } @@ -145,18 +134,12 @@ class CommentService { return new DataResponse($this->formatComment($comment)); } - public function delete(string $cardId, string $commentId): DataResponse { - if (!is_numeric($cardId)) { - throw new BadRequestException('A valid card id must be provided'); - } - if (!is_numeric($commentId)) { - throw new BadRequestException('A valid comment id must be provided'); - } + public function delete(int $cardId, int $commentId): DataResponse { $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ); try { - $comment = $this->commentsManager->get($commentId); - if ($comment->getObjectType() !== Application::COMMENT_ENTITY_TYPE || $comment->getObjectId() !== $cardId) { + $comment = $this->commentsManager->get((string)$commentId); + if ($comment->getObjectType() !== Application::COMMENT_ENTITY_TYPE || (int)$comment->getObjectId() !== $cardId) { throw new CommentNotFoundException(); } } catch (CommentNotFoundException $e) { @@ -165,11 +148,11 @@ class CommentService { if ($comment->getActorType() !== 'users' || $comment->getActorId() !== $this->userId) { throw new NoPermissionException('Only authors are allowed to edit their comment.'); } - $this->commentsManager->delete($commentId); + $this->commentsManager->delete((string)$commentId); return new DataResponse([]); } - private function formatComment(IComment $comment, $addReplyTo = false): array { + private function formatComment(IComment $comment, bool $addReplyTo = false): array { $actorDisplayName = $this->userManager->getDisplayName($comment->getActorId()) ?? $comment->getActorId(); $formattedComment = [ diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php index 0ff30f678..7b8b031cc 100644 --- a/lib/Service/ConfigService.php +++ b/lib/Service/ConfigService.php @@ -48,7 +48,8 @@ class ConfigService { } public function getAll(): array { - if ($this->getUserId() === null) { + $userId = $this->getUserId(); + if ($userId === null) { return []; } @@ -57,7 +58,7 @@ class ConfigService { 'cardDetailsInModal' => $this->isCardDetailsInModal(), 'cardIdBadge' => $this->isCardIdBadgeEnabled() ]; - if ($this->groupManager->isAdmin($this->getUserId())) { + if ($this->groupManager->isAdmin($userId)) { $data['groupLimit'] = $this->get('groupLimit'); } return $data; @@ -95,44 +96,48 @@ class ConfigService { } public function isCalendarEnabled(?int $boardId = null): bool { - if ($this->getUserId() === null) { + $userId = $this->getUserId(); + if ($userId === null) { return false; } $appConfigState = $this->config->getAppValue(Application::APP_ID, 'calendar', 'yes') === 'yes'; - $defaultState = (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'calendar', $appConfigState); + $defaultState = (bool)$this->config->getUserValue($userId, Application::APP_ID, 'calendar', $appConfigState); if ($boardId === null) { return $defaultState; } - return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'board:' . $boardId . ':calendar', $defaultState); + return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'board:' . $boardId . ':calendar', $defaultState); } public function isCardDetailsInModal(?int $boardId = null): bool { - if ($this->getUserId() === null) { + $userId = $this->getUserId(); + if ($userId === null) { return false; } - $defaultState = (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardDetailsInModal', true); + $defaultState = (bool)$this->config->getUserValue($userId, Application::APP_ID, 'cardDetailsInModal', true); if ($boardId === null) { return $defaultState; } - return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'board:' . $boardId . ':cardDetailsInModal', $defaultState); + return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'board:' . $boardId . ':cardDetailsInModal', $defaultState); } public function isCardIdBadgeEnabled(): bool { - if ($this->getUserId() === null) { + $userId = $this->getUserId(); + if ($userId === null) { return false; } $appConfigState = $this->config->getAppValue(Application::APP_ID, 'cardIdBadge', 'yes') === 'no'; - $defaultState = (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', $appConfigState); + $defaultState = (bool)$this->config->getUserValue($userId, Application::APP_ID, 'cardIdBadge', $appConfigState); - return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', $defaultState); + return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'cardIdBadge', $defaultState); } public function set($key, $value) { - if ($this->getUserId() === null) { + $userId = $this->getUserId(); + if ($userId === null) { throw new NoPermissionException('Must be logged in to set user config'); } @@ -140,21 +145,21 @@ class ConfigService { [$scope] = explode(':', $key, 2); switch ($scope) { case 'groupLimit': - if (!$this->groupManager->isAdmin($this->getUserId())) { + if (!$this->groupManager->isAdmin($userId)) { throw new NoPermissionException('You must be admin to set the group limit'); } $result = $this->setGroupLimit($value); break; case 'calendar': - $this->config->setUserValue($this->getUserId(), Application::APP_ID, 'calendar', (string)$value); + $this->config->setUserValue($userId, Application::APP_ID, 'calendar', (string)$value); $result = $value; break; case 'cardDetailsInModal': - $this->config->setUserValue($this->getUserId(), Application::APP_ID, 'cardDetailsInModal', (string)$value); + $this->config->setUserValue($userId, Application::APP_ID, 'cardDetailsInModal', (string)$value); $result = $value; break; case 'cardIdBadge': - $this->config->setUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', (string)$value); + $this->config->setUserValue($userId, Application::APP_ID, 'cardIdBadge', (string)$value); $result = $value; break; case 'board': @@ -162,7 +167,7 @@ class ConfigService { if ($boardConfigKey === 'notify-due' && !in_array($value, [self::SETTING_BOARD_NOTIFICATION_DUE_ALL, self::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED, self::SETTING_BOARD_NOTIFICATION_DUE_OFF], true)) { throw new BadRequestException('Board notification option must be one of: off, assigned, all'); } - $this->config->setUserValue($this->getUserId(), Application::APP_ID, $key, (string)$value); + $this->config->setUserValue($userId, Application::APP_ID, $key, (string)$value); $result = $value; } return $result; diff --git a/lib/Service/FullTextSearchService.php b/lib/Service/FullTextSearchService.php index 66e4bdb5f..2f1c6c052 100644 --- a/lib/Service/FullTextSearchService.php +++ b/lib/Service/FullTextSearchService.php @@ -124,18 +124,15 @@ class FullTextSearchService { /** - * @param int $cardId - * - * @return Board * @throws DoesNotExistException * @throws MultipleObjectsReturnedException */ public function getBoardFromCardId(int $cardId): Board { - $boardId = (int)$this->cardMapper->findBoardId($cardId); - /** @var Board $board */ - $board = $this->boardMapper->find($boardId); - - return $board; + $boardId = $this->cardMapper->findBoardId($cardId); + if ($boardId === null) { + throw new DoesNotExistException("Board '$cardId' does not exist"); + } + return $this->boardMapper->find($boardId); } @@ -145,7 +142,7 @@ class FullTextSearchService { * @return Card[] */ private function getCardsFromStack(int $stackId): array { - return $this->cardMapper->findAll($stackId, null, null); + return $this->cardMapper->findAll($stackId); } @@ -155,7 +152,7 @@ class FullTextSearchService { * @return Stack[] */ private function getStacksFromBoard(int $boardId): array { - return $this->stackMapper->findAll($boardId, null, null); + return $this->stackMapper->findAll($boardId); } @@ -165,6 +162,6 @@ class FullTextSearchService { * @return Board[] */ private function getBoardsFromUser(string $userId): array { - return $this->boardMapper->findAllByUser($userId, null, null, null); + return $this->boardMapper->findAllByUser($userId); } } diff --git a/lib/Service/Importer/ABoardImportService.php b/lib/Service/Importer/ABoardImportService.php index e32109d3a..7726f707f 100644 --- a/lib/Service/Importer/ABoardImportService.php +++ b/lib/Service/Importer/ABoardImportService.php @@ -19,29 +19,25 @@ use OCP\Comments\IComment; abstract class ABoardImportService { /** @var string */ public static $name = ''; - /** @var BoardImportService */ - private $boardImportService; - /** @var bool */ - protected $needValidateData = true; + private BoardImportService $boardImportService; + protected bool $needValidateData = true; /** @var Stack[] */ - protected $stacks = []; + protected array $stacks = []; /** @var Label[] */ - protected $labels = []; + protected array $labels = []; /** @var Card[] */ - protected $cards = []; + protected array $cards = []; /** @var Acl[] */ - protected $acls = []; + protected array $acls = []; /** @var IComment[][] */ - protected $comments = []; + protected array $comments = []; /** @var Assignment[] */ - protected $assignments = []; - /** @var string[][] */ - protected $labelCardAssignments = []; + protected array $assignments = []; + /** @var int[][] */ + protected array $labelCardAssignments = []; /** * Configure import service - * - * @return void */ abstract public function bootstrap(): void; @@ -68,10 +64,13 @@ abstract class ABoardImportService { abstract public function getCardAssignments(): array; + /** + * @return array> + */ abstract public function getCardLabelAssignment(): array; /** - * @return IComment[][]|array + * @return array> */ abstract public function getComments(): array; @@ -98,16 +97,16 @@ abstract class ABoardImportService { $this->acls[$code] = $acl; } - public function updateComment(string $cardId, string $commentId, IComment $comment): void { + public function updateComment(int $cardId, string $commentId, IComment $comment): void { $this->comments[$cardId][$commentId] = $comment; } - public function updateCardAssignment(string $cardId, string $assignmentId, Entity $assignment): void { + public function updateCardAssignment(int $cardId, int $assignmentId, Entity $assignment): void { $this->assignments[$cardId][$assignmentId] = $assignment; } - public function updateCardLabelsAssignment(string $cardId, string $assignmentId, string $assignment): void { - $this->labelCardAssignments[$cardId][$assignmentId] = $assignment; + public function updateCardLabelsAssignment(int $cardId, int $assignmentId, int $labelId): void { + $this->labelCardAssignments[$cardId][$assignmentId] = $labelId; } public function setImportService(BoardImportService $service): void { diff --git a/lib/Service/Importer/BoardImportService.php b/lib/Service/Importer/BoardImportService.php index 4863954aa..d8a982c5f 100644 --- a/lib/Service/Importer/BoardImportService.php +++ b/lib/Service/Importer/BoardImportService.php @@ -209,14 +209,15 @@ class BoardImportService { public function importBoard(): void { $board = $this->getImportSystem()->getBoard(); + if ($board === null) { + throw new \LogicException('Import board not found'); + } if (!$this->userManager->userExists($board->getOwner())) { throw new \Exception('Target owner ' . $board->getOwner() . ' not found. Please provide a mapping through the import config.'); } - if ($board) { - $this->boardMapper->insert($board); - $this->board = $board; - } + $this->boardMapper->insert($board); + $this->board = $board; } public function getBoard(bool $reset = false): Board { @@ -292,12 +293,7 @@ class BoardImportService { } } - /** - * @param mixed $cardId - * @param mixed $labelId - * @return self - */ - public function assignCardToLabel($cardId, $labelId): self { + public function assignCardToLabel(int $cardId, int $labelId): self { $this->cardMapper->assignLabel( $cardId, $labelId @@ -307,14 +303,14 @@ class BoardImportService { public function assignCardsToLabels(): void { $data = $this->getImportSystem()->getCardLabelAssignment(); - foreach ($data as $cardId => $assignemnt) { - foreach ($assignemnt as $assignmentId => $labelId) { + foreach ($data as $cardId => $assignment) { + foreach ($assignment as $assignmentId => $labelId) { try { $this->assignCardToLabel( - $cardId, + (int)$cardId, $labelId ); - $this->getImportSystem()->updateCardLabelsAssignment($cardId, $assignmentId, $labelId); + $this->getImportSystem()->updateCardLabelsAssignment((int)$cardId, (int)$assignmentId, $labelId); } catch (\Exception $e) { $this->addError('Failed to assign label ' . $labelId . ' to ' . $cardId, $e); } @@ -326,20 +322,20 @@ class BoardImportService { $allComments = $this->getImportSystem()->getComments(); foreach ($allComments as $cardId => $comments) { foreach ($comments as $commentId => $comment) { - $this->insertComment($cardId, $comment); - $this->getImportSystem()->updateComment($cardId, $commentId, $comment); + $this->insertComment((int)$cardId, $comment); + $this->getImportSystem()->updateComment((int)$cardId, $commentId, $comment); } } } - private function insertComment(string $cardId, IComment $comment): void { - $comment->setObject('deckCard', $cardId); + private function insertComment(int $cardId, IComment $comment): void { + $comment->setObject('deckCard', (string)$cardId); $comment->setVerb('comment'); // Check if parent is a comment on the same card if ($comment->getParentId() !== '0') { try { $parent = $this->commentsManager->get($comment->getParentId()); - if ($parent->getObjectType() !== Application::COMMENT_ENTITY_TYPE || $parent->getObjectId() !== $cardId) { + if ($parent->getObjectType() !== Application::COMMENT_ENTITY_TYPE || (int)$parent->getObjectId() !== $cardId) { throw new CommentNotFoundException(); } } catch (CommentNotFoundException $e) { @@ -362,7 +358,7 @@ class BoardImportService { foreach ($assignments as $assignment) { try { $assignment = $this->assignmentMapper->insert($assignment); - $this->getImportSystem()->updateCardAssignment($cardId, (string)$assignment->getId(), $assignment); + $this->getImportSystem()->updateCardAssignment((int)$cardId, $assignment->getId(), $assignment); $this->addOutput('Assignment ' . $assignment->getParticipant() . ' added'); } catch (NotFoundException $e) { $this->addError('No origin or mapping found for card "' . $cardId . '" and ' . $assignment->getTypeString() . ' assignment "' . $assignment->getParticipant(), $e); diff --git a/lib/Service/Importer/Systems/DeckJsonService.php b/lib/Service/Importer/Systems/DeckJsonService.php index b0c9b501d..e569cd2e7 100644 --- a/lib/Service/Importer/Systems/DeckJsonService.php +++ b/lib/Service/Importer/Systems/DeckJsonService.php @@ -16,6 +16,7 @@ use OCA\Deck\Db\Card; use OCA\Deck\Db\Label; use OCA\Deck\Db\Stack; use OCA\Deck\Service\Importer\ABoardImportService; +use OCP\Comments\IComment; use OCP\IUser; use OCP\IUserManager; @@ -118,6 +119,7 @@ class DeckJsonService extends ABoardImportService { $comments[$this->cards[$sourceCard->id]->getId()][$commentOriginal->id] = $comment; } } + /** @var array> */ return $comments; } diff --git a/lib/Service/Importer/Systems/TrelloApiService.php b/lib/Service/Importer/Systems/TrelloApiService.php index e94ff44bc..671556e16 100644 --- a/lib/Service/Importer/Systems/TrelloApiService.php +++ b/lib/Service/Importer/Systems/TrelloApiService.php @@ -17,7 +17,7 @@ use Psr\Log\LoggerInterface; class TrelloApiService extends TrelloJsonService { /** @var string */ public static $name = 'Trello API'; - protected $needValidateData = false; + protected bool $needValidateData = false; /** @var IClient */ private $httpClient; /** @var LoggerInterface */ @@ -176,7 +176,7 @@ class TrelloApiService extends TrelloJsonService { if (empty($queryString['limit'])) { return []; } - if (count($data) < $queryString['limit']) { + if ((count($data) < $queryString['limit']) || (count($data) === 0)) { return []; } $queryString['before'] = end($data)->id; diff --git a/lib/Service/Importer/Systems/TrelloJsonService.php b/lib/Service/Importer/Systems/TrelloJsonService.php index 25aa14105..9aead0e70 100644 --- a/lib/Service/Importer/Systems/TrelloJsonService.php +++ b/lib/Service/Importer/Systems/TrelloJsonService.php @@ -159,6 +159,7 @@ class TrelloJsonService extends ABoardImportService { $comments[$cardId][$commentId] = $comment; } } + /** @var array> */ return $comments; } diff --git a/lib/Service/LabelService.php b/lib/Service/LabelService.php index 69f372c42..bec844f9f 100644 --- a/lib/Service/LabelService.php +++ b/lib/Service/LabelService.php @@ -43,33 +43,24 @@ class LabelService { } /** - * @param $labelId - * @return \OCP\AppFramework\Db\Entity * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function find($labelId) { - if (is_numeric($labelId) === false) { - throw new BadRequestException('label id must be a number'); - } + public function find(int $labelId): Label { $this->permissionService->checkPermission($this->labelMapper, $labelId, Acl::PERMISSION_READ); return $this->labelMapper->find($labelId); } /** - * @param $title - * @param $color - * @param $boardId - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function create($title, $color, $boardId) { + public function create(string $title, string $color, int $boardId): Label { $this->labelServiceValidator->check(compact('title', 'color', 'boardId')); $this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE); @@ -106,15 +97,13 @@ class LabelService { } /** - * @param $id - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function delete($id) { + public function delete(int $id): Label { $this->labelServiceValidator->check(compact('id')); $this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE); @@ -127,17 +116,13 @@ class LabelService { } /** - * @param $id - * @param $title - * @param $color - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function update($id, $title, $color) { + public function update(int $id, string $title, string $color): Label { $this->labelServiceValidator->check(compact('title', 'color', 'id')); $this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE); diff --git a/lib/Service/OverviewService.php b/lib/Service/OverviewService.php index 1764dca07..f947748ec 100644 --- a/lib/Service/OverviewService.php +++ b/lib/Service/OverviewService.php @@ -9,53 +9,28 @@ declare(strict_types=1); namespace OCA\Deck\Service; -use OCA\Deck\Db\AssignmentMapper; use OCA\Deck\Db\Board; use OCA\Deck\Db\BoardMapper; use OCA\Deck\Db\CardMapper; -use OCA\Deck\Db\LabelMapper; use OCA\Deck\Model\CardDetails; -use OCP\Comments\ICommentsManager; -use OCP\IUserManager; class OverviewService { - private CardService $cardService; - private BoardMapper $boardMapper; - private LabelMapper $labelMapper; - private CardMapper $cardMapper; - private AssignmentMapper $assignedUsersMapper; - private IUserManager $userManager; - private ICommentsManager $commentsManager; - private AttachmentService $attachmentService; public function __construct( - CardService $cardService, - BoardMapper $boardMapper, - LabelMapper $labelMapper, - CardMapper $cardMapper, - AssignmentMapper $assignedUsersMapper, - IUserManager $userManager, - ICommentsManager $commentsManager, - AttachmentService $attachmentService, + private readonly CardService $cardService, + private readonly BoardMapper $boardMapper, + private readonly CardMapper $cardMapper, ) { - $this->cardService = $cardService; - $this->boardMapper = $boardMapper; - $this->labelMapper = $labelMapper; - $this->cardMapper = $cardMapper; - $this->assignedUsersMapper = $assignedUsersMapper; - $this->userManager = $userManager; - $this->commentsManager = $commentsManager; - $this->attachmentService = $attachmentService; } public function findUpcomingCards(string $userId): array { $userBoards = $this->boardMapper->findAllForUser($userId); $boardOwnerIds = array_filter(array_map(function (Board $board) { - return count($board->getAcl()) === 0 ? $board->getId() : null; + return count($board->getAcl() ?? []) === 0 ? $board->getId() : null; }, $userBoards)); $boardSharedIds = array_filter(array_map(function (Board $board) { - return count($board->getAcl()) > 0 ? $board->getId() : null; + return count($board->getAcl() ?? []) > 0 ? $board->getId() : null; }, $userBoards)); $foundCards = array_merge( diff --git a/lib/Service/PermissionService.php b/lib/Service/PermissionService.php index 27465e351..0f8831daa 100644 --- a/lib/Service/PermissionService.php +++ b/lib/Service/PermissionService.php @@ -29,6 +29,7 @@ class PermissionService { private array $users = []; private CappedMemoryCache $boardCache; + /** @var CappedMemoryCache> */ private CappedMemoryCache $permissionCache; public function __construct( @@ -49,15 +50,16 @@ class PermissionService { /** * Get current user permissions for a board by id * - * @return bool|array + * @return array */ - public function getPermissions(int $boardId, ?string $userId = null) { + public function getPermissions(int $boardId, ?string $userId = null): array { if ($userId === null) { $userId = $this->userId; } $cacheKey = $boardId . '-' . $userId; if ($cached = $this->permissionCache->get($cacheKey)) { + /** @var array $cached */ return $cached; } diff --git a/lib/Service/StackService.php b/lib/Service/StackService.php index 0a90390a0..631c4b184 100644 --- a/lib/Service/StackService.php +++ b/lib/Service/StackService.php @@ -77,7 +77,7 @@ class StackService { /** @param Stack[] $stacks */ private function enrichStacksWithCards(array $stacks, $since = -1): void { - $cardsByStackId = $this->cardMapper->findAllForStacks(array_map(fn (Stack $stack) => $stack->getId(), $stacks), null, null, $since); + $cardsByStackId = $this->cardMapper->findAllForStacks(array_map(fn (Stack $stack) => $stack->getId(), $stacks), null, 0, $since); foreach ($cardsByStackId as $stackId => $cards) { if (!$cards) { @@ -95,18 +95,11 @@ class StackService { } /** - * @param $stackId - * - * @return \OCP\AppFramework\Db\Entity * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function find($stackId) { - if (is_numeric($stackId) === false) { - throw new BadRequestException('stack id must be a number'); - } - + public function find(int $stackId): Stack { $this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_READ); $stack = $this->stackMapper->find($stackId); @@ -127,17 +120,11 @@ class StackService { } /** - * @param mixed $boardId - * * @return Stack[] * @throws \OCA\Deck\NoPermissionException * @throws BadRequestException */ - public function findAll($boardId, $since = -1) { - if (is_numeric($boardId) === false) { - throw new BadRequestException('boardId must be a number'); - } - + public function findAll(int $boardId, int $since = -1): array { $this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ); $stacks = $this->stackMapper->findAll($boardId); $this->enrichStacksWithCards($stacks, $since); @@ -145,7 +132,11 @@ class StackService { return $stacks; } - public function findCalendarEntries($boardId) { + /** + * @return Stack[] + * @throws \OCP\DB\Exception + */ + public function findCalendarEntries(int $boardId): array { try { $this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ); } catch (NoPermissionException $e) { @@ -155,7 +146,11 @@ class StackService { return $this->stackMapper->findAll($boardId); } - public function fetchDeleted($boardId) { + /** + * @return Stack[] + * @throws \OCP\DB\Exception + */ + public function fetchDeleted(int $boardId): array { $this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ); $stacks = $this->stackMapper->findDeleted($boardId); $this->enrichStacksWithCards($stacks); @@ -164,17 +159,11 @@ class StackService { } /** - * @param $boardId - * - * @return array + * @return Stack[] * @throws \OCA\Deck\NoPermissionException * @throws BadRequestException */ - public function findAllArchived($boardId) { - if (is_numeric($boardId) === false) { - throw new BadRequestException('board id must be a number'); - } - + public function findAllArchived(int $boardId): array { $this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ); $stacks = $this->stackMapper->findAll($boardId); $labels = $this->labelMapper->getAssignedLabelsForBoard($boardId); @@ -189,22 +178,18 @@ class StackService { $stacks[$stackIndex]->setCards($cards); } + /** @var Stack[] $stacks */ return $stacks; } /** - * @param $title - * @param $boardId - * @param integer $order - * - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function create($title, $boardId, $order) { + public function create(string $title, int $boardId, int $order): Stack { $this->stackServiceValidator->check(compact('title', 'boardId', 'order')); $this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE); @@ -226,19 +211,13 @@ class StackService { } /** - * @param $id - * - * @return \OCP\AppFramework\Db\Entity + * @return Stack The deleted stack. * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function delete($id) { - if (is_numeric($id) === false) { - throw new BadRequestException('stack id must be a number'); - } - + public function delete(int $id): Stack { $this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE); $stack = $this->stackMapper->find($id); @@ -256,20 +235,13 @@ class StackService { } /** - * @param $id - * @param $title - * @param $boardId - * @param $order - * @param $deletedAt - * - * @return \OCP\AppFramework\Db\Entity * @throws StatusException * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function update($id, $title, $boardId, $order, $deletedAt) { + public function update(int $id, string $title, int $boardId, int $order, ?int $deletedAt): Stack { $this->stackServiceValidator->check(compact('id', 'title', 'boardId', 'order')); $this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE); @@ -297,16 +269,13 @@ class StackService { } /** - * @param $id - * @param $order - * - * @return array + * @return array The stacks in the correct order. * @throws \OCA\Deck\NoPermissionException * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function reorder($id, $order) { + public function reorder(int $id, int $order): array { $this->stackServiceValidator->check(compact('id', 'order')); $this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE); @@ -327,7 +296,7 @@ class StackService { if ($stack->id !== $id) { $stack->setOrder($i++); } - $this->stackMapper->update($stack); + $stack = $this->stackMapper->update($stack); $result[$stack->getOrder()] = $stack; } $this->changeHelper->boardChanged($stackToSort->getBoardId()); diff --git a/lib/Sharing/DeckShareProvider.php b/lib/Sharing/DeckShareProvider.php index e77431248..ca0dbc5e7 100644 --- a/lib/Sharing/DeckShareProvider.php +++ b/lib/Sharing/DeckShareProvider.php @@ -13,7 +13,6 @@ namespace OCA\Deck\Sharing; use OC\Files\Cache\Cache; use OCA\Deck\Cache\AttachmentCacheHelper; use OCA\Deck\Db\Acl; -use OCA\Deck\Db\Board; use OCA\Deck\Db\BoardMapper; use OCA\Deck\Db\CardMapper; use OCA\Deck\Db\User; @@ -99,9 +98,13 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { * @inheritDoc */ public function create(IShare $share) { - $cardId = $share->getSharedWith(); + $cardId = (int)$share->getSharedWith(); $boardId = $this->cardMapper->findBoardId($cardId); $valid = $boardId !== null; + if (!$valid) { + throw new GenericShareException('Card not found', $this->l->t('Card not found'), 404); + } + try { $this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_EDIT); } catch (NoPermissionException $e) { @@ -147,7 +150,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ); $data = $this->getRawShare($shareId); - $this->attachmentCacheHelper->clearAttachmentCount((int)$cardId); + $this->attachmentCacheHelper->clearAttachmentCount($cardId); return $this->createShareObject($data); } @@ -213,7 +216,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ->from('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $data = $cursor->fetch(); $cursor->closeCursor(); @@ -297,7 +300,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) - ->execute(); + ->executeStatement(); /* * Update all user defined group shares @@ -310,7 +313,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) - ->execute(); + ->executeStatement(); /* * Now update the permissions for all children that have not set it to 0 @@ -320,7 +323,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0))) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) - ->execute(); + ->executeStatement(); return $share; } @@ -335,7 +338,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { $qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))); - $qb->execute(); + $qb->executeStatement(); $this->attachmentCacheHelper->clearAttachmentCount((int)$share->getSharedWith()); } @@ -355,7 +358,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )) - ->execute(); + ->executeQuery(); $data = $stmt->fetch(); $stmt->closeCursor(); @@ -376,14 +379,14 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { 'file_target' => $qb->createNamedParameter($share->getTarget()), 'permissions' => $qb->createNamedParameter(0), 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), - ])->execute(); + ])->executeStatement(); } elseif ($data['permissions'] !== 0) { // Already a userroom share. Update it. $qb = $this->dbConnection->getQueryBuilder(); $qb->update('share') ->set('permissions', $qb->createNamedParameter(0)) ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id']))) - ->execute(); + ->executeStatement(); } } @@ -397,7 +400,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ->where( $qb->expr()->eq('id', $qb->createNamedParameter($share->getId())) ); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $data = $cursor->fetch(); $cursor->closeCursor(); @@ -414,7 +417,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { $qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)) ); - $qb->execute(); + $qb->executeStatement(); return $this->getShareById((int)$share->getId(), $recipient); } @@ -435,7 +438,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )) ->setMaxResults(1) - ->execute(); + ->executeQuery(); $data = $stmt->fetch(); $stmt->closeCursor(); @@ -509,7 +512,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { $qb->orderBy('s.id'); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $shares = []; while ($data = $cursor->fetch()) { $shares[$data['fileid']][] = $this->createShareObject($data); @@ -554,7 +557,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { $qb->setFirstResult($offset); $qb->orderBy('id'); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $shares = []; while ($data = $cursor->fetch()) { $shares[] = $this->createShareObject($data); @@ -582,7 +585,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ->where($qb->expr()->eq('s.id', $qb->createNamedParameter($id))) ->andWhere($qb->expr()->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_DECK))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $data = $cursor->fetch(); $cursor->closeCursor(); @@ -647,7 +650,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )); - $stmt = $query->execute(); + $stmt = $query->executeQuery(); while ($data = $stmt->fetch()) { $this->applyBoardPermission($shareMap[$data['parent']], (int)$data['permissions'], $userId); @@ -677,7 +680,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ->from('share') ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_DECK))) - ->execute(); + ->executeQuery(); $shares = []; while ($data = $cursor->fetch()) { @@ -749,7 +752,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { $qb->andWhere($qb->expr()->eq('dc.deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); while ($data = $cursor->fetch()) { if (!$this->isAccessibleResult($data)) { continue; @@ -842,6 +845,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { /** * @inheritDoc + * @psalm-suppress InvalidReturnType Not returning anything */ public function getShareByToken($token) { throw new ShareNotFound(); @@ -851,7 +855,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_ROOM))) ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) - ->execute(); + ->executeQuery(); $data = $cursor->fetch(); @@ -1015,7 +1019,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_DECK))) ->orderBy('id'); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); while ($data = $cursor->fetch()) { $children[] = $this->createShareObject($data); } @@ -1038,7 +1042,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ) ); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); while ($data = $cursor->fetch()) { $share = $this->createShareObject($data); @@ -1055,7 +1059,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { ->where($qb->expr()->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_DECK))) ->andWhere($qb->expr()->notIn('s.share_with', $qb->createNamedParameter($allCardIds, IQueryBuilder::PARAM_STR_ARRAY))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $shares = []; while ($data = $cursor->fetch()) { $shares[] = $this->createShareObject($data); diff --git a/psalm.xml b/psalm.xml index fe9c87e23..26f04e051 100644 --- a/psalm.xml +++ b/psalm.xml @@ -6,6 +6,8 @@ xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config" errorBaseline="tests/psalm-baseline.xml" + findUnusedBaselineEntry="true" + findUnusedCode="false" phpVersion="8.1" > @@ -15,6 +17,7 @@ + @@ -26,13 +29,6 @@ - - - - - - - diff --git a/tests/data/deck.json b/tests/data/deck.json index b6744ec94..fbd79934e 100644 --- a/tests/data/deck.json +++ b/tests/data/deck.json @@ -90,8 +90,8 @@ } ], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -123,8 +123,8 @@ "createdAt": 1689667572, "labels": [], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -168,8 +168,8 @@ "type": 0 } ], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -240,8 +240,8 @@ } ], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -283,8 +283,8 @@ } ], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -328,8 +328,8 @@ "type": 0 } ], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -493,8 +493,8 @@ "createdAt": 1689667483, "labels": [], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -526,8 +526,8 @@ "createdAt": 1689667518, "labels": [], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -559,8 +559,8 @@ "createdAt": 1689667527, "labels": [], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -592,8 +592,8 @@ "createdAt": 1689667537, "labels": [], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -636,8 +636,8 @@ "createdAt": 1689667488, "labels": [], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -680,8 +680,8 @@ "createdAt": 1689667493, "labels": [], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", @@ -713,8 +713,8 @@ "createdAt": 1689667502, "labels": [], "assignedUsers": [], - "attachments": null, - "attachmentCount": null, + "attachments": [], + "attachmentCount": 0, "owner": { "primaryKey": "admin", "uid": "admin", diff --git a/tests/integration/base-query-count.txt b/tests/integration/base-query-count.txt index f66f298f8..a54d62168 100644 --- a/tests/integration/base-query-count.txt +++ b/tests/integration/base-query-count.txt @@ -1 +1 @@ -82773 +82774 diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml index 1fed3db61..41b05e8fa 100644 --- a/tests/psalm-baseline.xml +++ b/tests/psalm-baseline.xml @@ -1,152 +1,78 @@ - + - $types + - - - void - - - $this->boardMapper - $this->stackMapper - - - $this->boardMapper - $this->stackMapper - - - - $modified === null - $modified === null - - - Util - - - $this->userId - - - $this->userId - - - - - $cardId - $cardId - $cardId - $commentId - $commentId - $parentId - + + + - LoadSidebar + - - $modified !== null - - - Util - + + + - ExternalCalendar + - ICalendarObject + - ICalendarProvider + - NotFound + - VCalendar - VCalendar + + - - - $entity->getId() - - - getUserIdGroups - - - - - $labelId - - - VCalendar - VCalendar + + - - - public function __construct(Board $board) { - public function __construct(Board $board) { - - - - - public function __construct(Card $card, ?Board $board = null) { - public function __construct(Card $card, ?Board $board = null) { - - - - - try { - $attachment = $this->attachmentMapper->find($attachmentId); - } catch (IMapperException $e) { - throw new NoPermissionException('Permission denied'); - } - - - - - findAll - findAll - - - - - $member !== null - - - is_resource($content) - is_resource($content) + + - - - getShareByToken - + + + + + + - - - [self::class, 'listenPreShare'] - + + + date]]> + + + + + + diff --git a/tests/unit/Activity/ActivityManagerTest.php b/tests/unit/Activity/ActivityManagerTest.php index c1036c3d7..cb83a3e49 100644 --- a/tests/unit/Activity/ActivityManagerTest.php +++ b/tests/unit/Activity/ActivityManagerTest.php @@ -160,6 +160,7 @@ class ActivityManagerTest extends TestCase { 'id' => 123, 'title' => 'My card', 'description' => str_repeat('A', 1000), + 'stackId' => 42, ]); $this->cardMapper->expects(self::any()) ->method('find') @@ -170,6 +171,7 @@ class ActivityManagerTest extends TestCase { ]); $this->stackMapper->expects(self::any()) ->method('find') + ->with(42) ->willReturn($stack); $expectedCard = $card->jsonSerialize(); @@ -207,6 +209,7 @@ class ActivityManagerTest extends TestCase { $card->setDescription(str_repeat('A', 5000)); $card->setTitle('My card'); $card->setId(123); + $card->setStackId(42); $this->cardMapper->expects(self::any()) ->method('find') ->willReturn($card); @@ -254,6 +257,7 @@ class ActivityManagerTest extends TestCase { $card->setDescription(str_repeat('A', 5000)); $card->setTitle('My card'); $card->setId(123); + $card->setStackId(42); $this->cardMapper->expects(self::any()) ->method('find') ->willReturn($card); diff --git a/tests/unit/Db/AttachmentMapperTest.php b/tests/unit/Db/AttachmentMapperTest.php index 9489909f1..017aefb5d 100644 --- a/tests/unit/Db/AttachmentMapperTest.php +++ b/tests/unit/Db/AttachmentMapperTest.php @@ -119,7 +119,7 @@ class AttachmentMapperTest extends TestCase { ->method('isOwner') ->with('admin', 1) ->willReturn(true); - $this->assertTrue($this->attachmentMapper->isOwner('admin', (string)$this->attachments[0]->getId())); + $this->assertTrue($this->attachmentMapper->isOwner('admin', $this->attachments[0]->getId())); } public function testIsOwnerInvalid() { @@ -127,7 +127,7 @@ class AttachmentMapperTest extends TestCase { ->method('isOwner') ->with('admin', 1) ->will($this->throwException(new DoesNotExistException('does not exist'))); - $this->assertFalse($this->attachmentMapper->isOwner('admin', (string)$this->attachments[0]->getId())); + $this->assertFalse($this->attachmentMapper->isOwner('admin', $this->attachments[0]->getId())); } public function testFindBoardId() { diff --git a/tests/unit/Db/CardTest.php b/tests/unit/Db/CardTest.php index 92393e7ba..b33b8c961 100644 --- a/tests/unit/Db/CardTest.php +++ b/tests/unit/Db/CardTest.php @@ -81,8 +81,8 @@ class CardTest extends TestCase { 'duedate' => null, 'overdue' => 0, 'archived' => false, - 'attachments' => null, - 'attachmentCount' => null, + 'attachments' => [], + 'attachmentCount' => 0, 'assignedUsers' => null, 'deletedAt' => 0, 'commentsUnread' => 0, @@ -110,8 +110,8 @@ class CardTest extends TestCase { 'duedate' => null, 'overdue' => 0, 'archived' => false, - 'attachments' => null, - 'attachmentCount' => null, + 'attachments' => [], + 'attachmentCount' => 0, 'assignedUsers' => null, 'deletedAt' => 0, 'commentsUnread' => 0, @@ -141,8 +141,8 @@ class CardTest extends TestCase { 'duedate' => null, 'overdue' => 0, 'archived' => false, - 'attachments' => null, - 'attachmentCount' => null, + 'attachments' => [], + 'attachmentCount' => 0, 'assignedUsers' => ['user1'], 'deletedAt' => 0, 'commentsUnread' => 0, diff --git a/tests/unit/Service/AttachmentServiceTest.php b/tests/unit/Service/AttachmentServiceTest.php index abdebb0b1..0a4b4e344 100644 --- a/tests/unit/Service/AttachmentServiceTest.php +++ b/tests/unit/Service/AttachmentServiceTest.php @@ -1,5 +1,7 @@ * @@ -116,7 +118,7 @@ class AttachmentServiceTest extends TestCase { $this->activityManager = $this->createMock(ActivityManager::class); $this->appContainer->expects($this->exactly(2)) - ->method('query') + ->method('get') ->withConsecutive( [FileService::class], [FilesAppService::class] @@ -156,7 +158,7 @@ class AttachmentServiceTest extends TestCase { $fileAppServiceMock = $this->createMock(FilesAppService::class); $appContainer->expects($this->exactly(3)) - ->method('query') + ->method('get') ->withConsecutive( [FileService::class], [FilesAppService::class], @@ -185,7 +187,7 @@ class AttachmentServiceTest extends TestCase { $fileAppServiceMock = $this->createMock(FilesAppService::class); $appContainer->expects($this->exactly(3)) - ->method('query') + ->method('get') ->withConsecutive( [FileService::class], [FilesAppService::class], diff --git a/tests/unit/Service/BoardServiceTest.php b/tests/unit/Service/BoardServiceTest.php index 3dfed9b06..5ce29df8c 100644 --- a/tests/unit/Service/BoardServiceTest.php +++ b/tests/unit/Service/BoardServiceTest.php @@ -1,5 +1,7 @@ * @@ -240,6 +242,7 @@ class BoardServiceTest extends TestCase { public function testDelete() { $board = new Board(); + $board->setId(42); $board->setOwner('admin'); $board->setDeletedAt(0); $this->boardMapper->expects($this->once()) @@ -252,7 +255,7 @@ class BoardServiceTest extends TestCase { ]); $this->sessionMapper->expects($this->once()) ->method('findAllActive') - ->with(null) + ->with(42) ->willReturn([]); $boardDeleted = clone $board; $boardDeleted->setDeletedAt(1); @@ -267,7 +270,7 @@ class BoardServiceTest extends TestCase { $user->method('getUID')->willReturn('admin'); $acl = new Acl(); $acl->setBoardId(123); - $acl->setType('user'); + $acl->setType(Acl::PERMISSION_TYPE_USER); $acl->setParticipant('admin'); $acl->setPermissionEdit(true); $acl->setPermissionShare(true); @@ -287,7 +290,7 @@ class BoardServiceTest extends TestCase { 'admin' => 'admin', ]); $this->assertEquals($acl, $this->service->addAcl( - 123, 'user', 'admin', true, true, true + 123, Acl::PERMISSION_TYPE_USER, 'admin', true, true, true )); } @@ -323,7 +326,7 @@ class BoardServiceTest extends TestCase { public function testAddAclExtendPermission($currentUserAcl, $providedAcl, $resultingAcl) { $existingAcl = new Acl(); $existingAcl->setBoardId(123); - $existingAcl->setType('user'); + $existingAcl->setType(Acl::PERMISSION_TYPE_USER); $existingAcl->setParticipant('admin'); $existingAcl->setPermissionEdit($currentUserAcl[0]); $existingAcl->setPermissionShare($currentUserAcl[1]); @@ -391,14 +394,14 @@ class BoardServiceTest extends TestCase { ->method('dispatchTyped') ->with(new AclCreatedEvent($acl)); $this->assertEquals($expected, $this->service->addAcl( - 123, 'user', 'admin', $providedAcl[0], $providedAcl[1], $providedAcl[2] + 123, Acl::PERMISSION_TYPE_USER, 'admin', $providedAcl[0], $providedAcl[1], $providedAcl[2] )); } public function testUpdateAcl() { $acl = new Acl(); $acl->setBoardId(123); - $acl->setType('user'); + $acl->setType(Acl::PERMISSION_TYPE_USER); $acl->setParticipant('admin'); $acl->setPermissionEdit(true); $acl->setPermissionShare(true); diff --git a/tests/unit/Service/CardServiceTest.php b/tests/unit/Service/CardServiceTest.php index 0f1e1445e..041c012ff 100644 --- a/tests/unit/Service/CardServiceTest.php +++ b/tests/unit/Service/CardServiceTest.php @@ -183,6 +183,7 @@ class CardServiceTest extends TestCase { ->willReturn($boardMock); $card = new Card(); $card->setId(1337); + $card->setStackId(123); $this->cardMapper->expects($this->any()) ->method('find') ->with(123) @@ -200,6 +201,7 @@ class CardServiceTest extends TestCase { ->with([1337]) ->willReturn([$a1, $a2]); $cardExpected = new Card(); + $cardExpected->setStackId(123); $cardExpected->setId(1337); $cardExpected->setAssignedUsers([$a1, $a2]); $cardExpected->setRelatedBoard($boardMock); @@ -218,6 +220,7 @@ class CardServiceTest extends TestCase { 'stackId' => 123, 'order' => 999, 'type' => 'text', + 'id' => 0, ]); $stack = Stack::fromParams([ 'id' => 123, @@ -243,6 +246,8 @@ class CardServiceTest extends TestCase { $card = new Card(); $card->setId(1); $card->setTitle('Card title'); + $card->setType('test'); + $card->setOrder(0); $card->setOwner('admin'); $card->setStackId(12345); $clonedCard = clone $card; @@ -290,8 +295,7 @@ class CardServiceTest extends TestCase { ->willReturn([$label]); $this->cardMapper->expects($this->once()) ->method('assignLabel') - ->with($clonedCard->getId(), $label->getId()) - ->willReturn($label); + ->with($clonedCard->getId(), $label->getId()); $stackMock = new Stack(); $stackMock->setBoardId(1234); @@ -329,6 +333,7 @@ class CardServiceTest extends TestCase { ]); $this->cardMapper->expects($this->once())->method('find')->willReturn($card); $this->cardMapper->expects($this->once())->method('update')->willReturnCallback(function ($c) { + $c->setId(1); return $c; }); $this->stackMapper->expects($this->once()) diff --git a/tests/unit/Service/DefaultBoardServiceTest.php b/tests/unit/Service/DefaultBoardServiceTest.php index f5a1ad975..78011637c 100644 --- a/tests/unit/Service/DefaultBoardServiceTest.php +++ b/tests/unit/Service/DefaultBoardServiceTest.php @@ -220,8 +220,9 @@ class DefaultBoardServiceTest extends TestCase { return $stack; } - private function assembleTestCard($title, $stackId, $userId) { + private function assembleTestCard(string $title, int $stackId, string $userId): Card { $card = new Card(); + $card->setId(1); $card->setTitle($title); $card->setStackId($stackId); $card->setType('text'); diff --git a/tests/unit/Service/Importer/BoardImportServiceTest.php b/tests/unit/Service/Importer/BoardImportServiceTest.php index 7d8086092..c1cc7b2f9 100644 --- a/tests/unit/Service/Importer/BoardImportServiceTest.php +++ b/tests/unit/Service/Importer/BoardImportServiceTest.php @@ -192,14 +192,17 @@ class BoardImportServiceTest extends \Test\TestCase { ->expects($this->once()) ->method('save'); + $assignment = new Assignment(); + $assignment->setId(1); $this->trelloJsonService ->method('getCardAssignments') ->willReturn([ - 'fakecardid' => [new Assignment()] + 'fakecardid' => [$assignment] ]); $this->assignmentMapper ->expects($this->once()) - ->method('insert'); + ->method('insert') + ->willReturn($assignment); $this->boardImportService->import(); self::assertTrue(true); diff --git a/tests/unit/Service/LabelServiceTest.php b/tests/unit/Service/LabelServiceTest.php index c492a972c..c563bc136 100644 --- a/tests/unit/Service/LabelServiceTest.php +++ b/tests/unit/Service/LabelServiceTest.php @@ -1,5 +1,7 @@ * @@ -29,6 +31,7 @@ use OCA\Deck\Db\ChangeHelper; use OCA\Deck\Db\Label; use OCA\Deck\Db\LabelMapper; use OCA\Deck\Validators\LabelServiceValidator; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class LabelServiceTest extends TestCase { @@ -42,9 +45,8 @@ class LabelServiceTest extends TestCase { /** @var BoardService|\PHPUnit\Framework\MockObject\MockObject */ private $boardService; /** @var ChangeHelper|\PHPUnit\Framework\MockObject\MockObject */ - private $changeHelper; - /** @var LabelServiceValidator\MockObject */ - private $labelServiceValidator; + private ChangeHelper&MockObject $changeHelper; + private LabelServiceValidator&MockObject $labelServiceValidator; public function setUp(): void { parent::setUp(); @@ -66,8 +68,9 @@ class LabelServiceTest extends TestCase { } public function testFind() { - $this->labelMapper->expects($this->once())->method('find')->willReturn(true); - $this->assertTrue($this->labelService->find(123)); + $label = $this->createMock(Label::class); + $this->labelMapper->expects($this->once())->method('find')->willReturn($label); + $this->assertEquals($label, $this->labelService->find(123)); } public function testCreate() { diff --git a/tests/unit/Service/StackServiceTest.php b/tests/unit/Service/StackServiceTest.php index 2161a591b..d5b4371bd 100644 --- a/tests/unit/Service/StackServiceTest.php +++ b/tests/unit/Service/StackServiceTest.php @@ -1,5 +1,7 @@ * @@ -48,8 +50,7 @@ use Test\TestCase; */ class StackServiceTest extends TestCase { - /** @var StackService */ - private $stackService; + private StackService $stackService; /** @var \PHPUnit\Framework\MockObject\MockObject|StackMapper */ private $stackMapper; /** @var \PHPUnit\Framework\MockObject\MockObject|CardMapper */ @@ -251,6 +252,9 @@ class StackServiceTest extends TestCase { $this->stackMapper->expects($this->once()) ->method('findAll') ->willReturn($stacks); + $this->stackMapper->expects($this->any()) + ->method('update') + ->willReturnCallback(fn (Stack $stack): Stack => $stack); $actual = $this->stackService->reorder(1, 2); $a = $this->createStack(1, 2); $b = $this->createStack(2, 0); @@ -259,7 +263,7 @@ class StackServiceTest extends TestCase { $this->assertEquals($expected, $actual); } - private function createStack($id, $order) { + private function createStack(int $id, int $order) { $stack = new Stack(); $stack->setId($id); $stack->setBoardId(1); diff --git a/tests/unit/controller/BoardControllerTest.php b/tests/unit/controller/BoardControllerTest.php index 298e1c9c5..821edd207 100644 --- a/tests/unit/controller/BoardControllerTest.php +++ b/tests/unit/controller/BoardControllerTest.php @@ -1,5 +1,7 @@ * @@ -26,46 +28,47 @@ namespace OCA\Deck\Controller; use OCA\Deck\Db\Acl; use OCA\Deck\Db\Board; +use OCA\Deck\Service\BoardService; +use OCA\Deck\Service\Importer\BoardImportService; +use OCA\Deck\Service\PermissionService; +use OCP\IGroupManager; +use OCP\IL10N; +use OCP\IRequest; use OCP\IUser; +use OCP\IUserManager; +use PHPUnit\Framework\MockObject\MockObject; class BoardControllerTest extends \Test\TestCase { - private $l10n; - private $controller; - private $request; - private $userManager; - private $groupManager; - private $boardService; - private $permissionService; - private $boardImportService; + private IL10N&MockObject $l10n; + private BoardController $controller; + private IRequest&MockObject $request; + private IUserManager&MockObject $userManager; + private IGroupManager&MockObject $groupManager; + private BoardService&MockObject $boardService; + private PermissionService&MockObject $permissionService; + private BoardImportService&MockObject $boardImportService; private $userId = 'user'; public function setUp(): void { - $this->l10n = $this->request = $this->getMockBuilder( - '\OCP\IL10n') + $this->l10n = $this->getMockBuilder(IL10N::class) ->disableOriginalConstructor() ->getMock(); - $this->request = $this->getMockBuilder( - '\OCP\IRequest') + $this->request = $this->getMockBuilder(IRequest::class) ->disableOriginalConstructor() ->getMock(); - $this->userManager = $this->getMockBuilder( - '\OCP\IUserManager') + $this->userManager = $this->getMockBuilder(IUserManager::class) ->disableOriginalConstructor() ->getMock(); - $this->groupManager = $this->getMockBuilder( - '\OCP\IGroupManager') + $this->groupManager = $this->getMockBuilder(IGroupManager::class) ->disableOriginalConstructor() ->getMock(); - $this->boardService = $this->getMockBuilder( - '\OCA\Deck\Service\BoardService') + $this->boardService = $this->getMockBuilder(BoardService::class) ->disableOriginalConstructor() ->getMock(); - $this->permissionService = $this->getMockBuilder( - '\OCA\Deck\Service\PermissionService') + $this->permissionService = $this->getMockBuilder(PermissionService::class) ->disableOriginalConstructor() ->getMock(); - $this->boardImportService = $this->getMockBuilder( - '\OCA\Deck\Service\Importer\BoardImportService') + $this->boardImportService = $this->getMockBuilder(BoardImportService::class) ->disableOriginalConstructor() ->getMock(); @@ -107,35 +110,39 @@ class BoardControllerTest extends \Test\TestCase { } public function testCreate() { + $board = $this->createMock(Board::class); $this->boardService->expects($this->once()) ->method('create') - ->with(1, 'user', 3) - ->willReturn(1); - $this->assertEquals(1, $this->controller->create(1, 3)); + ->with('abc', 'user', 'green') + ->willReturn($board); + $this->assertEquals($board, $this->controller->create('abc', 'green')); } - public function testUpdate() { + public function testUpdate(): void { + $board = $this->createMock(Board::class); $this->boardService->expects($this->once()) ->method('update') - ->with(1, 2, 3, false) - ->willReturn(1); - $this->assertEquals(1, $this->controller->update(1, 2, 3, false)); + ->with(1, 'abc', 'green', false) + ->willReturn($board); + $this->assertEquals($board, $this->controller->update(1, 'abc', 'green', false)); } - public function testDelete() { + public function testDelete(): void { + $board = $this->createMock(Board::class); $this->boardService->expects($this->once()) ->method('delete') ->with(123) - ->willReturn(1); - $this->assertEquals(1, $this->controller->delete(123)); + ->willReturn($board); + $this->assertEquals($board, $this->controller->delete(123)); } public function testDeleteUndo() { + $board = $this->createMock(Board::class); $this->boardService->expects($this->once()) ->method('deleteUndo') ->with(123) - ->willReturn(1); - $this->assertEquals(1, $this->controller->deleteUndo(123)); + ->willReturn($board); + $this->assertEquals($board, $this->controller->deleteUndo(123)); } public function testGetUserPermissions() { @@ -158,20 +165,22 @@ class BoardControllerTest extends \Test\TestCase { $this->assertEquals($expected, $this->controller->getUserPermissions(123)); } - public function testAddAcl() { + public function testAddAcl(): void { + $acl = $this->createMock(Acl::class); $this->boardService->expects($this->once()) ->method('addAcl') - ->with(1, 2, 3, 4, 5, 6) - ->willReturn(1); - $this->assertEquals(1, $this->controller->addAcl(1, 2, 3, 4, 5, 6)); + ->with(1, 2, 'user1', true, true, true) + ->willReturn($acl); + $this->assertEquals($acl, $this->controller->addAcl(1, 2, 'user1', true, true, true)); } - public function testUpdateAcl() { + public function testUpdateAcl(): void { + $acl = $this->createMock(Acl::class); $this->boardService->expects($this->once()) ->method('updateAcl') - ->with(1, 2, 3, 4) - ->willReturn(1); - $this->assertEquals(1, $this->controller->updateAcl(1, 2, 3, 4)); + ->with(1, true, true, true) + ->willReturn($acl); + $this->assertEquals($acl, $this->controller->updateAcl(1, true, true, true)); } public function testDeleteAcl() { diff --git a/tests/unit/controller/CardApiControllerTest.php b/tests/unit/controller/CardApiControllerTest.php index c2fe13a50..c466b9614 100644 --- a/tests/unit/controller/CardApiControllerTest.php +++ b/tests/unit/controller/CardApiControllerTest.php @@ -1,5 +1,7 @@ * @@ -30,15 +32,16 @@ use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; class CardApiControllerTest extends \Test\TestCase { - private $controller; - private $request; - private $cardService; - private $userId = 'admin'; - private $cardExample; - private $stackExample; - private $assignmentService; + private CardApiController $controller; + private IRequest&MockObject $request; + private CardService&MockObject $cardService; + private string $userId = 'admin'; + private array $cardExample; + private array $stackExample; + private AssignmentService&MockObject $assignmentService; public function setUp(): void { parent::setUp(); @@ -51,7 +54,7 @@ class CardApiControllerTest extends \Test\TestCase { $this->stackExample['id'] = 1; $this->controller = new CardApiController( - $appName = 'deck', + 'deck', $this->request, $this->cardService, $this->assignmentService, @@ -59,7 +62,7 @@ class CardApiControllerTest extends \Test\TestCase { ); } - public function testGet() { + public function testGet(): void { $card = new Card(); $card->setId($this->cardExample['id']); @@ -116,7 +119,7 @@ class CardApiControllerTest extends \Test\TestCase { ->willReturn($card); $expected = new DataResponse($card, HTTP::STATUS_OK); - $actual = $this->controller->update('title', 'plain', 0, 'description', $this->userId, null); + $actual = $this->controller->update('title', 'plain', $this->userId, 'description', 0, null); $this->assertEquals($expected, $actual); } diff --git a/tests/unit/controller/CardControllerTest.php b/tests/unit/controller/CardControllerTest.php index 083d648b9..de70b26e9 100644 --- a/tests/unit/controller/CardControllerTest.php +++ b/tests/unit/controller/CardControllerTest.php @@ -1,5 +1,7 @@ * @@ -24,6 +26,7 @@ namespace OCA\Deck\Controller; +use OCA\Deck\Db\Card; use OCA\Deck\Service\AssignmentService; use OCA\Deck\Service\CardService; use OCP\IRequest; @@ -32,17 +35,11 @@ use Test\TestCase; class CardControllerTest extends TestCase { - /** @var CardController|MockObject */ - private $controller; - /** @var IRequest|MockObject */ - private $request; - /** @var CardService|MockObject */ - private $cardService; - /** @var AssignmentService|MockObject */ - private $assignmentService; - /** @var string */ - private $userId = 'user'; - + private CardController $controller; + private IRequest&MockObject $request; + private CardService&MockObject $cardService; + private AssignmentService&MockObject $assignmentService; + private string $userId = 'user'; public function setUp(): void { $this->request = $this->createMock(IRequest::class); @@ -58,39 +55,43 @@ class CardControllerTest extends TestCase { } public function testRead() { + $card = $this->createMock(Card::class); $this->cardService->expects($this->once()) ->method('find') ->with(123) - ->willReturn(1); - $this->assertEquals(1, $this->controller->read(123)); + ->willReturn($card); + $this->assertEquals($card, $this->controller->read(123)); } - public function testCreate() { + public function testCreate(): void { + $card = $this->createMock(Card::class); $this->cardService->expects($this->once()) ->method('create') ->with('foo', 1, 'text', 3, $this->userId) - ->willReturn(1); - $this->assertEquals(1, $this->controller->create('foo', 1, 'text', 3)); + ->willReturn($card); + $this->assertEquals($card, $this->controller->create('foo', 1, 'text', 3)); } - public function testUpdate() { + public function testUpdate(): void { + $card = $this->createMock(Card::class); $this->cardService->expects($this->once()) ->method('update') ->with(1, 'title', 3, 'text', $this->userId, 'foo', 5, '2017-01-01 00:00:00') - ->willReturn(1); - $this->assertEquals(1, $this->controller->update(1, 'title', 3, 'text', 5, 'foo', '2017-01-01 00:00:00', null)); + ->willReturn($card); + $this->assertEquals($card, $this->controller->update(1, 'title', 3, 'text', 5, 'foo', '2017-01-01 00:00:00', null)); } - public function testDelete() { + public function testDelete(): void { + $card = $this->createMock(Card::class); $this->cardService->expects($this->once()) ->method('delete') ->with(123) - ->willReturn(1); - $this->assertEquals(1, $this->controller->delete(123)); + ->willReturn($card); + $this->assertEquals($card, $this->controller->delete(123)); } public function testArchive() { - $this->cardService->expects($this->once())->method('archive')->willReturn(true); + $this->cardService->expects($this->once())->method('archive'); $this->controller->archive(1); } public function testUnarchive() { diff --git a/tests/unit/controller/LabelControllerTest.php b/tests/unit/controller/LabelControllerTest.php index 05bbd7276..5b2d12a16 100644 --- a/tests/unit/controller/LabelControllerTest.php +++ b/tests/unit/controller/LabelControllerTest.php @@ -24,6 +24,7 @@ namespace OCA\Deck\Controller; +use OCA\Deck\Db\Label; use OCA\Deck\Service\LabelService; use OCP\AppFramework\Controller; use OCP\IRequest; @@ -56,27 +57,30 @@ class LabelControllerTest extends \Test\TestCase { } - public function testCreate() { + public function testCreate(): void { + $label = $this->createMock(Label::class); $this->labelService->expects($this->once()) ->method('create') ->with(1, 2, 3) - ->willReturn(1); - $this->assertEquals(1, $this->controller->create(1, 2, 3)); + ->willReturn($label); + $this->assertEquals($label, $this->controller->create(1, 2, 3)); } - public function testUpdate() { + public function testUpdate(): void { + $label = $this->createMock(Label::class); $this->labelService->expects($this->once()) ->method('update') ->with(1, 2, 3) - ->willReturn(1); - $this->assertEquals(1, $this->controller->update(1, 2, 3)); + ->willReturn($label); + $this->assertEquals($label, $this->controller->update(1, 2, 3)); } - public function testDelete() { + public function testDelete(): void { + $label = $this->createMock(Label::class); $this->labelService->expects($this->once()) ->method('delete') ->with(123) - ->willReturn(1); - $this->assertEquals(1, $this->controller->delete(123)); + ->willReturn($label); + $this->assertEquals($label, $this->controller->delete(123)); } } diff --git a/tests/unit/controller/StackControllerTest.php b/tests/unit/controller/StackControllerTest.php index f48f06341..b925cd3e0 100644 --- a/tests/unit/controller/StackControllerTest.php +++ b/tests/unit/controller/StackControllerTest.php @@ -1,5 +1,7 @@ * @@ -24,6 +26,7 @@ namespace OCA\Deck\Controller; +use OCA\Deck\Db\Stack; use OCA\Deck\Service\StackService; use OCP\AppFramework\Controller; use OCP\IRequest; @@ -67,34 +70,38 @@ class StackControllerTest extends \Test\TestCase { } public function testCreate() { + $stack = $this->createMock(Stack::class); $this->stackService->expects($this->once()) ->method('create') - ->with(1, 2, 3) - ->willReturn(1); - $this->assertEquals(1, $this->controller->create(1, 2, 3)); + ->with('abc', 2, 3) + ->willReturn($stack); + $this->assertEquals($stack, $this->controller->create('abc', 2, 3)); } public function testUpdate() { + $stack = $this->createMock(Stack::class); $this->stackService->expects($this->once()) ->method('update') - ->with(1, 2, 3, 4) - ->willReturn(1); - $this->assertEquals(1, $this->controller->update(1, 2, 3, 4, null)); + ->with(1, 'abc', 3, 4) + ->willReturn($stack); + $this->assertEquals($stack, $this->controller->update(1, 'abc', 3, 4, null)); } public function testReorder() { + $stack = $this->createMock(Stack::class); $this->stackService->expects($this->once()) ->method('reorder') ->with(1, 2) - ->willReturn(1); - $this->assertEquals(1, $this->controller->reorder(1, 2)); + ->willReturn([$stack]); + $this->assertEquals([$stack], $this->controller->reorder(1, 2)); } public function testDelete() { + $stack = $this->createMock(Stack::class); $this->stackService->expects($this->once()) ->method('delete') ->with(123) - ->willReturn(1); - $this->assertEquals(1, $this->controller->delete(123)); + ->willReturn($stack); + $this->assertEquals($stack, $this->controller->delete(123)); } } diff --git a/vendor-bin/psalm/composer.json b/vendor-bin/psalm/composer.json new file mode 100644 index 000000000..5050a8472 --- /dev/null +++ b/vendor-bin/psalm/composer.json @@ -0,0 +1,10 @@ +{ + "require-dev": { + "vimeo/psalm": "^6.4" + }, + "config": { + "platform": { + "php": "8.1.17" + } + } +} diff --git a/vendor-bin/psalm/composer.lock b/vendor-bin/psalm/composer.lock new file mode 100644 index 000000000..a91d496d7 --- /dev/null +++ b/vendor-bin/psalm/composer.lock @@ -0,0 +1,3217 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "8213a64b36972a05a20265830eecb0c5", + "packages": [], + "packages-dev": [ + { + "name": "amphp/amp", + "version": "v3.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9", + "reference": "7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v3.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-01-26T16:07:39+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T17:10:27+00:00" + }, + { + "name": "amphp/cache", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/cache.git", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Cache\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A fiber-aware cache API based on Amp and Revolt.", + "homepage": "https://amphp.org/cache", + "support": { + "issues": "https://github.com/amphp/cache/issues", + "source": "https://github.com/amphp/cache/tree/v2.0.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:38:06+00:00" + }, + { + "name": "amphp/dns", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/dns.git", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/process": "^2", + "daverandom/libdns": "^2.0.2", + "ext-filter": "*", + "ext-json": "*", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Wright", + "email": "addr@daverandom.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" + ], + "support": { + "issues": "https://github.com/amphp/dns/issues", + "source": "https://github.com/amphp/dns/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-01-19T15:43:40+00:00" + }, + { + "name": "amphp/parallel", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel.git", + "reference": "5113111de02796a782f5d90767455e7391cca190" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel/zipball/5113111de02796a782f5d90767455e7391cca190", + "reference": "5113111de02796a782f5d90767455e7391cca190", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/pipeline": "^1", + "amphp/process": "^2", + "amphp/serialization": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "files": [ + "src/Context/functions.php", + "src/Context/Internal/functions.php", + "src/Ipc/functions.php", + "src/Worker/functions.php" + ], + "psr-4": { + "Amp\\Parallel\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", + "keywords": [ + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" + ], + "support": { + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v2.3.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-12-21T01:56:09+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:16:53+00:00" + }, + { + "name": "amphp/pipeline", + "version": "v1.2.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/pipeline.git", + "reference": "7b52598c2e9105ebcddf247fc523161581930367" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/7b52598c2e9105ebcddf247fc523161581930367", + "reference": "7b52598c2e9105ebcddf247fc523161581930367", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Pipeline\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" + ], + "support": { + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T16:33:53+00:00" + }, + { + "name": "amphp/process", + "version": "v2.0.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A fiber-aware process manager based on Amp and Revolt.", + "homepage": "https://amphp.org/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v2.0.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:13:44+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/socket", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/socket.git", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/socket/zipball/58e0422221825b79681b72c50c47a930be7bf1e1", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/dns": "^2", + "ext-openssl": "*", + "kelunik/certificate": "^1.1", + "league/uri": "^6.5 | ^7", + "league/uri-interfaces": "^2.3 | ^7", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/process": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php", + "src/SocketAddress/functions.php" + ], + "psr-4": { + "Amp\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", + "homepage": "https://github.com/amphp/socket", + "keywords": [ + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" + ], + "support": { + "issues": "https://github.com/amphp/socket/issues", + "source": "https://github.com/amphp/socket/tree/v2.3.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-21T14:33:03+00:00" + }, + { + "name": "amphp/sync", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-08-03T19:31:26+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.3", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-09-19T14:15:21+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "daverandom/libdns", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "Required for IDN support" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "LibDNS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "DNS protocol implementation written in pure PHP", + "keywords": [ + "dns" + ], + "support": { + "issues": "https://github.com/DaveRandom/LibDNS/issues", + "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" + }, + "time": "2024-04-12T12:12:48+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=13" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^13", + "phpstan/phpstan": "1.4.10 || 2.1.11", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + }, + "time": "2025-04-07T20:06:18+00:00" + }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.3", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" + }, + "time": "2024-04-30T00:40:11+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, + { + "name": "kelunik/certificate", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/kelunik/certificate.git", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=7.0" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^6 | 7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Kelunik\\Certificate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Access certificate details and transform between different formats.", + "keywords": [ + "DER", + "certificate", + "certificates", + "openssl", + "pem", + "x509" + ], + "support": { + "issues": "https://github.com/kelunik/certificate/issues", + "source": "https://github.com/kelunik/certificate/tree/v1.1.3" + }, + "time": "2023-02-03T21:26:53+00:00" + }, + { + "name": "league/uri", + "version": "7.5.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "81fb5145d2644324614cc532b28efd0215bda430" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.5", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.5.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:40:02+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:18:47+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.5.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.5.0" + }, + "time": "2024-09-08T10:13:13+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.5.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + }, + "time": "2025-05-31T08:24:38+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" + }, + "time": "2025-04-13T19:20:35+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + }, + "time": "2024-11-09T15:12:26+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" + }, + "time": "2025-02-19T13:28:12+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "revolt/event-loop", + "version": "v1.0.7", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/09bf1bf7f7f574453efe43044b06fafe12216eb3", + "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.15" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.7" + }, + "time": "2025-01-25T19:27:39+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "spatie/array-to-xml", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67", + "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/3.4.0" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-12-16T12:45:15+00:00" + }, + { + "name": "symfony/console", + "version": "v6.4.22", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", + "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.4.22" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-05-07T07:05:04+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^5.4|^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-25T15:07:50+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-25T09:37:31+00:00" + }, + { + "name": "symfony/string", + "version": "v6.4.21", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.4.21" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-18T15:23:29+00:00" + }, + { + "name": "vimeo/psalm", + "version": "6.5.0", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "38fc8444edf0cebc9205296ee6e30e906ade783b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/38fc8444edf0cebc9205296ee6e30e906ade783b", + "reference": "38fc8444edf0cebc9205296ee6e30e906ade783b", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/parallel": "^2.3", + "composer-runtime-api": "^2", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.1", + "felixfbecker/language-server-protocol": "^1.5.3", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^5.0.0", + "php": "~8.1.17 || ~8.2.4 || ~8.3.0 || ~8.4.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^6.0 || ^7.0", + "symfony/filesystem": "^6.0 || ^7.0" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "amphp/phpunit-util": "^3", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", + "dg/bypass-finals": "^1.5", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.19", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^6.0 || ^7.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalm-review", + "psalter" + ], + "type": "project", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev", + "dev-3.x": "3.x-dev", + "dev-4.x": "4.x-dev", + "dev-5.x": "5.x-dev", + "dev-6.x": "6.x-dev", + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown" + }, + { + "name": "Daniil Gentili", + "email": "daniil@daniil.it" + } + ], + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php", + "static analysis" + ], + "support": { + "docs": "https://psalm.dev/docs", + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm" + }, + "time": "2025-02-07T20:42:25+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "platform-overrides": { + "php": "8.1.17" + }, + "plugin-api-version": "2.6.0" +}