From 3dcb6569836beff687455816f8abbd52c6780a22 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Feb 2026 12:05:36 +0100 Subject: [PATCH 01/12] chore(psalm): Remove Signed-off-by: provokateurin --- .github/dependabot.yml | 1 - .github/workflows/psalm.yml | 59 - .php-cs-fixer.dist.php | 1 - LICENSES/AGPL-3.0-only.txt | 235 -- REUSE.toml | 24 - composer.json | 5 +- lib/Controller/DelegationController.php | 1 - lib/Controller/FolderController.php | 3 - lib/DAV/PropFindPlugin.php | 1 - lib/Folder/FolderManager.php | 1 - lib/Mount/GroupFolderNoEncryptionStorage.php | 5 - lib/Versions/ExpireManager.php | 1 - lib/Versions/VersionsBackend.php | 4 - psalm.xml | 138 - rector.php | 3 - tests/ACL/ACLScannerTest.php | 3 - tests/ACL/ACLStorageWrapperTest.php | 1 - .../CircleDestroyedEventListenerTest.php | 1 - tests/Listeners/NodeRenamedListenerTest.php | 1 - tests/phpunit.xml | 1 - tests/psalm-baseline.xml | 85 - tests/psalm-baseline.xml.license | 2 - .../doctrine_dbal_schema_abstractasset.php | 171 - tests/stubs/doctrine_dbal_schema_column.php | 334 -- tests/stubs/doctrine_dbal_schema_schema.php | 315 -- tests/stubs/doctrine_dbal_schema_table.php | 522 --- tests/stubs/icewind_streams_directory.php | 42 - .../icewind_streams_iteratordirectory.php | 85 - .../stubs/icewind_streams_wrapperhandler.php | 49 - tests/stubs/oc.php | 150 - .../oc_appframework_ocs_baseresponse.php | 72 - .../stubs/oc_appframework_ocs_v1response.php | 49 - ...c_appframework_utility_simplecontainer.php | 131 - tests/stubs/oc_core_command_base.php | 103 - .../stubs/oc_db_querybuilder_querybuilder.php | 997 ----- tests/stubs/oc_files_cache_cache.php | 432 --- tests/stubs/oc_files_cache_cacheentry.php | 119 - tests/stubs/oc_files_cache_scanner.php | 221 -- .../oc_files_cache_wrapper_cachejail.php | 251 -- .../oc_files_cache_wrapper_cachewrapper.php | 269 -- tests/stubs/oc_files_filesystem.php | 553 --- tests/stubs/oc_files_mount_mountpoint.php | 156 - tests/stubs/oc_files_node_lazyfolder.php | 502 --- tests/stubs/oc_files_node_node.php | 354 -- ...c_files_objectstore_objectstorescanner.php | 30 - ...c_files_objectstore_objectstorestorage.php | 195 - ...s_objectstore_primaryobjectstoreconfig.php | 86 - tests/stubs/oc_files_setupmanager.php | 98 - tests/stubs/oc_files_storage_common.php | 311 -- tests/stubs/oc_files_storage_local.php | 181 - tests/stubs/oc_files_storage_storage.php | 69 - tests/stubs/oc_files_storage_temporary.php | 33 - tests/stubs/oc_files_storage_wrapper_jail.php | 233 -- ..._files_storage_wrapper_permissionsmask.php | 99 - .../stubs/oc_files_storage_wrapper_quota.php | 80 - .../oc_files_storage_wrapper_wrapper.php | 289 -- tests/stubs/oc_files_view.php | 674 ---- tests/stubs/oc_group_database.php | 211 - tests/stubs/oc_group_manager.php | 214 -- tests/stubs/oc_hooks_basicemitter.php | 15 - tests/stubs/oc_hooks_emitter.php | 40 - tests/stubs/oc_hooks_emittertrait.php | 48 - tests/stubs/oc_hooks_publicemitter.php | 25 - tests/stubs/oc_server.php | 644 ---- tests/stubs/oc_servercontainer.php | 101 - .../oc_settings_authorizedgroupmapper.php | 70 - tests/stubs/oc_user_user.php | 312 -- tests/stubs/oca_circles_circlesmanager.php | 481 --- .../stubs/oca_circles_circlesqueryhelper.php | 105 - .../stubs/oca_circles_db_corequerybuilder.php | 755 ---- ...ca_circles_events_circledestroyedevent.php | 39 - ...ircles_events_circleresultgenericevent.php | 74 - ...les_exceptions_circlenotfoundexception.php | 14 - ...cles_exceptions_federateditemexception.php | 70 - ...eptions_federateditemnotfoundexception.php | 35 - tests/stubs/oca_circles_ientity.php | 56 - tests/stubs/oca_circles_ifederatedmodel.php | 33 - tests/stubs/oca_circles_ifederateduser.php | 117 - tests/stubs/oca_circles_iqueryprobe.php | 102 - tests/stubs/oca_circles_model_circle.php | 670 ---- .../stubs/oca_circles_model_federateduser.php | 309 -- .../stubs/oca_circles_model_managedmodel.php | 38 - tests/stubs/oca_circles_model_member.php | 687 ---- .../oca_circles_model_probes_basicprobe.php | 215 -- .../oca_circles_model_probes_circleprobe.php | 282 -- .../oca_circles_model_probes_memberprobe.php | 122 - ..._circles_tools_db_extendedquerybuilder.php | 719 ---- .../stubs/oca_circles_tools_db_iqueryrow.php | 30 - .../oca_circles_tools_ideserializable.php | 23 - .../oca_dav_connector_sabre_directory.php | 191 - tests/stubs/oca_dav_connector_sabre_node.php | 239 -- .../oca_dav_connector_sabre_principal.php | 183 - ...files_event_loadadditionalscriptsevent.php | 19 - ...ring_event_beforetemplaterenderedevent.php | 46 - tests/stubs/oca_files_trashbin_expiration.php | 60 - ...oca_files_trashbin_trash_itrashbackend.php | 79 - .../oca_files_trashbin_trash_itrashitem.php | 86 - .../oca_files_trashbin_trash_trashitem.php | 172 - tests/stubs/oca_files_versions_expiration.php | 65 - ...ions_versions_ideletableversionbackend.php | 23 - ...les_versions_versions_imetadataversion.php | 35 - ...sions_versions_imetadataversionbackend.php | 31 - ...sions_versions_ineedsyncversionbackend.php | 31 - .../oca_files_versions_versions_iversion.php | 103 - ...iles_versions_versions_iversionbackend.php | 103 - ...ions_versions_iversionsimporterbackend.php | 37 - .../oca_files_versions_versions_version.php | 72 - ...ettings_service_authorizedgroupservice.php | 66 - ...on_completion_completionawareinterface.php | 31 - ...fony_component_console_command_command.php | 442 --- ...symfony_component_console_helper_table.php | 218 -- ..._component_console_input_inputargument.php | 107 - ...component_console_input_inputinterface.php | 180 - ...ny_component_console_input_inputoption.php | 159 - ...mponent_console_output_outputinterface.php | 135 - ..._console_question_confirmationquestion.php | 29 - ...ny_component_console_question_question.php | 207 - tests/stubs/test_testcase.php | 279 -- tests/stubs/test_traits_usertrait.php | 50 - vendor-bin/psalm/composer.json | 11 - vendor-bin/psalm/composer.lock | 3406 ----------------- 121 files changed, 1 insertion(+), 22381 deletions(-) delete mode 100644 .github/workflows/psalm.yml delete mode 100644 LICENSES/AGPL-3.0-only.txt delete mode 100644 psalm.xml delete mode 100644 tests/psalm-baseline.xml delete mode 100644 tests/psalm-baseline.xml.license delete mode 100644 tests/stubs/doctrine_dbal_schema_abstractasset.php delete mode 100644 tests/stubs/doctrine_dbal_schema_column.php delete mode 100644 tests/stubs/doctrine_dbal_schema_schema.php delete mode 100644 tests/stubs/doctrine_dbal_schema_table.php delete mode 100644 tests/stubs/icewind_streams_directory.php delete mode 100644 tests/stubs/icewind_streams_iteratordirectory.php delete mode 100644 tests/stubs/icewind_streams_wrapperhandler.php delete mode 100644 tests/stubs/oc.php delete mode 100644 tests/stubs/oc_appframework_ocs_baseresponse.php delete mode 100644 tests/stubs/oc_appframework_ocs_v1response.php delete mode 100644 tests/stubs/oc_appframework_utility_simplecontainer.php delete mode 100644 tests/stubs/oc_core_command_base.php delete mode 100644 tests/stubs/oc_db_querybuilder_querybuilder.php delete mode 100644 tests/stubs/oc_files_cache_cache.php delete mode 100644 tests/stubs/oc_files_cache_cacheentry.php delete mode 100644 tests/stubs/oc_files_cache_scanner.php delete mode 100644 tests/stubs/oc_files_cache_wrapper_cachejail.php delete mode 100644 tests/stubs/oc_files_cache_wrapper_cachewrapper.php delete mode 100644 tests/stubs/oc_files_filesystem.php delete mode 100644 tests/stubs/oc_files_mount_mountpoint.php delete mode 100644 tests/stubs/oc_files_node_lazyfolder.php delete mode 100644 tests/stubs/oc_files_node_node.php delete mode 100644 tests/stubs/oc_files_objectstore_objectstorescanner.php delete mode 100644 tests/stubs/oc_files_objectstore_objectstorestorage.php delete mode 100644 tests/stubs/oc_files_objectstore_primaryobjectstoreconfig.php delete mode 100644 tests/stubs/oc_files_setupmanager.php delete mode 100644 tests/stubs/oc_files_storage_common.php delete mode 100644 tests/stubs/oc_files_storage_local.php delete mode 100644 tests/stubs/oc_files_storage_storage.php delete mode 100644 tests/stubs/oc_files_storage_temporary.php delete mode 100644 tests/stubs/oc_files_storage_wrapper_jail.php delete mode 100644 tests/stubs/oc_files_storage_wrapper_permissionsmask.php delete mode 100644 tests/stubs/oc_files_storage_wrapper_quota.php delete mode 100644 tests/stubs/oc_files_storage_wrapper_wrapper.php delete mode 100644 tests/stubs/oc_files_view.php delete mode 100644 tests/stubs/oc_group_database.php delete mode 100644 tests/stubs/oc_group_manager.php delete mode 100644 tests/stubs/oc_hooks_basicemitter.php delete mode 100644 tests/stubs/oc_hooks_emitter.php delete mode 100644 tests/stubs/oc_hooks_emittertrait.php delete mode 100644 tests/stubs/oc_hooks_publicemitter.php delete mode 100644 tests/stubs/oc_server.php delete mode 100644 tests/stubs/oc_servercontainer.php delete mode 100644 tests/stubs/oc_settings_authorizedgroupmapper.php delete mode 100644 tests/stubs/oc_user_user.php delete mode 100644 tests/stubs/oca_circles_circlesmanager.php delete mode 100644 tests/stubs/oca_circles_circlesqueryhelper.php delete mode 100644 tests/stubs/oca_circles_db_corequerybuilder.php delete mode 100644 tests/stubs/oca_circles_events_circledestroyedevent.php delete mode 100644 tests/stubs/oca_circles_events_circleresultgenericevent.php delete mode 100644 tests/stubs/oca_circles_exceptions_circlenotfoundexception.php delete mode 100644 tests/stubs/oca_circles_exceptions_federateditemexception.php delete mode 100644 tests/stubs/oca_circles_exceptions_federateditemnotfoundexception.php delete mode 100644 tests/stubs/oca_circles_ientity.php delete mode 100644 tests/stubs/oca_circles_ifederatedmodel.php delete mode 100644 tests/stubs/oca_circles_ifederateduser.php delete mode 100644 tests/stubs/oca_circles_iqueryprobe.php delete mode 100644 tests/stubs/oca_circles_model_circle.php delete mode 100644 tests/stubs/oca_circles_model_federateduser.php delete mode 100644 tests/stubs/oca_circles_model_managedmodel.php delete mode 100644 tests/stubs/oca_circles_model_member.php delete mode 100644 tests/stubs/oca_circles_model_probes_basicprobe.php delete mode 100644 tests/stubs/oca_circles_model_probes_circleprobe.php delete mode 100644 tests/stubs/oca_circles_model_probes_memberprobe.php delete mode 100644 tests/stubs/oca_circles_tools_db_extendedquerybuilder.php delete mode 100644 tests/stubs/oca_circles_tools_db_iqueryrow.php delete mode 100644 tests/stubs/oca_circles_tools_ideserializable.php delete mode 100644 tests/stubs/oca_dav_connector_sabre_directory.php delete mode 100644 tests/stubs/oca_dav_connector_sabre_node.php delete mode 100644 tests/stubs/oca_dav_connector_sabre_principal.php delete mode 100644 tests/stubs/oca_files_event_loadadditionalscriptsevent.php delete mode 100644 tests/stubs/oca_files_sharing_event_beforetemplaterenderedevent.php delete mode 100644 tests/stubs/oca_files_trashbin_expiration.php delete mode 100644 tests/stubs/oca_files_trashbin_trash_itrashbackend.php delete mode 100644 tests/stubs/oca_files_trashbin_trash_itrashitem.php delete mode 100644 tests/stubs/oca_files_trashbin_trash_trashitem.php delete mode 100644 tests/stubs/oca_files_versions_expiration.php delete mode 100644 tests/stubs/oca_files_versions_versions_ideletableversionbackend.php delete mode 100644 tests/stubs/oca_files_versions_versions_imetadataversion.php delete mode 100644 tests/stubs/oca_files_versions_versions_imetadataversionbackend.php delete mode 100644 tests/stubs/oca_files_versions_versions_ineedsyncversionbackend.php delete mode 100644 tests/stubs/oca_files_versions_versions_iversion.php delete mode 100644 tests/stubs/oca_files_versions_versions_iversionbackend.php delete mode 100644 tests/stubs/oca_files_versions_versions_iversionsimporterbackend.php delete mode 100644 tests/stubs/oca_files_versions_versions_version.php delete mode 100644 tests/stubs/oca_settings_service_authorizedgroupservice.php delete mode 100644 tests/stubs/stecman_component_symfony_console_bashcompletion_completion_completionawareinterface.php delete mode 100644 tests/stubs/symfony_component_console_command_command.php delete mode 100644 tests/stubs/symfony_component_console_helper_table.php delete mode 100644 tests/stubs/symfony_component_console_input_inputargument.php delete mode 100644 tests/stubs/symfony_component_console_input_inputinterface.php delete mode 100644 tests/stubs/symfony_component_console_input_inputoption.php delete mode 100644 tests/stubs/symfony_component_console_output_outputinterface.php delete mode 100644 tests/stubs/symfony_component_console_question_confirmationquestion.php delete mode 100644 tests/stubs/symfony_component_console_question_question.php delete mode 100644 tests/stubs/test_testcase.php delete mode 100644 tests/stubs/test_traits_usertrait.php delete mode 100644 vendor-bin/psalm/composer.json delete mode 100644 vendor-bin/psalm/composer.lock diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a3c5ddb51..e548184cf 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,7 +16,6 @@ updates: - /vendor-bin/cs-fixer - /vendor-bin/openapi-extractor - /vendor-bin/phpunit - - /vendor-bin/psalm - /vendor-bin/rector schedule: interval: weekly diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml deleted file mode 100644 index 9a0d2efef..000000000 --- a/.github/workflows/psalm.yml +++ /dev/null @@ -1,59 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Static analysis - -on: pull_request - -concurrency: - group: psalm-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - static-analysis: - runs-on: ubuntu-latest - - name: static-psalm-analysis - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Get php version - id: versions - uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 - - - name: Check enforcement of minimum PHP version ${{ steps.versions.outputs.php-min }} in psalm.xml - run: grep 'phpVersion="${{ steps.versions.outputs.php-min }}' psalm.xml - - - name: Set up php${{ steps.versions.outputs.php-available }} - uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1 - with: - php-version: ${{ steps.versions.outputs.php-available }} - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3 - ini-values: disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Install dependencies - run: | - composer remove nextcloud/ocp --dev --no-scripts - composer i - - - name: Install nextcloud/ocp - run: composer require --dev nextcloud/ocp:dev-${{ steps.versions.outputs.branches-max }} --ignore-platform-reqs --with-dependencies - - - name: Run coding standards check - run: composer run psalm -- --threads=1 --monochrome --no-progress --output-format=github diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index d93550682..166e1a0ab 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -14,7 +14,6 @@ ->getFinder() ->ignoreVCSIgnored(true) ->notPath('build') - ->notPath('tests/stubs') ->notPath('l10n') ->notPath('src') ->notPath('vendor') diff --git a/LICENSES/AGPL-3.0-only.txt b/LICENSES/AGPL-3.0-only.txt deleted file mode 100644 index 0c97efd25..000000000 --- a/LICENSES/AGPL-3.0-only.txt +++ /dev/null @@ -1,235 +0,0 @@ -GNU AFFERO GENERAL PUBLIC LICENSE -Version 3, 19 November 2007 - -Copyright (C) 2007 Free Software Foundation, Inc. - -Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - - Preamble - -The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. - -The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. - -When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. - -Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. - -A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. - -The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. - -An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. - -The precise terms and conditions for copying, distribution and modification follow. - - TERMS AND CONDITIONS - -0. Definitions. - -"This License" refers to version 3 of the GNU Affero General Public License. - -"Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. - -"The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. - -To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. - -A "covered work" means either the unmodified Program or a work based on the Program. - -To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. - -To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. - -An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. - -1. Source Code. -The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. - -A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. - -The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. - -The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those -subprograms and other parts of the work. - -The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. - -The Corresponding Source for a work in source code form is that same work. - -2. Basic Permissions. -All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. - -You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. - -Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. - -3. Protecting Users' Legal Rights From Anti-Circumvention Law. -No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. - -When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. - -4. Conveying Verbatim Copies. -You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. - -You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. - -5. Conveying Modified Source Versions. -You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". - - c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. - -A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. - -6. Conveying Non-Source Forms. -You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: - - a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. - - d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. - -A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. - -A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. - -"Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. - -If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). - -The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. - -Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. - -7. Additional Terms. -"Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. - -When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. - -Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or authors of the material; or - - e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. - -All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. - -If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. - -Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. - -8. Termination. - -You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). - -However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. - -Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. - -Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. - -9. Acceptance Not Required for Having Copies. - -You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. - -10. Automatic Licensing of Downstream Recipients. - -Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. - -An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. - -11. Patents. - -A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". - -A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. - -In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. - -If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. - -If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. - -A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. - -Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. - -12. No Surrender of Others' Freedom. - -If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. - -13. Remote Network Interaction; Use with the GNU General Public License. - -Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. - -Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. - -14. Revised Versions of this License. - -The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. - -If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. - -Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. - -15. Disclaimer of Warranty. - -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -16. Limitation of Liability. - -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -17. Interpretation of Sections 15 and 16. - -If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. - -END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. - -To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . diff --git a/REUSE.toml b/REUSE.toml index acdcf1082..85f0d767b 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -53,30 +53,6 @@ precedence = "aggregate" SPDX-FileCopyrightText = "2018-2024 Google LLC" SPDX-License-Identifier = "Apache-2.0" -[[annotations]] -path = ["tests/stubs/doctrine_dbal_**"] -precedence = "aggregate" -SPDX-FileCopyrightText = "none" -SPDX-License-Identifier = "MIT" - -[[annotations]] -path = ["tests/stubs/symfony_component_**"] -precedence = "aggregate" -SPDX-FileCopyrightText = "none" -SPDX-License-Identifier = "MIT" - -[[annotations]] -path = ["tests/stubs/stecman_component_**"] -precedence = "aggregate" -SPDX-FileCopyrightText = "none" -SPDX-License-Identifier = "MIT" - -[[annotations]] -path = ["tests/stubs/icewind_streams_**"] -precedence = "aggregate" -SPDX-FileCopyrightText = "none" -SPDX-License-Identifier = "MIT" - [[annotations]] path = ["openapi.json", "src/types/openapi/openapi.ts"] precedence = "aggregate" diff --git a/composer.json b/composer.json index 2f232a488..69b7b9299 100644 --- a/composer.json +++ b/composer.json @@ -12,12 +12,9 @@ "post-install-cmd": [ "@composer bin all install --ansi" ], - "lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './tests/stubs/*' -print0 | xargs -0 -n1 php -l", + "lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -print0 | xargs -0 -n1 php -l", "cs:check": "php-cs-fixer fix --dry-run --diff", "cs:fix": "php-cs-fixer fix", - "psalm": "psalm --threads=$(nproc) --no-cache", - "psalm:update-baseline": "psalm --threads=$(nproc) --no-cache --update-baseline", - "psalm:fix": "psalm --no-cache --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType,MissingOverrideAttribute", "test:unit": "phpunit -c tests/phpunit.xml", "test:unit:coverage": "XDEBUG_MODE=coverage phpunit -c tests/phpunit.xml", "rector": "rector && composer cs:fix", diff --git a/lib/Controller/DelegationController.php b/lib/Controller/DelegationController.php index b09434269..3f072ef43 100644 --- a/lib/Controller/DelegationController.php +++ b/lib/Controller/DelegationController.php @@ -96,7 +96,6 @@ public function getAllCircles(): DataResponse { // As admin, get all circles, // As non-admin, only returns circles current user is members of. - /** @psalm-suppress PossiblyNullReference current user cannot be null */ if ($this->groupManager->isAdmin($this->userSession->getUser()->getUID())) { $circlesManager->startSuperSession(); } else { diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index 8b411a82c..84df73abc 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -113,17 +113,14 @@ private function formatFolder(FolderWithMappingsAndCache $folder): array { #[NoAdminRequired] #[FrontpageRoute(verb: 'GET', url: '/folders')] public function getFolders(bool $applicable = false, int $offset = 0, ?int $limit = null, string $orderBy = 'mount_point', string $order = 'asc'): DataResponse { - /** @psalm-suppress DocblockTypeContradiction */ if ($limit !== null && $limit <= 0) { throw new OCSBadRequestException('The limit must be greater than 0.'); } - /** @psalm-suppress DocblockTypeContradiction */ if (!in_array($orderBy, ['mount_point', 'quota', 'groups', 'acl'], true)) { throw new OCSBadRequestException('The orderBy is not allowed.'); } - /** @psalm-suppress DocblockTypeContradiction */ if (!in_array($order, ['asc', 'desc'], true)) { throw new OCSBadRequestException('The order is not allowed.'); } diff --git a/lib/DAV/PropFindPlugin.php b/lib/DAV/PropFindPlugin.php index 0444bc6f4..da707b324 100644 --- a/lib/DAV/PropFindPlugin.php +++ b/lib/DAV/PropFindPlugin.php @@ -50,7 +50,6 @@ public function propFind(PropFind $propFind, INode $node): void { if ($node instanceof GroupFolderNode) { $propFind->handle( self::MOUNT_POINT_PROPERTYNAME, - /** @psalm-suppress PossiblyNullReference Null already checked above */ fn () => $this->userFolder->getRelativePath($node->getFileInfo()->getMountPoint()->getMountPoint()) ); $propFind->handle( diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php index 18141c399..5ceb929ef 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -712,7 +712,6 @@ public function getFoldersFromCircleMemberships(IUser $user, ?int $folderId = nu $query->andWhere($query->expr()->eq('f.mount_point', $query->createNamedParameter($path))); } - /** @psalm-suppress RedundantCondition */ if (method_exists($queryHelper, 'limitToMemberships')) { $queryHelper->limitToMemberships('a', 'circle_id', $federatedUser); } else { diff --git a/lib/Mount/GroupFolderNoEncryptionStorage.php b/lib/Mount/GroupFolderNoEncryptionStorage.php index 3cad1f7e6..9b1dbd18a 100644 --- a/lib/Mount/GroupFolderNoEncryptionStorage.php +++ b/lib/Mount/GroupFolderNoEncryptionStorage.php @@ -10,10 +10,5 @@ use OCP\Files\Storage\IDisableEncryptionStorage; -/** - * @psalm-suppress UnimplementedInterfaceMethod - * Psalm gets confused about missing methods, but those are implemented in OC\Files\Storage\Wrapper\Wrapper, - * so this suppression is fine and necessary as there is nothing wrong. - */ class GroupFolderNoEncryptionStorage extends GroupFolderStorage implements IDisableEncryptionStorage { } diff --git a/lib/Versions/ExpireManager.php b/lib/Versions/ExpireManager.php index 161225f3c..567d78756 100644 --- a/lib/Versions/ExpireManager.php +++ b/lib/Versions/ExpireManager.php @@ -85,7 +85,6 @@ protected function getAutoExpireList(int $time, array $versions): array { $newInterval = false; // version checked so we can move to the next one } else { // time to move on to the next interval $interval++; - /** @psalm-suppress InvalidArrayOffset We know that $interval is <= 6 thanks to the -1 intervalEndsAfter in the last step */ $step = self::MAX_VERSIONS_PER_INTERVAL[$interval]['step']; $nextVersion = $prevTimestamp - $step; if (self::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'] === -1) { diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index 18c94e1a7..045035748 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -406,10 +406,6 @@ private function currentUserHasPermissions(FileInfo $sourceFile, int $permission return ($sourceFile->getPermissions() & $permissions) === $permissions; } - /** - * @inheritdoc - * @psalm-suppress MethodSignatureMismatch - The signature of the method is correct, but psalm somehow can't understand it - */ public function importVersionsForFile(IUser $user, Node $source, Node $target, array $versions): void { if (!$target->getStorage()->instanceOfStorage(GroupFolderStorage::class)) { return; diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index 6d12b4b3e..000000000 --- a/psalm.xml +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/rector.php b/rector.php index 484e82f3b..a39ad56a4 100644 --- a/rector.php +++ b/rector.php @@ -13,9 +13,6 @@ __DIR__ . '/lib', __DIR__ . '/tests', ]) - ->withSkip([ - __DIR__ . '/tests/stubs', - ]) ->withPreparedSets( deadCode: true, typeDeclarations: true, diff --git a/tests/ACL/ACLScannerTest.php b/tests/ACL/ACLScannerTest.php index 003630db7..e72ea020c 100644 --- a/tests/ACL/ACLScannerTest.php +++ b/tests/ACL/ACLScannerTest.php @@ -43,7 +43,6 @@ public function testScanAclStorage(): void { $cache->calculateFolderSize('foo/bar'); $cache->calculateFolderSize('foo'); - /** @psalm-suppress PossiblyFalseReference */ $this->assertEquals(-1, $cache->get('foo/bar')->getSize()); $acls = $this->getAclManager([ @@ -63,10 +62,8 @@ public function testScanAclStorage(): void { $aclCache = $aclStorage->getCache(); $scanner->scan(''); - /** @psalm-suppress PossiblyFalseReference */ $this->assertEquals(0, $cache->get('foo/bar')->getSize()); - /** @psalm-suppress PossiblyFalseReference */ $this->assertEquals(31, $cache->get('foo/bar')->getPermissions()); $this->assertEquals(false, $aclCache->get('foo/bar')); } diff --git a/tests/ACL/ACLStorageWrapperTest.php b/tests/ACL/ACLStorageWrapperTest.php index 0e5d75810..6b8b48cbe 100644 --- a/tests/ACL/ACLStorageWrapperTest.php +++ b/tests/ACL/ACLStorageWrapperTest.php @@ -62,7 +62,6 @@ public function testOpenDir(): void { $dh = $this->storage->opendir('foo'); $result = []; - /** @psalm-suppress PossiblyFalseArgument */ while ($file = readdir($dh)) { $result[] = $file; } diff --git a/tests/Listeners/CircleDestroyedEventListenerTest.php b/tests/Listeners/CircleDestroyedEventListenerTest.php index 38ac59cb6..5cac85aea 100644 --- a/tests/Listeners/CircleDestroyedEventListenerTest.php +++ b/tests/Listeners/CircleDestroyedEventListenerTest.php @@ -37,7 +37,6 @@ public function testHandleInvalid(): void { ->expects($this->never()) ->method('deleteCircle'); - /** @psalm-suppress InvalidArgument on purpose */ $this->listener->handle($event); } diff --git a/tests/Listeners/NodeRenamedListenerTest.php b/tests/Listeners/NodeRenamedListenerTest.php index a7f97b49d..930b9f3eb 100644 --- a/tests/Listeners/NodeRenamedListenerTest.php +++ b/tests/Listeners/NodeRenamedListenerTest.php @@ -86,7 +86,6 @@ public function testHandleInvalid(): void { ->expects($this->never()) ->method('getSource'); - /** @psalm-suppress InvalidArgument on purpose */ $this->listener->handle($event); } diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 98c505a2e..5240c842d 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -16,7 +16,6 @@ . - ./stubs/ diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml deleted file mode 100644 index 5c6ac16e5..000000000 --- a/tests/psalm-baseline.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/psalm-baseline.xml.license b/tests/psalm-baseline.xml.license deleted file mode 100644 index 7e235b600..000000000 --- a/tests/psalm-baseline.xml.license +++ /dev/null @@ -1,2 +0,0 @@ -SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors -SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/tests/stubs/doctrine_dbal_schema_abstractasset.php b/tests/stubs/doctrine_dbal_schema_abstractasset.php deleted file mode 100644 index 36e8fdb9b..000000000 --- a/tests/stubs/doctrine_dbal_schema_abstractasset.php +++ /dev/null @@ -1,171 +0,0 @@ - Table($tableName)); if you want to rename the table, you have to make sure this does not get - * recreated during schema migration. - */ -abstract class AbstractAsset -{ - /** @var string */ - protected $_name = ''; - - /** - * Namespace of the asset. If none isset the default namespace is assumed. - * - * @var string|null - */ - protected $_namespace; - - /** @var bool */ - protected $_quoted = false; - - /** - * Sets the name of this asset. - * - * @param string $name - * - * @return void - */ - protected function _setName($name) - { - } - - /** - * Is this asset in the default namespace? - * - * @param string $defaultNamespaceName - * - * @return bool - */ - public function isInDefaultNamespace($defaultNamespaceName) - { - } - - /** - * Gets the namespace name of this asset. - * - * If NULL is returned this means the default namespace is used. - * - * @return string|null - */ - public function getNamespaceName() - { - } - - /** - * The shortest name is stripped of the default namespace. All other - * namespaced elements are returned as full-qualified names. - * - * @param string|null $defaultNamespaceName - * - * @return string - */ - public function getShortestName($defaultNamespaceName) - { - } - - /** - * The normalized name is full-qualified and lower-cased. Lower-casing is - * actually wrong, but we have to do it to keep our sanity. If you are - * using database objects that only differentiate in the casing (FOO vs - * Foo) then you will NOT be able to use Doctrine Schema abstraction. - * - * Every non-namespaced element is prefixed with the default namespace - * name which is passed as argument to this method. - * - * @deprecated Use {@see getNamespaceName()} and {@see getName()} instead. - * - * @param string $defaultNamespaceName - * - * @return string - */ - public function getFullQualifiedName($defaultNamespaceName) - { - } - - /** - * Checks if this asset's name is quoted. - * - * @return bool - */ - public function isQuoted() - { - } - - /** - * Checks if this identifier is quoted. - * - * @param string $identifier - * - * @return bool - */ - protected function isIdentifierQuoted($identifier) - { - } - - /** - * Trim quotes from the identifier. - * - * @param string $identifier - * - * @return string - */ - protected function trimQuotes($identifier) - { - } - - /** - * Returns the name of this schema asset. - * - * @return string - */ - public function getName() - { - } - - /** - * Gets the quoted representation of this asset but only if it was defined with one. Otherwise - * return the plain unquoted value as inserted. - * - * @return string - */ - public function getQuotedName(AbstractPlatform $platform) - { - } - - /** - * Generates an identifier from a list of column names obeying a certain string length. - * - * This is especially important for Oracle, since it does not allow identifiers larger than 30 chars, - * however building idents automatically for foreign keys, composite keys or such can easily create - * very long names. - * - * @param string[] $columnNames - * @param string $prefix - * @param int $maxSize - * - * @return string - */ - protected function _generateIdentifierName($columnNames, $prefix = '', $maxSize = 30) - { - } -} diff --git a/tests/stubs/doctrine_dbal_schema_column.php b/tests/stubs/doctrine_dbal_schema_column.php deleted file mode 100644 index 425c83457..000000000 --- a/tests/stubs/doctrine_dbal_schema_column.php +++ /dev/null @@ -1,334 +0,0 @@ - - * - * @throws Exception - */ - public function toSql(AbstractPlatform $platform) - { - } - - /** - * Return an array of necessary SQL queries to drop the schema on the given platform. - * - * @return list - * - * @throws Exception - */ - public function toDropSql(AbstractPlatform $platform) - { - } - - /** - * @deprecated - * - * @return string[] - * - * @throws SchemaException - */ - public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform) - { - } - - /** - * @deprecated - * - * @return string[] - * - * @throws SchemaException - */ - public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform) - { - } - - /** - * @deprecated - * - * @return void - */ - public function visit(Visitor $visitor) - { - } - - /** - * Cloning a Schema triggers a deep clone of all related assets. - * - * @return void - */ - public function __clone() - { - } -} diff --git a/tests/stubs/doctrine_dbal_schema_table.php b/tests/stubs/doctrine_dbal_schema_table.php deleted file mode 100644 index 794175a43..000000000 --- a/tests/stubs/doctrine_dbal_schema_table.php +++ /dev/null @@ -1,522 +0,0 @@ - [], - ]; - - /** @var SchemaConfig|null */ - protected $_schemaConfig; - - /** - * @param Column[] $columns - * @param Index[] $indexes - * @param UniqueConstraint[] $uniqueConstraints - * @param ForeignKeyConstraint[] $fkConstraints - * @param mixed[] $options - * - * @throws SchemaException - * @throws Exception - */ - public function __construct(string $name, array $columns = [], array $indexes = [], array $uniqueConstraints = [], array $fkConstraints = [], array $options = []) - { - } - - /** @return void */ - public function setSchemaConfig(SchemaConfig $schemaConfig) - { - } - - /** @return int */ - protected function _getMaxIdentifierLength() - { - } - - /** - * Sets the Primary Key. - * - * @param string[] $columnNames - * @param string|false $indexName - * - * @return self - * - * @throws SchemaException - */ - public function setPrimaryKey(array $columnNames, $indexName = false) - { - } - - /** - * @param string[] $columnNames - * @param string[] $flags - * @param mixed[] $options - * - * @return self - * - * @throws SchemaException - */ - public function addIndex(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) - { - } - - /** - * @param string[] $columnNames - * @param string[] $flags - * @param mixed[] $options - * - * @return self - */ - public function addUniqueConstraint(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []): Table - { - } - - /** - * Drops the primary key from this table. - * - * @return void - * - * @throws SchemaException - */ - public function dropPrimaryKey() - { - } - - /** - * Drops an index from this table. - * - * @param string $name The index name. - * - * @return void - * - * @throws SchemaException If the index does not exist. - */ - public function dropIndex($name) - { - } - - /** - * @param string[] $columnNames - * @param string|null $indexName - * @param mixed[] $options - * - * @return self - * - * @throws SchemaException - */ - public function addUniqueIndex(array $columnNames, $indexName = null, array $options = []) - { - } - - /** - * Renames an index. - * - * @param string $oldName The name of the index to rename from. - * @param string|null $newName The name of the index to rename to. - * If null is given, the index name will be auto-generated. - * - * @return self This table instance. - * - * @throws SchemaException If no index exists for the given current name - * or if an index with the given new name already exists on this table. - */ - public function renameIndex($oldName, $newName = null) - { - } - - /** - * Checks if an index begins in the order of the given columns. - * - * @param string[] $columnNames - * - * @return bool - */ - public function columnsAreIndexed(array $columnNames) - { - } - - /** - * @param string $name - * @param string $typeName - * @param mixed[] $options - * - * @return Column - * - * @throws SchemaException - */ - public function addColumn($name, $typeName, array $options = []) - { - } - - /** - * Change Column Details. - * - * @deprecated Use {@link modifyColumn()} instead. - * - * @param string $name - * @param mixed[] $options - * - * @return self - * - * @throws SchemaException - */ - public function changeColumn($name, array $options) - { - } - - /** - * @param string $name - * @param mixed[] $options - * - * @return self - * - * @throws SchemaException - */ - public function modifyColumn($name, array $options) - { - } - - /** - * Drops a Column from the Table. - * - * @param string $name - * - * @return self - */ - public function dropColumn($name) - { - } - - /** - * Adds a foreign key constraint. - * - * Name is inferred from the local columns. - * - * @param Table|string $foreignTable Table schema instance or table name - * @param string[] $localColumnNames - * @param string[] $foreignColumnNames - * @param mixed[] $options - * @param string|null $name - * - * @return self - * - * @throws SchemaException - */ - public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $name = null) - { - } - - /** - * @param string $name - * @param mixed $value - * - * @return self - */ - public function addOption($name, $value) - { - } - - /** - * @return void - * - * @throws SchemaException - */ - protected function _addColumn(Column $column) - { - } - - /** - * Adds an index to the table. - * - * @return self - * - * @throws SchemaException - */ - protected function _addIndex(Index $indexCandidate) - { - } - - /** @return self */ - protected function _addUniqueConstraint(UniqueConstraint $constraint): Table - { - } - - /** @return self */ - protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) - { - } - - /** - * Returns whether this table has a foreign key constraint with the given name. - * - * @param string $name - * - * @return bool - */ - public function hasForeignKey($name) - { - } - - /** - * Returns the foreign key constraint with the given name. - * - * @param string $name The constraint name. - * - * @return ForeignKeyConstraint - * - * @throws SchemaException If the foreign key does not exist. - */ - public function getForeignKey($name) - { - } - - /** - * Removes the foreign key constraint with the given name. - * - * @param string $name The constraint name. - * - * @return void - * - * @throws SchemaException - */ - public function removeForeignKey($name) - { - } - - /** - * Returns whether this table has a unique constraint with the given name. - */ - public function hasUniqueConstraint(string $name): bool - { - } - - /** - * Returns the unique constraint with the given name. - * - * @throws SchemaException If the unique constraint does not exist. - */ - public function getUniqueConstraint(string $name): UniqueConstraint - { - } - - /** - * Removes the unique constraint with the given name. - * - * @throws SchemaException If the unique constraint does not exist. - */ - public function removeUniqueConstraint(string $name): void - { - } - - /** - * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest) - * - * @return Column[] - */ - public function getColumns() - { - } - - /** - * Returns the foreign key columns - * - * @deprecated Use {@see getForeignKey()} and {@see ForeignKeyConstraint::getLocalColumns()} instead. - * - * @return Column[] - */ - public function getForeignKeyColumns() - { - } - - /** - * Returns whether this table has a Column with the given name. - * - * @param string $name The column name. - * - * @return bool - */ - public function hasColumn($name) - { - } - - /** - * Returns the Column with the given name. - * - * @param string $name The column name. - * - * @return Column - * - * @throws SchemaException If the column does not exist. - */ - public function getColumn($name) - { - } - - /** - * Returns the primary key. - * - * @return Index|null The primary key, or null if this Table has no primary key. - */ - public function getPrimaryKey() - { - } - - /** - * Returns the primary key columns. - * - * @deprecated Use {@see getPrimaryKey()} and {@see Index::getColumns()} instead. - * - * @return Column[] - * - * @throws Exception - */ - public function getPrimaryKeyColumns() - { - } - - /** - * Returns whether this table has a primary key. - * - * @deprecated Use {@see getPrimaryKey()} instead. - * - * @return bool - */ - public function hasPrimaryKey() - { - } - - /** - * Returns whether this table has an Index with the given name. - * - * @param string $name The index name. - * - * @return bool - */ - public function hasIndex($name) - { - } - - /** - * Returns the Index with the given name. - * - * @param string $name The index name. - * - * @return Index - * - * @throws SchemaException If the index does not exist. - */ - public function getIndex($name) - { - } - - /** @return Index[] */ - public function getIndexes() - { - } - - /** - * Returns the unique constraints. - * - * @return UniqueConstraint[] - */ - public function getUniqueConstraints(): array - { - } - - /** - * Returns the foreign key constraints. - * - * @return ForeignKeyConstraint[] - */ - public function getForeignKeys() - { - } - - /** - * @param string $name - * - * @return bool - */ - public function hasOption($name) - { - } - - /** - * @param string $name - * - * @return mixed - */ - public function getOption($name) - { - } - - /** @return mixed[] */ - public function getOptions() - { - } - - /** - * @deprecated - * - * @return void - * - * @throws SchemaException - */ - public function visit(Visitor $visitor) - { - } - - /** - * Clone of a Table triggers a deep clone of all affected assets. - * - * @return void - */ - public function __clone() - { - } - - public function setComment(?string $comment): self - { - } - - public function getComment(): ?string - { - } -} diff --git a/tests/stubs/icewind_streams_directory.php b/tests/stubs/icewind_streams_directory.php deleted file mode 100644 index 50ec2cc6c..000000000 --- a/tests/stubs/icewind_streams_directory.php +++ /dev/null @@ -1,42 +0,0 @@ - - * SPDX-License-Identifier: MIT - */ - -namespace Icewind\Streams; - -/** - * Interface for stream wrappers that implements a directory - */ -interface Directory { - /** - * @param string $path - * @param array $options - * @return bool - */ - public function dir_opendir($path, $options) - { - } - - /** - * @return string|bool - */ - public function dir_readdir() - { - } - - /** - * @return bool - */ - public function dir_closedir() - { - } - - /** - * @return bool - */ - public function dir_rewinddir() - { - } -} diff --git a/tests/stubs/icewind_streams_iteratordirectory.php b/tests/stubs/icewind_streams_iteratordirectory.php deleted file mode 100644 index 32f3198fe..000000000 --- a/tests/stubs/icewind_streams_iteratordirectory.php +++ /dev/null @@ -1,85 +0,0 @@ - - * SPDX-License-Identifier: MIT - */ - -namespace Icewind\Streams; - -/** - * Create a directory handle from an iterator or array - * - * The following options should be passed in the context when opening the stream - * [ - * 'dir' => [ - * 'array' => string[] - * 'iterator' => \Iterator - * ] - * ] - * - * Either 'array' or 'iterator' need to be set, if both are set, 'iterator' takes preference - */ -class IteratorDirectory extends WrapperHandler implements Directory { - /** - * @var resource - */ - public $context; - - /** - * @var \Iterator - */ - protected $iterator; - - /** - * Load the source from the stream context and return the context options - * - * @param string $name - * @return array - * @throws \BadMethodCallException - */ - protected function loadContext($name = null) - { - } - - /** - * @param string $path - * @param array $options - * @return bool - */ - public function dir_opendir($path, $options) - { - } - - /** - * @return string|bool - */ - public function dir_readdir() - { - } - - /** - * @return bool - */ - public function dir_closedir() - { - } - - /** - * @return bool - */ - public function dir_rewinddir() - { - } - - /** - * Creates a directory handle from the provided array or iterator - * - * @param \Iterator | array $source - * @return resource|false - * - * @throws \BadMethodCallException - */ - public static function wrap($source) - { - } -} diff --git a/tests/stubs/icewind_streams_wrapperhandler.php b/tests/stubs/icewind_streams_wrapperhandler.php deleted file mode 100644 index 407bc7b9c..000000000 --- a/tests/stubs/icewind_streams_wrapperhandler.php +++ /dev/null @@ -1,49 +0,0 @@ - - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -namespace Icewind\Streams; - -class WrapperHandler { - /** @var resource $context */ - protected $context; - - const NO_SOURCE_DIR = 1; - - /** - * get the protocol name that is generated for the class - * @param string|null $class - * @return string - */ - public static function getProtocol($class = null) - { - } - - /** - * @param resource|int $source - * @param resource|array $context - * @param string|null $protocol deprecated, protocol is now automatically generated - * @param string|null $class deprecated, class is now automatically generated - * @return resource|false - */ - protected static function wrapSource($source, $context = [], $protocol = null, $class = null, $mode = 'r+') - { - } - - protected static function isDirectoryHandle($resource) - { - } - - /** - * Load the source from the stream context and return the context options - * - * @param string|null $name if not set, the generated protocol name is used - * @return array - * @throws \BadMethodCallException - */ - protected function loadContext($name = null) - { - } -} diff --git a/tests/stubs/oc.php b/tests/stubs/oc.php deleted file mode 100644 index 4216334cb..000000000 --- a/tests/stubs/oc.php +++ /dev/null @@ -1,150 +0,0 @@ - - * @template-extends Response> - */ -abstract class BaseResponse extends Response { - /** @var array */ - protected $data; - - /** @var string */ - protected $format; - - /** @var ?string */ - protected $statusMessage; - - /** @var ?int */ - protected $itemsCount; - - /** @var ?int */ - protected $itemsPerPage; - - /** - * BaseResponse constructor. - * - * @param DataResponse $dataResponse - * @param string $format - * @param string|null $statusMessage - * @param int|null $itemsCount - * @param int|null $itemsPerPage - */ - public function __construct(DataResponse $dataResponse, $format = 'xml', $statusMessage = null, $itemsCount = null, $itemsPerPage = null) - { - } - - /** - * @param array $meta - * @return string - */ - protected function renderResult(array $meta): string - { - } - - /** - * @psalm-taint-escape has_quotes - * @psalm-taint-escape html - */ - protected function toJson(array $array): string - { - } - - protected function toXML(array $array, \XMLWriter $writer): void - { - } - - public function getOCSStatus() - { - } -} diff --git a/tests/stubs/oc_appframework_ocs_v1response.php b/tests/stubs/oc_appframework_ocs_v1response.php deleted file mode 100644 index b9ccfecfc..000000000 --- a/tests/stubs/oc_appframework_ocs_v1response.php +++ /dev/null @@ -1,49 +0,0 @@ - - * @template-extends BaseResponse> - */ -class V1Response extends BaseResponse { - /** - * The V1 endpoint has very limited http status codes basically everything - * is status 200 except 401 - * - * @return Http::STATUS_* - */ - public function getStatus() - { - } - - /** - * In v1 all OK is 100 - * - * @return int - */ - public function getOCSStatus() - { - } - - /** - * Construct the meta part of the response - * And then late the base class render - * - * @return string - */ - public function render() - { - } -} diff --git a/tests/stubs/oc_appframework_utility_simplecontainer.php b/tests/stubs/oc_appframework_utility_simplecontainer.php deleted file mode 100644 index 5d7358a0e..000000000 --- a/tests/stubs/oc_appframework_utility_simplecontainer.php +++ /dev/null @@ -1,131 +0,0 @@ -|string $id - * @return T|mixed - * @psalm-template S as class-string|string - * @psalm-param S $id - * @psalm-return (S is class-string ? T : mixed) - */ - public function get(string $id): mixed - { - } - - public function has(string $id): bool - { - } - - public function resolve($name) - { - } - - public function query(string $name, bool $autoload = true) - { - } - - /** - * @param string $name - * @param mixed $value - */ - public function registerParameter($name, $value) - { - } - - /** - * The given closure is call the first time the given service is queried. - * The closure has to return the instance for the given service. - * Created instance will be cached in case $shared is true. - * - * @param string $name name of the service to register another backend for - * @param Closure $closure the closure to be called on service creation - * @param bool $shared - */ - public function registerService($name, Closure $closure, $shared = true) - { - } - - /** - * Shortcut for returning a service from a service under a different key, - * e.g. to tell the container to return a class when queried for an - * interface - * @param string $alias the alias that should be registered - * @param string $target the target that should be resolved instead - */ - public function registerAlias($alias, $target): void - { - } - - protected function registerDeprecatedAlias(string $alias, string $target): void - { - } - - /** - * @param string $name - * @return string - */ - protected function sanitizeName($name) - { - } - - /** - * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::has - */ - public function offsetExists($id): bool - { - } - - /** - * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::get - * @return mixed - */ - #[\ReturnTypeWillChange] - public function offsetGet($id) - { - } - - /** - * @deprecated 20.0.0 use \OCP\IContainer::registerService - */ - public function offsetSet($offset, $value): void - { - } - - /** - * @deprecated 20.0.0 - */ - public function offsetUnset($offset): void - { - } -} diff --git a/tests/stubs/oc_core_command_base.php b/tests/stubs/oc_core_command_base.php deleted file mode 100644 index b60f7fae4..000000000 --- a/tests/stubs/oc_core_command_base.php +++ /dev/null @@ -1,103 +0,0 @@ - - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where($qb->expr()->eq('u.id', 1)); - * - * - * For more complex expression construction, consider storing the expression - * builder object in a local variable. - * - * @return \OCP\DB\QueryBuilder\IExpressionBuilder - */ - public function expr() - { - } - - /** - * Gets an FunctionBuilder used for object-oriented construction of query functions. - * This producer method is intended for convenient inline usage. Example: - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where($qb->fun()->md5('u.id')); - * - * - * For more complex function construction, consider storing the function - * builder object in a local variable. - * - * @return \OCP\DB\QueryBuilder\IFunctionBuilder - */ - public function func() - { - } - - /** - * Gets the type of the currently built query. - * - * @return integer - */ - public function getType() - { - } - - /** - * Gets the associated DBAL Connection for this query builder. - * - * @return \OCP\IDBConnection - */ - public function getConnection() - { - } - - /** - * Gets the state of this query builder instance. - * - * @return int Always returns 0 which is former `QueryBuilder::STATE_DIRTY` - * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update - * and we can not fix this in our wrapper. - */ - public function getState() - { - } - - /** - * Executes this query using the bound parameters and their types. - * - * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} - * for insert, update and delete statements. - * - * @return IResult|int - */ - public function execute(?IDBConnection $connection = null) - { - } - - public function executeQuery(?IDBConnection $connection = null): IResult - { - } - - public function executeStatement(?IDBConnection $connection = null): int - { - } - - - /** - * Gets the complete SQL string formed by the current specifications of this QueryBuilder. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('User', 'u') - * echo $qb->getSQL(); // SELECT u FROM User u - * - * - * @return string The SQL query string. - */ - public function getSQL() - { - } - - /** - * Sets a query parameter for the query being constructed. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where('u.id = :user_id') - * ->setParameter(':user_id', 1); - * - * - * @param string|integer $key The parameter position or name. - * @param mixed $value The parameter value. - * @param string|null|int $type One of the IQueryBuilder::PARAM_* constants. - * - * @return $this This QueryBuilder instance. - */ - public function setParameter($key, $value, $type = null) - { - } - - /** - * Sets a collection of query parameters for the query being constructed. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where('u.id = :user_id1 OR u.id = :user_id2') - * ->setParameters(array( - * ':user_id1' => 1, - * ':user_id2' => 2 - * )); - * - * - * @param array $params The query parameters to set. - * @param array $types The query parameters types to set. - * - * @return $this This QueryBuilder instance. - */ - public function setParameters(array $params, array $types = []) - { - } - - /** - * Gets all defined query parameters for the query being constructed indexed by parameter index or name. - * - * @return array The currently defined query parameters indexed by parameter index or name. - */ - public function getParameters() - { - } - - /** - * Gets a (previously set) query parameter of the query being constructed. - * - * @param mixed $key The key (index or name) of the bound parameter. - * - * @return mixed The value of the bound parameter. - */ - public function getParameter($key) - { - } - - /** - * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. - * - * @return array The currently defined query parameter types indexed by parameter index or name. - */ - public function getParameterTypes() - { - } - - /** - * Gets a (previously set) query parameter type of the query being constructed. - * - * @param mixed $key The key (index or name) of the bound parameter type. - * - * @return mixed The value of the bound parameter type. - */ - public function getParameterType($key) - { - } - - /** - * Sets the position of the first result to retrieve (the "offset"). - * - * @param int $firstResult The first result to return. - * - * @return $this This QueryBuilder instance. - */ - public function setFirstResult($firstResult) - { - } - - /** - * Gets the position of the first result the query object was set to retrieve (the "offset"). - * Returns 0 if {@link setFirstResult} was not applied to this QueryBuilder. - * - * @return int The position of the first result. - */ - public function getFirstResult() - { - } - - /** - * Sets the maximum number of results to retrieve (the "limit"). - * - * NOTE: Setting max results to "0" will cause mixed behaviour. While most - * of the databases will just return an empty result set, Oracle will return - * all entries. - * - * @param int|null $maxResults The maximum number of results to retrieve. - * - * @return $this This QueryBuilder instance. - */ - public function setMaxResults($maxResults) - { - } - - /** - * Gets the maximum number of results the query object was set to retrieve (the "limit"). - * Returns NULL if {@link setMaxResults} was not applied to this query builder. - * - * @return int|null The maximum number of results. - */ - public function getMaxResults() - { - } - - /** - * Specifies an item that is to be returned in the query result. - * Replaces any previously specified selections, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.id', 'p.id') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); - * - * - * @param mixed ...$selects The selection expressions. - * - * '@return $this This QueryBuilder instance. - */ - public function select(...$selects) - { - } - - /** - * Specifies an item that is to be returned with a different name in the query result. - * - * - * $qb = $conn->getQueryBuilder() - * ->selectAlias('u.id', 'user_id') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); - * - * - * @param mixed $select The selection expressions. - * @param string $alias The column alias used in the constructed query. - * - * @return $this This QueryBuilder instance. - */ - public function selectAlias($select, $alias) - { - } - - /** - * Specifies an item that is to be returned uniquely in the query result. - * - * - * $qb = $conn->getQueryBuilder() - * ->selectDistinct('type') - * ->from('users'); - * - * - * @param mixed $select The selection expressions. - * - * @return $this This QueryBuilder instance. - */ - public function selectDistinct($select) - { - } - - /** - * Adds an item that is to be returned in the query result. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.id') - * ->addSelect('p.id') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); - * - * - * @param mixed ...$selects The selection expression. - * - * @return $this This QueryBuilder instance. - */ - public function addSelect(...$selects) - { - } - - public function getOutputColumns(): array - { - } - - /** - * Turns the query being built into a bulk delete query that ranges over - * a certain table. - * - * - * $qb = $conn->getQueryBuilder() - * ->delete('users', 'u') - * ->where('u.id = :user_id'); - * ->setParameter(':user_id', 1); - * - * - * @param string $delete The table whose rows are subject to the deletion. - * @param string $alias The table alias used in the constructed query. - * - * @return $this This QueryBuilder instance. - * @since 30.0.0 Alias is deprecated and will no longer be used with the next Doctrine/DBAL update - */ - public function delete($delete = null, $alias = null) - { - } - - /** - * Turns the query being built into a bulk update query that ranges over - * a certain table - * - * - * $qb = $conn->getQueryBuilder() - * ->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where('u.id = ?'); - * - * - * @param string $update The table whose rows are subject to the update. - * @param string $alias The table alias used in the constructed query. - * - * @return $this This QueryBuilder instance. - * @since 30.0.0 Alias is deprecated and will no longer be used with the next Doctrine/DBAL update - */ - public function update($update = null, $alias = null) - { - } - - /** - * Turns the query being built into an insert query that inserts into - * a certain table - * - * - * $qb = $conn->getQueryBuilder() - * ->insert('users') - * ->values( - * array( - * 'name' => '?', - * 'password' => '?' - * ) - * ); - * - * - * @param string $insert The table into which the rows should be inserted. - * - * @return $this This QueryBuilder instance. - */ - public function insert($insert = null) - { - } - - /** - * Creates and adds a query root corresponding to the table identified by the - * given alias, forming a cartesian product with any existing query roots. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.id') - * ->from('users', 'u') - * - * - * @param string|IQueryFunction $from The table. - * @param string|null $alias The alias of the table. - * - * @return $this This QueryBuilder instance. - */ - public function from($from, $alias = null) - { - } - - /** - * Creates and adds a join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @param string $fromAlias The alias that points to a from clause. - * @param string $join The table name to join. - * @param string $alias The alias of the join table. - * @param string|ICompositeExpression|null $condition The condition for the join. - * - * @return $this This QueryBuilder instance. - */ - public function join($fromAlias, $join, $alias, $condition = null) - { - } - - /** - * Creates and adds a join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @param string $fromAlias The alias that points to a from clause. - * @param string $join The table name to join. - * @param string $alias The alias of the join table. - * @param string|ICompositeExpression|null $condition The condition for the join. - * - * @return $this This QueryBuilder instance. - */ - public function innerJoin($fromAlias, $join, $alias, $condition = null) - { - } - - /** - * Creates and adds a left join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @param string $fromAlias The alias that points to a from clause. - * @param string|IQueryFunction $join The table name or sub-query to join. - * @param string $alias The alias of the join table. - * @param string|ICompositeExpression|null $condition The condition for the join. - * - * @return $this This QueryBuilder instance. - */ - public function leftJoin($fromAlias, $join, $alias, $condition = null) - { - } - - /** - * Creates and adds a right join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @param string $fromAlias The alias that points to a from clause. - * @param string $join The table name to join. - * @param string $alias The alias of the join table. - * @param string|ICompositeExpression|null $condition The condition for the join. - * - * @return $this This QueryBuilder instance. - */ - public function rightJoin($fromAlias, $join, $alias, $condition = null) - { - } - - /** - * Sets a new value for a column in a bulk update query. - * - * - * $qb = $conn->getQueryBuilder() - * ->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where('u.id = ?'); - * - * - * @param string $key The column to set. - * @param ILiteral|IParameter|IQueryFunction|string $value The value, expression, placeholder, etc. - * - * @return $this This QueryBuilder instance. - */ - public function set($key, $value) - { - } - - /** - * Specifies one or more restrictions to the query result. - * Replaces any previously specified restrictions, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->where('u.id = ?'); - * - * // You can optionally programmatically build and/or expressions - * $qb = $conn->getQueryBuilder(); - * - * $or = $qb->expr()->orx( - * $qb->expr()->eq('u.id', 1), - * $qb->expr()->eq('u.id', 2), - * ); - * - * $qb->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where($or); - * - * - * @param mixed ...$predicates The restriction predicates. - * - * @return $this This QueryBuilder instance. - */ - public function where(...$predicates) - { - } - - /** - * Adds one or more restrictions to the query results, forming a logical - * conjunction with any previously specified restrictions. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where('u.username LIKE ?') - * ->andWhere('u.is_active = 1'); - * - * - * @param mixed ...$where The query restrictions. - * - * @return $this This QueryBuilder instance. - * - * @see where() - */ - public function andWhere(...$where) - { - } - - /** - * Adds one or more restrictions to the query results, forming a logical - * disjunction with any previously specified restrictions. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->where('u.id = 1') - * ->orWhere('u.id = 2'); - * - * - * @param mixed ...$where The WHERE statement. - * - * @return $this This QueryBuilder instance. - * - * @see where() - */ - public function orWhere(...$where) - { - } - - /** - * Specifies a grouping over the results of the query. - * Replaces any previously specified groupings, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->groupBy('u.id'); - * - * - * @param mixed ...$groupBys The grouping expression. - * - * @return $this This QueryBuilder instance. - */ - public function groupBy(...$groupBys) - { - } - - /** - * Adds a grouping expression to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->groupBy('u.lastLogin'); - * ->addGroupBy('u.createdAt') - * - * - * @param mixed ...$groupBy The grouping expression. - * - * @return $this This QueryBuilder instance. - */ - public function addGroupBy(...$groupBy) - { - } - - /** - * Sets a value for a column in an insert query. - * - * - * $qb = $conn->getQueryBuilder() - * ->insert('users') - * ->values( - * array( - * 'name' => '?' - * ) - * ) - * ->setValue('password', '?'); - * - * - * @param string $column The column into which the value should be inserted. - * @param IParameter|string $value The value that should be inserted into the column. - * - * @return $this This QueryBuilder instance. - */ - public function setValue($column, $value) - { - } - - /** - * Specifies values for an insert query indexed by column names. - * Replaces any previous values, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->insert('users') - * ->values( - * array( - * 'name' => '?', - * 'password' => '?' - * ) - * ); - * - * - * @param array $values The values to specify for the insert query indexed by column names. - * - * @return $this This QueryBuilder instance. - */ - public function values(array $values) - { - } - - /** - * Specifies a restriction over the groups of the query. - * Replaces any previous having restrictions, if any. - * - * @param mixed ...$having The restriction over the groups. - * - * @return $this This QueryBuilder instance. - */ - public function having(...$having) - { - } - - /** - * Adds a restriction over the groups of the query, forming a logical - * conjunction with any existing having restrictions. - * - * @param mixed ...$having The restriction to append. - * - * @return $this This QueryBuilder instance. - */ - public function andHaving(...$having) - { - } - - /** - * Adds a restriction over the groups of the query, forming a logical - * disjunction with any existing having restrictions. - * - * @param mixed ...$having The restriction to add. - * - * @return $this This QueryBuilder instance. - */ - public function orHaving(...$having) - { - } - - /** - * Specifies an ordering for the query results. - * Replaces any previously specified orderings, if any. - * - * @param string|IQueryFunction|ILiteral|IParameter $sort The ordering expression. - * @param string $order The ordering direction. - * - * @return $this This QueryBuilder instance. - */ - public function orderBy($sort, $order = null) - { - } - - /** - * Adds an ordering to the query results. - * - * @param string|ILiteral|IParameter|IQueryFunction $sort The ordering expression. - * @param string $order The ordering direction. - * - * @return $this This QueryBuilder instance. - */ - public function addOrderBy($sort, $order = null) - { - } - - /** - * Gets a query part by its name. - * - * @param string $queryPartName - * - * @return mixed - * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update - * and we can not fix this in our wrapper. Please track the details you need, outside the object. - */ - public function getQueryPart($queryPartName) - { - } - - /** - * Gets all query parts. - * - * @return array - * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update - * and we can not fix this in our wrapper. Please track the details you need, outside the object. - */ - public function getQueryParts() - { - } - - /** - * Resets SQL parts. - * - * @param array|null $queryPartNames - * - * @return $this This QueryBuilder instance. - * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update - * and we can not fix this in our wrapper. Please create a new IQueryBuilder instead. - */ - public function resetQueryParts($queryPartNames = null) - { - } - - /** - * Resets a single SQL part. - * - * @param string $queryPartName - * - * @return $this This QueryBuilder instance. - * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update - * and we can not fix this in our wrapper. Please create a new IQueryBuilder instead. - */ - public function resetQueryPart($queryPartName) - { - } - - /** - * Creates a new named parameter and bind the value $value to it. - * - * This method provides a shortcut for PDOStatement::bindValue - * when using prepared statements. - * - * The parameter $value specifies the value that you want to bind. If - * $placeholder is not provided bindValue() will automatically create a - * placeholder for you. An automatic placeholder will be of the name - * ':dcValue1', ':dcValue2' etc. - * - * For more information see {@link https://www.php.net/pdostatement-bindparam} - * - * Example: - * - * $value = 2; - * $q->eq( 'id', $q->bindValue( $value ) ); - * $stmt = $q->executeQuery(); // executed with 'id = 2' - * - * - * @license New BSD License - * @link http://www.zetacomponents.org - * - * @param mixed $value - * @param IQueryBuilder::PARAM_* $type - * @param string $placeHolder The name to bind with. The string must start with a colon ':'. - * - * @return IParameter the placeholder name used. - */ - public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $placeHolder = null) - { - } - - /** - * Creates a new positional parameter and bind the given value to it. - * - * Attention: If you are using positional parameters with the query builder you have - * to be very careful to bind all parameters in the order they appear in the SQL - * statement , otherwise they get bound in the wrong order which can lead to serious - * bugs in your code. - * - * Example: - * - * $qb = $conn->getQueryBuilder(); - * $qb->select('u.*') - * ->from('users', 'u') - * ->where('u.username = ' . $qb->createPositionalParameter('Foo', IQueryBuilder::PARAM_STR)) - * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', IQueryBuilder::PARAM_STR)) - * - * - * @param mixed $value - * @param IQueryBuilder::PARAM_* $type - * - * @return IParameter - */ - public function createPositionalParameter($value, $type = IQueryBuilder::PARAM_STR) - { - } - - /** - * Creates a new parameter - * - * Example: - * - * $qb = $conn->getQueryBuilder(); - * $qb->select('u.*') - * ->from('users', 'u') - * ->where('u.username = ' . $qb->createParameter('name')) - * ->setParameter('name', 'Bar', IQueryBuilder::PARAM_STR)) - * - * - * @param string $name - * - * @return IParameter - */ - public function createParameter($name) - { - } - - /** - * Creates a new function - * - * Attention: Column names inside the call have to be quoted before hand - * - * Example: - * - * $qb = $conn->getQueryBuilder(); - * $qb->select($qb->createFunction('COUNT(*)')) - * ->from('users', 'u') - * echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u - * - * - * $qb = $conn->getQueryBuilder(); - * $qb->select($qb->createFunction('COUNT(`column`)')) - * ->from('users', 'u') - * echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u - * - * - * @param string $call - * - * @return IQueryFunction - */ - public function createFunction($call) - { - } - - /** - * Used to get the id of the last inserted element - * @return int - * @throws \BadMethodCallException When being called before an insert query has been run. - */ - public function getLastInsertId(): int - { - } - - /** - * Returns the table name quoted and with database prefix as needed by the implementation - * - * @param string|IQueryFunction $table - * @return string - */ - public function getTableName($table) - { - } - - /** - * Returns the table name with database prefix as needed by the implementation - * - * Was protected until version 30. - * - * @param string $table - * @return string - */ - public function prefixTableName(string $table): string - { - } - - /** - * Returns the column name quoted and with table alias prefix as needed by the implementation - * - * @param string $column - * @param string $tableAlias - * @return string - */ - public function getColumnName($column, $tableAlias = '') - { - } - - /** - * Returns the column name quoted and with table alias prefix as needed by the implementation - * - * @param string $alias - * @return string - */ - public function quoteAlias($alias) - { - } - - public function escapeLikeParameter(string $parameter): string - { - } - - public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self - { - } - - public function runAcrossAllShards(): self - { - } - -} diff --git a/tests/stubs/oc_files_cache_cache.php b/tests/stubs/oc_files_cache_cache.php deleted file mode 100644 index 90a240793..000000000 --- a/tests/stubs/oc_files_cache_cache.php +++ /dev/null @@ -1,432 +0,0 @@ - $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged - */ - public function update($id, array $data) - { - } - - /** - * extract query parts and params array from data array - * - * @param array $data - * @return array - */ - protected function normalizeData(array $data): array - { - } - - /** - * get the file id for a file - * - * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file - * - * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing - * - * @param string $file - * @return int - */ - public function getId($file) - { - } - - /** - * get the id of the parent folder of a file - * - * @param string $file - * @return int - */ - public function getParentId($file) - { - } - - /** - * check if a file is available in the cache - * - * @param string $file - * @return bool - */ - public function inCache($file) - { - } - - /** - * remove a file or folder from the cache - * - * when removing a folder from the cache all files and folders inside the folder will be removed as well - * - * @param string $file - */ - public function remove($file) - { - } - - /** - * Move a file or folder in the cache - * - * @param string $source - * @param string $target - */ - public function move($source, $target) - { - } - - /** - * Get the storage id and path needed for a move - * - * @param string $path - * @return array [$storageId, $internalPath] - */ - protected function getMoveInfo($path) - { - } - - protected function hasEncryptionWrapper(): bool - { - } - - protected function shouldEncrypt(string $targetPath): bool - { - } - - /** - * Move a file or folder in the cache - * - * @param ICache $sourceCache - * @param string $sourcePath - * @param string $targetPath - * @throws \OC\DatabaseException - * @throws \Exception if the given storages have an invalid id - */ - public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) - { - } - - /** - * remove all entries for files that are stored on the storage from the cache - */ - public function clear() - { - } - - /** - * Get the scan status of a file - * - * - Cache::NOT_FOUND: File is not in the cache - * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known - * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned - * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned - * - * @param string $file - * - * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE - */ - public function getStatus($file) - { - } - - /** - * search for files matching $pattern - * - * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%') - * @return ICacheEntry[] an array of cache entries where the name matches the search pattern - */ - public function search($pattern) - { - } - - /** - * search for files by mimetype - * - * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image') - * where it will search for all mimetypes in the group ('image/*') - * @return ICacheEntry[] an array of cache entries where the mimetype matches the search - */ - public function searchByMime($mimetype) - { - } - - public function searchQuery(ISearchQuery $query) - { - } - - /** - * Re-calculate the folder size and the size of all parent folders - * - * @param array|ICacheEntry|null $data (optional) meta data of the folder - */ - public function correctFolderSize(string $path, $data = null, bool $isBackgroundScan = false): void - { - } - - /** - * get the incomplete count that shares parent $folder - * - * @param int $fileId the file id of the folder - * @return int - */ - public function getIncompleteChildrenCount($fileId) - { - } - - /** - * calculate the size of a folder and set it in the cache - * - * @param string $path - * @param array|null|ICacheEntry $entry (optional) meta data of the folder - * @return int|float - */ - public function calculateFolderSize($path, $entry = null) - { - } - - - /** - * inner function because we can't add new params to the public function without breaking any child classes - * - * @param string $path - * @param array|null|ICacheEntry $entry (optional) meta data of the folder - * @param bool $ignoreUnknown don't mark the folder size as unknown if any of it's children are unknown - * @return int|float - */ - protected function calculateFolderSizeInner(string $path, $entry = null, bool $ignoreUnknown = false) - { - } - - /** - * get all file ids on the files on the storage - * - * @return int[] - */ - public function getAll() - { - } - - /** - * find a folder in the cache which has not been fully scanned - * - * If multiple incomplete folders are in the cache, the one with the highest id will be returned, - * use the one with the highest id gives the best result with the background scanner, since that is most - * likely the folder where we stopped scanning previously - * - * @return string|false the path of the folder or false when no folder matched - */ - public function getIncomplete() - { - } - - /** - * get the path of a file on this storage by it's file id - * - * @param int $id the file id of the file or folder to search - * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache - */ - public function getPathById($id) - { - } - - /** - * get the storage id of the storage for a file and the internal path of the file - * unlike getPathById this does not limit the search to files on this storage and - * instead does a global search in the cache table - * - * @param int $id - * @return array first element holding the storage id, second the path - * @deprecated 17.0.0 use getPathById() instead - */ - public static function getById($id) - { - } - - /** - * normalize the given path - * - * @param string $path - * @return string - */ - public function normalize($path) - { - } - - /** - * Copy a file or folder in the cache - * - * @param ICache $sourceCache - * @param ICacheEntry $sourceEntry - * @param string $targetPath - * @return int fileId of copied entry - */ - public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int - { - } - - public function getQueryFilterForStorage(): ISearchOperator - { - } - - public function getCacheEntryFromSearchResult(ICacheEntry $rawEntry): ?ICacheEntry - { - } -} diff --git a/tests/stubs/oc_files_cache_cacheentry.php b/tests/stubs/oc_files_cache_cacheentry.php deleted file mode 100644 index d6d4b62ca..000000000 --- a/tests/stubs/oc_files_cache_cacheentry.php +++ /dev/null @@ -1,119 +0,0 @@ - - */ - protected function getExistingChildren($folderId): array - { - } - - /** - * scan all the files and folders in a folder - * - * @param string $path - * @param bool|IScanner::SCAN_RECURSIVE_INCOMPLETE $recursive - * @param int $reuse a combination of self::REUSE_* - * @param int $folderId id for the folder to be scanned - * @param bool $lock set to false to disable getting an additional read lock during scanning - * @param int|float $oldSize the size of the folder before (re)scanning the children - * @return int|float the size of the scanned folder or -1 if the size is unknown at this stage - */ - protected function scanChildren(string $path, $recursive, int $reuse, int $folderId, bool $lock, int|float $oldSize, &$etagChanged = false) - { - } - - /** - * check if the file should be ignored when scanning - * NOTE: files with a '.part' extension are ignored as well! - * prevents unfinished put requests to be scanned - * - * @param string $file - * @return boolean - */ - public static function isPartialFile($file) - { - } - - /** - * walk over any folders that are not fully scanned yet and scan them - */ - public function backgroundScan() - { - } - - protected function runBackgroundScanJob(callable $callback, $path) - { - } - - /** - * Set whether the cache is affected by scan operations - * - * @param boolean $active The active state of the cache - */ - public function setCacheActive($active) - { - } -} diff --git a/tests/stubs/oc_files_cache_wrapper_cachejail.php b/tests/stubs/oc_files_cache_wrapper_cachejail.php deleted file mode 100644 index 9fff517d1..000000000 --- a/tests/stubs/oc_files_cache_wrapper_cachejail.php +++ /dev/null @@ -1,251 +0,0 @@ - - */ - public function getMetadata(): array - { - } - - public function verifyPath($fileName, $readonly = false): void - { - } -} diff --git a/tests/stubs/oc_files_node_node.php b/tests/stubs/oc_files_node_node.php deleted file mode 100644 index 962634890..000000000 --- a/tests/stubs/oc_files_node_node.php +++ /dev/null @@ -1,354 +0,0 @@ - - */ - public function getMetadata(): array - { - } -} diff --git a/tests/stubs/oc_files_objectstore_objectstorescanner.php b/tests/stubs/oc_files_objectstore_objectstorescanner.php deleted file mode 100644 index 135e92445..000000000 --- a/tests/stubs/oc_files_objectstore_objectstorescanner.php +++ /dev/null @@ -1,30 +0,0 @@ -, arguments: array{multibucket: bool, objectPrefix?: string, ...}} - */ -class PrimaryObjectStoreConfig { - public function __construct( - private readonly IConfig $config, - private readonly IAppManager $appManager, - ) { - } - - /** - * @param ObjectStoreConfig $config - */ - public function buildObjectStore(array $config): IObjectStore - { - } - - /** - * @return ?ObjectStoreConfig - */ - public function getObjectStoreConfigForRoot(): ?array - { - } - - /** - * @return ?ObjectStoreConfig - */ - public function getObjectStoreConfigForUser(IUser $user): ?array - { - } - - /** - * @param string $name - * @return ObjectStoreConfig - */ - public function getObjectStoreConfiguration(string $name): array - { - } - - public function resolveAlias(string $name): string - { - } - - public function hasObjectStore(): bool - { - } - - public function hasMultipleObjectStorages(): bool - { - } - - /** - * @return ?array - * @throws InvalidObjectStoreConfigurationException - */ - public function getObjectStoreConfigs(): ?array - { - } - - public function getBucketForUser(IUser $user, array $config): string - { - } - - public function getSetBucketForUser(IUser $user): ?string - { - } - - public function getObjectStoreForUser(IUser $user): string - { - } -} diff --git a/tests/stubs/oc_files_setupmanager.php b/tests/stubs/oc_files_setupmanager.php deleted file mode 100644 index 6bb940563..000000000 --- a/tests/stubs/oc_files_setupmanager.php +++ /dev/null @@ -1,98 +0,0 @@ -stat() . - */ -abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage, IConstructableStorage { - use LocalTempFileTrait; - - protected ?Cache $cache = null; - protected ?Scanner $scanner = null; - protected ?Watcher $watcher = null; - protected ?Propagator $propagator = null; - protected $storageCache; - protected ?Updater $updater = null; - - protected array $mountOptions = []; - protected $owner = null; - - public function __construct(array $parameters) { - } - - protected function remove(string $path): bool - { - } - - public function is_dir(string $path): bool - { - } - - public function is_file(string $path): bool - { - } - - public function filesize(string $path): int|float|false - { - } - - public function isReadable(string $path): bool - { - } - - public function isUpdatable(string $path): bool - { - } - - public function isCreatable(string $path): bool - { - } - - public function isDeletable(string $path): bool - { - } - - public function isSharable(string $path): bool - { - } - - public function getPermissions(string $path): int - { - } - - public function filemtime(string $path): int|false - { - } - - public function file_get_contents(string $path): string|false - { - } - - public function file_put_contents(string $path, mixed $data): int|float|false - { - } - - public function rename(string $source, string $target): bool - { - } - - public function copy(string $source, string $target): bool - { - } - - public function getMimeType(string $path): string|false - { - } - - public function hash(string $type, string $path, bool $raw = false): string|false - { - } - - public function getLocalFile(string $path): string|false - { - } - - protected function searchInDir(string $query, string $dir = ''): array - { - } - - /** - * @inheritDoc - * Check if a file or folder has been updated since $time - * - * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking - * the mtime should always return false here. As a result storage implementations that always return false expect - * exclusive access to the backend and will not pick up files that have been added in a way that circumvents - * Nextcloud filesystem. - */ - public function hasUpdated(string $path, int $time): bool - { - } - - protected function getCacheDependencies(): CacheDependencies - { - } - - public function getCache(string $path = '', ?IStorage $storage = null): ICache - { - } - - public function getScanner(string $path = '', ?IStorage $storage = null): IScanner - { - } - - public function getWatcher(string $path = '', ?IStorage $storage = null): IWatcher - { - } - - public function getPropagator(?IStorage $storage = null): IPropagator - { - } - - public function getUpdater(?IStorage $storage = null): IUpdater - { - } - - public function getStorageCache(?IStorage $storage = null): \OC\Files\Cache\Storage - { - } - - public function getOwner(string $path): string|false - { - } - - public function getETag(string $path): string|false - { - } - - /** - * clean a path, i.e. remove all redundant '.' and '..' - * making sure that it can't point to higher than '/' - * - * @param string $path The path to clean - * @return string cleaned path - */ - public function cleanPath(string $path): string - { - } - - /** - * Test a storage for availability - */ - public function test(): bool - { - } - - public function free_space(string $path): int|float|false - { - } - - public function isLocal(): bool - { - } - - /** - * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class - */ - public function instanceOfStorage(string $class): bool - { - } - - /** - * A custom storage implementation can return an url for direct download of a give file. - * - * For now the returned array can hold the parameter url - in future more attributes might follow. - */ - public function getDirectDownload(string $path): array|false - { - } - - public function getDirectDownloadById(string $fileId): array|false - { - } - - public function verifyPath(string $path, string $fileName): void - { - } - - /** - * Get the filename validator - * (cached for performance) - */ - protected function getFilenameValidator(): IFilenameValidator - { - } - - public function setMountOptions(array $options): void - { - } - - public function getMountOption(string $name, mixed $default = null): mixed - { - } - - public function copyFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath, bool $preserveMtime = false): bool - { - } - - public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool - { - } - - public function getMetaData(string $path): ?array - { - } - - public function acquireLock(string $path, int $type, ILockingProvider $provider): void - { - } - - public function releaseLock(string $path, int $type, ILockingProvider $provider): void - { - } - - public function changeLock(string $path, int $type, ILockingProvider $provider): void - { - } - - /** - * @return array [ available, last_checked ] - */ - public function getAvailability(): array - { - } - - public function setAvailability(bool $isAvailable): void - { - } - - public function setOwner(?string $user): void - { - } - - public function needsPartFile(): bool - { - } - - public function writeStream(string $path, $stream, ?int $size = null): int - { - } - - public function getDirectoryContent(string $directory): \Traversable - { - } -} diff --git a/tests/stubs/oc_files_storage_local.php b/tests/stubs/oc_files_storage_local.php deleted file mode 100644 index 4fe5790a0..000000000 --- a/tests/stubs/oc_files_storage_local.php +++ /dev/null @@ -1,181 +0,0 @@ - $storage, 'root' => $root] - * - * $storage: The storage that will be wrapper - * $root: The folder in the wrapped storage that will become the root folder of the wrapped storage - */ - public function __construct(array $parameters) - { - } - - public function getUnjailedPath(string $path): string - { - } - - /** - * This is separate from Wrapper::getWrapperStorage so we can get the jailed storage consistently even if the jail is inside another wrapper - */ - public function getUnjailedStorage(): IStorage - { - } - - - public function getJailedPath(string $path): ?string - { - } - - public function getId(): string - { - } - - public function mkdir(string $path): bool - { - } - - public function rmdir(string $path): bool - { - } - - public function opendir(string $path) - { - } - - public function is_dir(string $path): bool - { - } - - public function is_file(string $path): bool - { - } - - public function stat(string $path): array|false - { - } - - public function filetype(string $path): string|false - { - } - - public function filesize(string $path): int|float|false - { - } - - public function isCreatable(string $path): bool - { - } - - public function isReadable(string $path): bool - { - } - - public function isUpdatable(string $path): bool - { - } - - public function isDeletable(string $path): bool - { - } - - public function isSharable(string $path): bool - { - } - - public function getPermissions(string $path): int - { - } - - public function file_exists(string $path): bool - { - } - - public function filemtime(string $path): int|false - { - } - - public function file_get_contents(string $path): string|false - { - } - - public function file_put_contents(string $path, mixed $data): int|float|false - { - } - - public function unlink(string $path): bool - { - } - - public function rename(string $source, string $target): bool - { - } - - public function copy(string $source, string $target): bool - { - } - - public function fopen(string $path, string $mode) - { - } - - public function getMimeType(string $path): string|false - { - } - - public function hash(string $type, string $path, bool $raw = false): string|false - { - } - - public function free_space(string $path): int|float|false - { - } - - public function touch(string $path, ?int $mtime = null): bool - { - } - - public function getLocalFile(string $path): string|false - { - } - - public function hasUpdated(string $path, int $time): bool - { - } - - public function getCache(string $path = '', ?IStorage $storage = null): ICache - { - } - - public function getOwner(string $path): string|false - { - } - - public function getWatcher(string $path = '', ?IStorage $storage = null): IWatcher - { - } - - public function getETag(string $path): string|false - { - } - - public function getMetaData(string $path): ?array - { - } - - public function acquireLock(string $path, int $type, ILockingProvider $provider): void - { - } - - public function releaseLock(string $path, int $type, ILockingProvider $provider): void - { - } - - public function changeLock(string $path, int $type, ILockingProvider $provider): void - { - } - - /** - * Resolve the path for the source of the share - */ - public function resolvePath(string $path): array - { - } - - public function copyFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool - { - } - - public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool - { - } - - public function getPropagator(?IStorage $storage = null): IPropagator - { - } - - public function writeStream(string $path, $stream, ?int $size = null): int - { - } - - public function getDirectoryContent(string $directory): \Traversable - { - } -} diff --git a/tests/stubs/oc_files_storage_wrapper_permissionsmask.php b/tests/stubs/oc_files_storage_wrapper_permissionsmask.php deleted file mode 100644 index 73d73e812..000000000 --- a/tests/stubs/oc_files_storage_wrapper_permissionsmask.php +++ /dev/null @@ -1,99 +0,0 @@ - $storage, 'mask' => $mask] - * - * $storage: The storage the permissions mask should be applied on - * $mask: The permission bits that should be kept, a combination of the \OCP\Constant::PERMISSION_ constants - */ - public function __construct(array $parameters) - { - } - - public function isUpdatable(string $path): bool - { - } - - public function isCreatable(string $path): bool - { - } - - public function isDeletable(string $path): bool - { - } - - public function isSharable(string $path): bool - { - } - - public function getPermissions(string $path): int - { - } - - public function rename(string $source, string $target): bool - { - } - - public function copy(string $source, string $target): bool - { - } - - public function touch(string $path, ?int $mtime = null): bool - { - } - - public function mkdir(string $path): bool - { - } - - public function rmdir(string $path): bool - { - } - - public function unlink(string $path): bool - { - } - - public function file_put_contents(string $path, mixed $data): int|float|false - { - } - - public function fopen(string $path, string $mode) - { - } - - public function getCache(string $path = '', ?IStorage $storage = null): \OCP\Files\Cache\ICache - { - } - - public function getMetaData(string $path): ?array - { - } - - public function getScanner(string $path = '', ?IStorage $storage = null): \OCP\Files\Cache\IScanner - { - } - - public function getDirectoryContent(string $directory): \Traversable - { - } -} diff --git a/tests/stubs/oc_files_storage_wrapper_quota.php b/tests/stubs/oc_files_storage_wrapper_quota.php deleted file mode 100644 index e7ea43c05..000000000 --- a/tests/stubs/oc_files_storage_wrapper_quota.php +++ /dev/null @@ -1,80 +0,0 @@ - $class - * @psalm-return T|null - */ - public function getInstanceOfStorage(string $class): ?IStorage - { - } - - /** - * Pass any methods custom to specific storage implementations to the wrapped storage - * - * @return mixed - */ - public function __call(string $method, array $args) - { - } - - public function getDirectDownload(string $path): array|false - { - } - - public function getDirectDownloadById(string $fileId): array|false - { - } - - public function getAvailability(): array - { - } - - public function setAvailability(bool $isAvailable): void - { - } - - public function verifyPath(string $path, string $fileName): void - { - } - - public function copyFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool - { - } - - public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool - { - } - - public function getMetaData(string $path): ?array - { - } - - public function acquireLock(string $path, int $type, ILockingProvider $provider): void - { - } - - public function releaseLock(string $path, int $type, ILockingProvider $provider): void - { - } - - public function changeLock(string $path, int $type, ILockingProvider $provider): void - { - } - - public function needsPartFile(): bool - { - } - - public function writeStream(string $path, $stream, ?int $size = null): int - { - } - - public function getDirectoryContent(string $directory): \Traversable - { - } - - public function isWrapperOf(IStorage $storage): bool - { - } - - public function setOwner(?string $user): void - { - } -} diff --git a/tests/stubs/oc_files_view.php b/tests/stubs/oc_files_view.php deleted file mode 100644 index ca2afc9d7..000000000 --- a/tests/stubs/oc_files_view.php +++ /dev/null @@ -1,674 +0,0 @@ - an array of group names - * - * This function fetches all groups a user belongs to. It does not check - * if the user exists at all. - */ - public function getUserGroups($uid) - { - } - - /** - * get a list of all groups - * @param string $search - * @param int $limit - * @param int $offset - * @return array an array of group names - * - * Returns a list with all groups - */ - public function getGroups(string $search = '', int $limit = -1, int $offset = 0) - { - } - - /** - * check if a group exists - * @param string $gid - * @return bool - */ - public function groupExists($gid) - { - } - - /** - * {@inheritdoc} - */ - public function groupsExists(array $gids): array - { - } - - /** - * Get a list of all users in a group - * @param string $gid - * @param string $search - * @param int $limit - * @param int $offset - * @return array an array of user ids - */ - public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0): array - { - } - - public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array - { - } - - /** - * get the number of all users matching the search string in a group - * @param string $gid - * @param string $search - * @return int - */ - public function countUsersInGroup(string $gid, string $search = ''): int - { - } - - /** - * get the number of disabled users in a group - * - * @param string $search - * - * @return int - */ - public function countDisabledInGroup(string $gid): int - { - } - - public function getDisplayName(string $gid): string - { - } - - public function getGroupDetails(string $gid): array - { - } - - /** - * {@inheritdoc} - */ - public function getGroupsDetails(array $gids): array - { - } - - public function setDisplayName(string $gid, string $displayName): bool - { - } - - /** - * Backend name to be shown in group management - * @return string the name of the backend to be shown - * @since 21.0.0 - */ - public function getBackendName(): string - { - } -} diff --git a/tests/stubs/oc_group_manager.php b/tests/stubs/oc_group_manager.php deleted file mode 100644 index 13e045bbd..000000000 --- a/tests/stubs/oc_group_manager.php +++ /dev/null @@ -1,214 +0,0 @@ - $gids List of groupIds for which we want to create a IGroup object - * @param array $displayNames Array containing already know display name for a groupId - * @return array - */ - protected function getGroupsObjects(array $gids, array $displayNames = []): array - { - } - - /** - * @param string $gid - * @return bool - */ - public function groupExists($gid) - { - } - - /** - * @param string $gid - * @return IGroup|null - */ - public function createGroup($gid) - { - } - - public function search(string $search, ?int $limit = null, ?int $offset = 0) - { - } - - /** - * @param IUser|null $user - * @return \OC\Group\Group[] - */ - public function getUserGroups(?IUser $user = null) - { - } - - /** - * @param string $uid the user id - * @return \OC\Group\Group[] - */ - public function getUserIdGroups(string $uid): array - { - } - - /** - * Checks if a userId is in the admin group - * - * @param string $userId - * @return bool if admin - */ - public function isAdmin($userId) - { - } - - public function isDelegatedAdmin(string $userId): bool - { - } - - /** - * Checks if a userId is in a group - * - * @param string $userId - * @param string $group - * @return bool if in group - */ - public function isInGroup($userId, $group) - { - } - - public function getUserGroupIds(IUser $user): array - { - } - - /** - * @param string $groupId - * @return ?string - */ - public function getDisplayName(string $groupId): ?string - { - } - - /** - * get an array of groupid and displayName for a user - * - * @param IUser $user - * @return array ['displayName' => displayname] - */ - public function getUserGroupNames(IUser $user) - { - } - - /** - * get a list of all display names in a group - * - * @param string $gid - * @param string $search - * @param int $limit - * @param int $offset - * @return array an array of display names (value) and user ids (key) - */ - public function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) - { - } - - /** - * @return \OC\SubAdmin - */ - public function getSubAdmin() - { - } -} diff --git a/tests/stubs/oc_hooks_basicemitter.php b/tests/stubs/oc_hooks_basicemitter.php deleted file mode 100644 index 091334b71..000000000 --- a/tests/stubs/oc_hooks_basicemitter.php +++ /dev/null @@ -1,15 +0,0 @@ -get($app) instead - */ - public function getAppDataDir($app) - { - } - - /** - * @return \OCP\Federation\ICloudIdManager - * @deprecated 20.0.0 - */ - public function getCloudIdManager() - { - } -} diff --git a/tests/stubs/oc_servercontainer.php b/tests/stubs/oc_servercontainer.php deleted file mode 100644 index 2c7c3cafb..000000000 --- a/tests/stubs/oc_servercontainer.php +++ /dev/null @@ -1,101 +0,0 @@ -|string $name - * @return T|mixed - * @psalm-template S as class-string|string - * @psalm-param S $name - * @psalm-return (S is class-string ? T : mixed) - * @throws QueryException - * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::get - */ - public function query(string $name, bool $autoload = true) - { - } - - /** - * @internal - * @param string $id - * @return DIContainer|null - */ - public function getAppContainerForService(string $id): ?DIContainer - { - } -} diff --git a/tests/stubs/oc_settings_authorizedgroupmapper.php b/tests/stubs/oc_settings_authorizedgroupmapper.php deleted file mode 100644 index 411448cc5..000000000 --- a/tests/stubs/oc_settings_authorizedgroupmapper.php +++ /dev/null @@ -1,70 +0,0 @@ - - */ -class AuthorizedGroupMapper extends QBMapper { - public function __construct(IDBConnection $db) - { - } - - /** - * @throws Exception - */ - public function findAllClassesForUser(IUser $user): array - { - } - - /** - * @throws \OCP\AppFramework\Db\DoesNotExistException - * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException - * @throws \OCP\DB\Exception - */ - public function find(int $id): AuthorizedGroup - { - } - - /** - * Get all the authorizations stored in the database. - * - * @return AuthorizedGroup[] - * @throws \OCP\DB\Exception - */ - public function findAll(): array - { - } - - public function findByGroupIdAndClass(string $groupId, string $class) - { - } - - /** - * @return Entity[] - * @throws \OCP\DB\Exception - */ - public function findExistingGroupsForClass(string $class): array - { - } - - /** - * @throws Exception - */ - public function removeGroup(string $gid) - { - } -} diff --git a/tests/stubs/oc_user_user.php b/tests/stubs/oc_user_user.php deleted file mode 100644 index f20db3a93..000000000 --- a/tests/stubs/oc_user_user.php +++ /dev/null @@ -1,312 +0,0 @@ -federatedUserService->bypassCurrentUserCondition(true); - // $this->memberService->getMemberById($circleId, $singleId); - // } - - - /** - * WIP - * - * @param string $memberId - * - * @return Member - */ - // public function getMemberById(string $memberId): Member { -} diff --git a/tests/stubs/oca_circles_circlesqueryhelper.php b/tests/stubs/oca_circles_circlesqueryhelper.php deleted file mode 100644 index 58e0e65c5..000000000 --- a/tests/stubs/oca_circles_circlesqueryhelper.php +++ /dev/null @@ -1,105 +0,0 @@ - [ - self::MEMBER - ], - self::CIRCLE => [ - self::OPTIONS => [ - ], - self::MEMBER, - self::OWNER => [ - self::BASED_ON - ], - self::MEMBERSHIPS => [ - self::CONFIG - ], - self::DIRECT_INITIATOR => [ - self::BASED_ON - ], - self::INITIATOR => [ - self::BASED_ON, - self::INHERITED_BY => [ - self::MEMBERSHIPS - ] - ], - self::REMOTE => [ - self::MEMBER, - self::CIRCLE => [ - self::OWNER - ] - ] - ], - self::MEMBER => [ - self::MEMBERSHIPS => [ - self::CONFIG - ], - self::INHERITANCE_FROM, - self::CIRCLE => [ - self::OPTIONS => [ - 'getData' => true - ], - self::OWNER, - self::MEMBERSHIPS => [ - self::CONFIG - ], - self::DIRECT_INITIATOR, - self::INITIATOR => [ - self::OPTIONS => [ - 'minimumLevel' => Member::LEVEL_MEMBER - ], - self::BASED_ON, - self::INHERITED_BY => [ - self::MEMBERSHIPS - ], - self::INVITED_BY => [ - self::OWNER, - self::BASED_ON - ] - ] - ], - self::BASED_ON => [ - self::OWNER, - self::MEMBERSHIPS, - self::INITIATOR => [ - self::BASED_ON, - self::INHERITED_BY => [ - self::MEMBERSHIPS - ] - ] - ], - self::REMOTE => [ - self::MEMBER, - self::CIRCLE => [ - self::OWNER - ] - ], - self::INVITED_BY => [ - self::OWNER, - self::BASED_ON - ] - ], - self::MEMBERSHIPS => [ - self::CONFIG - ], - self::SHARE => [ - self::SHARE, - self::TOKEN, - self::FILE_CACHE => [ - self::STORAGES - ], - self::UPSTREAM_MEMBERSHIPS => [ - self::MEMBERSHIPS, - self::INHERITED_BY => [ - self::BASED_ON - ], - self::SHARE, - ], - self::MEMBERSHIPS => [ - self::CONFIG - ], - self::INHERITANCE_FROM, - self::INHERITED_BY => [ - self::BASED_ON - ], - self::CIRCLE => [ - self::OWNER - ], - self::INITIATOR => [ - self::BASED_ON, - self::INHERITED_BY => [ - self::MEMBERSHIPS - ] - ] - ], - self::REMOTE => [ - self::MEMBER - ], - self::MOUNT => [ - self::MEMBER => [ - self::REMOTE - ], - self::INITIATOR => [ - self::INHERITED_BY => [ - self::MEMBERSHIPS - ] - ], - self::MOUNTPOINT, - self::MEMBERSHIPS => [ - self::CONFIG - ] - ], - self::HELPER => [ - self::MEMBERSHIPS => [ - self::CONFIG - ], - self::INITIATOR => [ - self::INHERITED_BY => [ - self::MEMBERSHIPS - ] - ], - self::CIRCLE => [ - self::OPTIONS => [ - ], - self::MEMBER, - self::OWNER => [ - self::BASED_ON - ] - ] - ] - ]; - - /** - * CoreQueryBuilder constructor. - */ - public function __construct() - { - } - - - /** - * @param IFederatedModel $federatedModel - * - * @return string - */ - public function getInstance(IFederatedModel $federatedModel): string - { - } - - - /** - * @param string $id - */ - public function limitToCircleId(string $id): void - { - } - - /** - * @param string $name - */ - public function limitToName(string $name): void - { - } - - /** - * @param string $name - */ - public function limitToDisplayName(string $name): void - { - } - - /** - * @param string $name - */ - public function limitToSanitizedName(string $name): void - { - } - - /** - * @param int $config - */ - public function limitToConfig(int $config): void - { - } - - /** - * @param int $source - */ - public function limitToSource(int $source): void - { - } - - /** - * @param int $config - * @param string $alias - */ - public function limitToConfigFlag(int $config, string $alias = ''): void - { - } - - - /** - * @param string $singleId - */ - public function limitToSingleId(string $singleId, string $alias = ''): void - { - } - - - /** - * @param string $itemId - */ - public function limitToItemId(string $itemId): void - { - } - - - /** - * @param string $host - */ - public function limitToInstance(string $host): void - { - } - - - /** - * @param int $userType - */ - public function limitToUserType(int $userType): void - { - } - - - /** - * @param int $shareType - */ - public function limitToShareType(int $shareType): void - { - } - - - /** - * @param string $shareWith - */ - public function limitToShareWith(string $shareWith): void - { - } - - - /** - * @param int $nodeId - */ - public function limitToFileSource(int $nodeId): void - { - } - - /** - * @param array $files - */ - public function limitToFileSourceArray(array $files): void - { - } - - - /** - * @param int $shareId - */ - public function limitToShareParent(int $shareId): void - { - } - - - /** - * filter result on details (ie. displayName, Description, ...) - * - * @param Circle $circle - */ - public function filterCircleDetails(Circle $circle): void - { - } - - - /** - * left join RemoteInstance based on a Member - */ - public function leftJoinRemoteInstance(string $alias): void - { - } - - - /** - * @param string $alias - * @param RemoteInstance $remoteInstance - * @param bool $filterSensitiveData - * @param string $aliasCircle - * - * @throws RequestBuilderException - */ - public function limitToRemoteInstance(string $alias, RemoteInstance $remoteInstance, bool $filterSensitiveData = true, string $aliasCircle = ''): void - { - } - - - /** - * Left join RemoteInstance based on an incoming request - * - * @param string $alias - * @param RemoteInstance $remoteInstance - * - * @throws RequestBuilderException - */ - public function leftJoinRemoteInstanceIncomingRequest(string $alias, RemoteInstance $remoteInstance): void - { - } - - - /** - * - global_scale: visibility on all Circles - * - trusted: visibility on all FEDERATED Circle if owner is local - * - external: visibility on all FEDERATED Circle if owner is local and: - * - with if Circle contains at least one member from the remote instance - * - one circle from the remote instance contains the local circle as member, and confirmed (using - * sync locally) - * - passive: like external, but the members list will only contains member from the local instance and - * from the remote instance. - * - * @param string $alias - * @param bool $sensitive - * @param string $aliasCircle - * - * @throws RequestBuilderException - */ - protected function limitRemoteVisibility(string $alias, bool $sensitive, string $aliasCircle) - { - } - - - /** - * @param string $alias - * @param Member $member - * - * @throws RequestBuilderException - */ - public function limitToDirectMembership(string $alias, Member $member): void - { - } - - - /** - * @param string $alias - * @param string $aliasCircle - * @param FederatedUser $federatedUser - * - * @throws RequestBuilderException - */ - public function limitToFederatedUserMemberships(string $alias, string $aliasCircle, FederatedUser $federatedUser): void - { - } - - - /** - * @param string $aliasMember - * @param Member $member - */ - public function filterDirectMembership(string $aliasMember, Member $member): void - { - } - - - /** - * @param string $alias - * @param IFederatedUser|null $initiator - * @param string $field - * @param string $helperAlias - * - * @throws RequestBuilderException - */ - public function leftJoinCircle(string $alias, ?IFederatedUser $initiator = null, string $field = 'circle_id', string $helperAlias = ''): void - { - } - - - /** - * @param string $aliasMember - * - * @throws RequestBuilderException - */ - public function leftJoinInvitedBy(string $aliasMember): void - { - } - - - /** - * @param string $aliasMember - * @param IFederatedUser|null $initiator - * - * @throws RequestBuilderException - */ - public function leftJoinBasedOn(string $aliasMember, ?IFederatedUser $initiator = null): void - { - } - - - /** - * @param string $alias - * @param string $field - * - * @throws RequestBuilderException - */ - public function leftJoinOwner(string $alias, string $field = 'unique_id'): void - { - } - - - /** - * @param CircleProbe $probe - * @param string $alias - * @param string $field - */ - public function innerJoinMembership(?CircleProbe $probe, string $alias, string $field = 'unique_id'): void - { - } - - - /** - * @param string $alias - * @param string $fieldCircleId - * @param string $fieldSingleId - * - * @throws RequestBuilderException - */ - public function leftJoinMember(string $alias, string $fieldCircleId = 'circle_id', string $fieldSingleId = 'single_id'): void - { - } - - - /** - * if 'getData' is true, will returns 'inheritanceBy': the Member at the end of a sub-chain of - * memberships (based on $field for Top Circle's singleId) - * - * @param string $alias - * @param string $field - * @param string $aliasInheritedBy - * - * @throws RequestBuilderException - */ - public function leftJoinInheritedMembers(string $alias, string $field = '', string $aliasInheritedBy = ''): void - { - } - - - /** - * @throws RequestBuilderException - */ - public function limitToInheritedMemberships(string $alias, string $singleId, string $field = ''): void - { - } - - - /** - * limit the request to Members and Sub Members of a Circle. - * - * @param string $alias - * @param string $singleId - * @param int $level - * - * @throws RequestBuilderException - */ - public function limitToMembersByInheritance(string $alias, string $singleId, int $level = 0): void - { - } - - - /** - * if 'getData' is true, will returns 'inheritanceFrom': the Circle-As-Member of the Top Circle - * that explain the membership of a Member (based on $field for singleId) to a specific Circle - * - * // TODO: returns the link/path ? - * - * @param string $alias - * @param string $field - * - * @throws RequestBuilderException - */ - public function leftJoinMembersByInheritance(string $alias, string $field = ''): void - { - } - - - /** - * @param string $alias - * @param string $token - * - * @throws RequestBuilderException - */ - public function limitToShareToken(string $alias, string $token): void - { - } - - /** - * @param string $alias - * @param string $field - * - * @throws RequestBuilderException - */ - public function leftJoinShareToken(string $alias, string $field = ''): void - { - } - - - /** - * limit the result to the point of view of a FederatedUser - * - * @param string $alias - * @param IFederatedUser $user - * @param string $field - * @param string $helperAlias - * - * @return ICompositeExpression - * @throws RequestBuilderException - */ - public function limitToInitiator(string $alias, IFederatedUser $user, string $field = '', string $helperAlias = ''): ICompositeExpression - { - } - - - /** - * @param string $alias - */ - public function leftJoinCircleConfig(string $alias): void - { - } - - - /** - * Left join members to filter userId as initiator. - * - * @param string $alias - * @param IFederatedUser $initiator - * @param string $field - * @param string $helperAlias - * - * @throws RequestBuilderException - */ - public function leftJoinInitiator(string $alias, IFederatedUser $initiator, string $field = '', string $helperAlias = ''): void - { - } - - - public function completeProbeWithInitiator(string $alias, string $field = 'single_id', string $helperAlias = ''): void - { - } - - /** - * @param string $alias - * - * @return ICompositeExpression - * @throws RequestBuilderException - */ - protected function limitInitiatorVisibility(string $alias): ICompositeExpression - { - } - - - /** - * @param string $aliasCircle - * @param CircleProbe $probe - */ - public function filterCircles(string $aliasCircle, CircleProbe $probe): void - { - } - - - /** - * Link to storage/filecache - * - * @param string $aliasShare - * - * @throws RequestBuilderException - */ - public function leftJoinFileCache(string $aliasShare) - { - } - - - /** - * @param string $aliasShare - * @param string $aliasShareMemberships - * - * @throws RequestBuilderException - */ - public function leftJoinShareChild(string $aliasShare, string $aliasShareMemberships = '') - { - } - - - /** - * @param string $alias - * @param FederatedUser $federatedUser - * @param bool $reshares - */ - public function limitToShareOwner(string $alias, FederatedUser $federatedUser, bool $reshares, int $nodeId = 0): void - { - } - - - /** - * @param string $aliasMount - * @param string $aliasMountMemberships - * - * @throws RequestBuilderException - */ - public function leftJoinMountpoint(string $aliasMount, IFederatedUser $federatedUser, string $aliasMountMemberships = '') - { - } - - - /** - * @param array $path - * @param array $options - * - * @return CoreQueryBuilder&IQueryBuilder - */ - public function setOptions(array $path, array $options): self - { - } - - - /** - * @param string $base - * @param string $extension - * @param array|null $options - * - * @return string - * @throws RequestBuilderException - */ - public function generateAlias(string $base, string $extension, ?array &$options = []): string - { - } - - - /** - * @param string $prefix - * - * @return array - */ - public function getAvailablePath(string $prefix): array - { - } - - - /** - * @return array - */ - public function getSqlPath(): array - { - } - - - /** - * DataProbe uses this to set which data need to be extracted, based on self::$SQL_PATH. - * - * @param string $key - * @param array $path - * - * @return $this - */ - public function setSqlPath(string $key, array $path = []): self - { - } - - public function resetSqlPath(): self - { - } -} diff --git a/tests/stubs/oca_circles_events_circledestroyedevent.php b/tests/stubs/oca_circles_events_circledestroyedevent.php deleted file mode 100644 index 6b096488b..000000000 --- a/tests/stubs/oca_circles_events_circledestroyedevent.php +++ /dev/null @@ -1,39 +0,0 @@ - everyone can enter. moderator can add members. - * - CFG_OPEN | CFG_REQUEST => anyone can initiate a request to join the circle, moderator can - * add members - * - CFG_OPEN | CFG_INVITE => every one can enter, moderator must send invitation. - * - CFG_OPEN | CFG_INVITE | CFG_REQUEST => every one send a request, moderator must send invitation. - * - CFG_OPEN | CFG_FRIEND => useless - * - CFG_OPEN | CFG_FRIEND | * => useless - * - * - CFG_CIRCLE => no one can enter, moderator can add members. - * default config, this is only for code readability. - * - CFG_INVITE => no one can enter, moderator must send invitation. - * - CFG_FRIEND => no one can enter, but all members can add new member. - * - CFG_REQUEST => useless (use CFG_OPEN | CFG_REQUEST) - * - CFG_FRIEND | CFG_REQUEST => no one can join the circle, but all members can request a - * moderator to accept new member - * - CFG_FRIEND | CFG_INVITE => no one can join the circle, but all members can add new member. - * An invitation will be generated - * - CFG_FRIEND | CFG_INVITE | CFG_REQUEST => no one can join the circle, but all members can request a - * moderator to accept new member. An invitation will be generated - * - * @package OCA\Circles\Model - */ -class Circle extends ManagedModel implements IEntity, IDeserializable, IQueryRow, JsonSerializable { - use TArrayTools; - use TDeserialize; - - public const FLAGS_SHORT = 1; - public const FLAGS_LONG = 2; - - // specific value - public const CFG_CIRCLE = 0; // only for code readability. Circle is locked by default. - public const CFG_SINGLE = 1; // Circle with only one single member. - public const CFG_PERSONAL = 2; // Personal circle, only the owner can see it. - - // bitwise - public const CFG_SYSTEM = 4; // System Circle (not managed by the official front-end). Meaning some config are limited - public const CFG_VISIBLE = 8; // Visible to everyone, if not visible, people have to know its name to be able to find it - public const CFG_OPEN = 16; // Circle is open, people can join - public const CFG_INVITE = 32; // Adding a member generate an invitation that needs to be accepted - public const CFG_REQUEST = 64; // Request to join Circles needs to be confirmed by a moderator - public const CFG_FRIEND = 128; // Members of the circle can invite their friends - public const CFG_PROTECTED = 256; // Password protected to join/request - public const CFG_NO_OWNER = 512; // no owner, only members - public const CFG_HIDDEN = 1024; // hidden from listing, but available as a share entity - public const CFG_BACKEND = 2048; // Fully hidden, only backend Circles - public const CFG_LOCAL = 4096; // Local even on GlobalScale - public const CFG_ROOT = 8192; // Circle cannot be inside another Circle - public const CFG_CIRCLE_INVITE = 16384; // Circle must confirm when invited in another circle - public const CFG_FEDERATED = 32768; // Federated - public const CFG_MOUNTPOINT = 65536; // Generate a Files folder for this Circle - public const CFG_APP = 131072; // Some features are not available to the OCS API (ie. destroying Circle) - public static $DEF_CFG_MAX = 262143; - - - /** - * Note: When editing those values, update lib/Application/Capabilities.php - * - * @see Capabilities::getCapabilitiesCircleConstants() - * @var array - */ - public static $DEF_CFG = [ - 1 => 'S|Single', - 2 => 'P|Personal', - 4 => 'Y|System', - 8 => 'V|Visible', - 16 => 'O|Open', - 32 => 'I|Invite', - 64 => 'JR|Join Request', - 128 => 'F|Friends', - 256 => 'PP|Password Protected', - 512 => 'NO|No Owner', - 1024 => 'H|Hidden', - 2048 => 'T|Backend', - 4096 => 'L|Local', - 8192 => 'T|Root', - 16384 => 'CI|Circle Invite', - 32768 => 'F|Federated', - 65536 => 'M|Nountpoint', - 131072 => 'A|App' - ]; - - - /** - * Note: When editing those values, update lib/AppInfo/Capabilities.php - * - * @see Capabilities::getCapabilitiesCircleConstants() - * @var array - */ - public static $DEF_SOURCE = [ - 1 => 'Nextcloud Account', - 2 => 'Nextcloud Group', - 4 => 'Email Address', - 8 => 'Contact', - 16 => 'Team', - 10000 => 'Nextcloud App', - 10001 => 'Teams App', - 10002 => 'Admin Command Line', - 11000 => '3rd party app', - 11010 => 'Collectives App' - ]; - - - public static $DEF_CFG_CORE_FILTER = [ - 1, - 2, - 4 - ]; - - public static $DEF_CFG_SYSTEM_FILTER = [ - 512, - 1024, - 2048 - ]; - - - /** - * Circle constructor. - */ - public function __construct() { - } - - /** - * @param string $singleId - * - * @return self - */ - public function setSingleId(string $singleId): self - { - } - - /** - * @return string - */ - public function getSingleId(): string - { - } - - /** - * @return string - * @deprecated - removed in NC23 - */ - public function getUniqueId(): string - { - } - - - /** - * @param int $config - * - * @return self - */ - public function setConfig(int $config): self - { - } - - /** - * @return int - */ - public function getConfig(): int - { - } - - /** - * @param int $flag - * @param int $test - * - * @return bool - */ - public function isConfig(int $flag, int $test = 0): bool - { - } - - /** - * @param int $flag - */ - public function addConfig(int $flag): void - { - } - - /** - * @param int $flag - */ - public function remConfig(int $flag): void - { - } - - - /** - * @param string $name - * - * @return self - */ - public function setName(string $name): self - { - } - - /** - * @return string - */ - public function getName(): string - { - } - - - /** - * @param string $displayName - * - * @return self - */ - public function setDisplayName(string $displayName): self - { - } - - /** - * @return string - */ - public function getDisplayName(): string - { - } - - - /** - * @param string $sanitizedName - * - * @return Circle - */ - public function setSanitizedName(string $sanitizedName): self - { - } - - /** - * @return string - */ - public function getSanitizedName(): string - { - } - - - /** - * @param int $source - * - * @return Circle - */ - public function setSource(int $source): self - { - } - - /** - * @return int - */ - public function getSource(): int - { - } - - - /** - * @param ?Member $owner - * - * @return self - */ - public function setOwner(?Member $owner): self - { - } - - /** - * @return Member - */ - public function getOwner(): Member - { - } - - /** - * @return bool - */ - public function hasOwner(): bool - { - } - - - /** - * @return bool - */ - public function hasMembers(): bool - { - } - - /** - * @param Member[] $members - * - * @return self - */ - public function setMembers(array $members): self - { - } - - /** - * @return Member[] - */ - public function getMembers(int $limit = 0): array - { - } - - - /** - * @param array $members - * @param bool $detailed - * - * @return self - */ - public function setInheritedMembers(array $members, bool $detailed): self - { - } - - /** - * @param Member[] $members - * - * @return Circle - */ - public function addInheritedMembers(array $members): self - { - } - - - /** - * if $remote is true, it will returns also details on inherited members from remote+locals Circles. - * This should be used only if extra details are required (mail address ?) as it will send a request to - * the remote instance if the circleId is not locally known. - * because of the resource needed to retrieve this data, $remote=true should not be used on main process ! - * - * @param bool $detailed - * @param bool $remote - * - * @return Member[] - * @throws FederatedItemException - * @throws RemoteInstanceException - * @throws RemoteNotFoundException - * @throws RemoteResourceNotFoundException - * @throws RequestBuilderException - * @throws UnknownRemoteException - */ - public function getInheritedMembers(bool $detailed = false, bool $remote = false): array - { - } - - - /** - * @return bool - */ - public function hasMemberships(): bool - { - } - - /** - * @param array $memberships - * - * @return self - */ - public function setMemberships(array $memberships): IEntity - { - } - - /** - * @return Membership[] - */ - public function getMemberships(): array - { - } - - - /** - * @param string $singleId - * @param bool $detailed - * - * @return Membership - * @throws MembershipNotFoundException - * @throws RequestBuilderException - */ - public function getLink(string $singleId, bool $detailed = false): Membership - { - } - - - /** - * @param Member|null $initiator - * - * @return Circle - */ - public function setInitiator(?Member $initiator): self - { - } - - /** - * @return Member - */ - public function getInitiator(): Member - { - } - - /** - * @return bool - */ - public function hasInitiator(): bool - { - } - - /** - * @param Member|null $directInitiator - * - * @return $this - */ - public function setDirectInitiator(?Member $directInitiator): self - { - } - - - /** - * @param string $instance - * - * @return Circle - */ - public function setInstance(string $instance): self - { - } - - /** - * @return string - * @throws OwnerNotFoundException - */ - public function getInstance(): string - { - } - - - /** - * @return bool - * @throws OwnerNotFoundException - */ - public function isLocal(): bool - { - } - - - /** - * @param int $population - * - * @return Circle - */ - public function setPopulation(int $population): self - { - } - - /** - * @return int - */ - public function getPopulation(): int - { - } - - - /** - * @param int $population - * - * @return Circle - */ - public function setPopulationInherited(int $population): self - { - } - - /** - * @return int - */ - public function getPopulationInherited(): int - { - } - - - /** - * @param array $settings - * - * @return self - */ - public function setSettings(array $settings): self - { - } - - /** - * @return array - */ - public function getSettings(): array - { - } - - - /** - * @param string $description - * - * @return self - */ - public function setDescription(string $description): self - { - } - - /** - * @return string - */ - public function getDescription(): string - { - } - - - /** - * @return string - */ - public function getUrl(): string - { - } - - - /** - * @param int $contactAddressBook - * - * @return self - */ - public function setContactAddressBook(int $contactAddressBook): self - { - } - - /** - * @return int - */ - public function getContactAddressBook(): int - { - } - - - /** - * @param string $contactGroupName - * - * @return self - */ - public function setContactGroupName(string $contactGroupName): self - { - } - - /** - * @return string - */ - public function getContactGroupName(): string - { - } - - - /** - * @param int $creation - * - * @return self - */ - public function setCreation(int $creation): self - { - } - - /** - * @return int - */ - public function getCreation(): int - { - } - - - /** - * @param array $data - * - * @return $this - * @throws InvalidItemException - */ - public function import(array $data): IDeserializable - { - } - - - /** - * @return array - * @throws FederatedItemException - * @throws RemoteInstanceException - * @throws RemoteNotFoundException - * @throws RemoteResourceNotFoundException - * @throws RequestBuilderException - * @throws UnknownRemoteException - */ - public function jsonSerialize(): array - { - } - - - /** - * @param array $data - * @param string $prefix - * - * @return IQueryRow - * @throws CircleNotFoundException - */ - public function importFromDatabase(array $data, string $prefix = ''): IQueryRow - { - } - - - /** - * @param Circle $circle - * - * @return bool - * @throws OwnerNotFoundException - */ - public function compareWith(Circle $circle): bool - { - } - - - /** - * @param Circle $circle - * @param int $display - * - * @return array - */ - public static function getCircleFlags(Circle $circle, int $display = self::FLAGS_LONG): array - { - } -} diff --git a/tests/stubs/oca_circles_model_federateduser.php b/tests/stubs/oca_circles_model_federateduser.php deleted file mode 100644 index 6aaad5850..000000000 --- a/tests/stubs/oca_circles_model_federateduser.php +++ /dev/null @@ -1,309 +0,0 @@ - 'single', - 1 => 'user', - 2 => 'group', - 4 => 'mail', - 8 => 'contact', - 16 => 'circle', - 10000 => 'app' - ]; - - /** - * Note: When editing those values, update lib/Application/Capabilities.php - * - * @see Capabilities::generateConstantsMember() - */ - public const STATUS_INVITED = 'Invited'; - public const STATUS_REQUEST = 'Requesting'; - public const STATUS_MEMBER = 'Member'; - public const STATUS_BLOCKED = 'Blocked'; - - - /** - * Note: When editing those values, update lib/Application/Capabilities.php - * - * @see Capabilities::generateConstantsMember() - * @var array - */ - public static $DEF_LEVEL = [ - 1 => 'Member', - 4 => 'Moderator', - 8 => 'Admin', - 9 => 'Owner' - ]; - - - public static $DEF_TYPE_MAX = 31; - - - /** - * Member constructor. - */ - public function __construct() { - } - - - /** - * @param string $id - * - * @return $this - */ - public function setId(string $id): self - { - } - - /** - * @return string - */ - public function getId(): string - { - } - - - /** - * @param string $circleId - * - * @return Member - */ - public function setCircleId(string $circleId): self - { - } - - /** - * @return string - */ - public function getCircleId(): string - { - } - - - /** - * This should replace user_id, user_type and instance; and will use the data from Circle with - * Config=CFG_SINGLE - * - * @param string $singleId - * - * @return $this - */ - public function setSingleId(string $singleId): self - { - } - - /** - * @return string - */ - public function getSingleId(): string - { - } - - - /** - * @param string $userId - * - * @return Member - */ - public function setUserId(string $userId): self - { - } - - /** - * @return string - */ - public function getUserId(): string - { - } - - - /** - * @param int $userType - * - * @return Member - */ - public function setUserType(int $userType): self - { - } - - /** - * @return int - */ - public function getUserType(): int - { - } - - /** - * @return int - * @deprecated 22.0.0 Use `getUserType()` instead - */ - public function getType(): int - { - } - - - /** - * @param string $instance - * - * @return Member - */ - public function setInstance(string $instance): self - { - } - - /** - * @return string - */ - public function getInstance(): string - { - } - - - /** - * @return bool - */ - public function isLocal(): bool - { - } - - - /** - * @param FederatedUser $invitedBy - * - * @return Member - */ - public function setInvitedBy(FederatedUser $invitedBy): Member - { - } - - /** - * @return FederatedUser - */ - public function getInvitedBy(): FederatedUser - { - } - - /** - * @return bool - */ - public function hasInvitedBy(): bool - { - } - - - /** - * @return bool - */ - public function hasRemoteInstance(): bool - { - } - - /** - * @param RemoteInstance $remoteInstance - * - * @return Member - */ - public function setRemoteInstance(RemoteInstance $remoteInstance): self - { - } - - /** - * @return RemoteInstance - */ - public function getRemoteInstance(): RemoteInstance - { - } - - - /** - * @return bool - */ - public function hasBasedOn(): bool - { - } - - /** - * @param ?Circle $basedOn - * - * @return $this - */ - public function setBasedOn(?Circle $basedOn): self - { - } - - /** - * @return Circle - */ - public function getBasedOn(): Circle - { - } - - - /** - * @return bool - */ - public function hasInheritedBy(): bool - { - } - - /** - * @param FederatedUser $inheritedBy - * - * @return $this - */ - public function setInheritedBy(FederatedUser $inheritedBy): self - { - } - - /** - * @return FederatedUser - */ - public function getInheritedBy(): FederatedUser - { - } - - - /** - * @return bool - */ - public function hasInheritanceFrom(): bool - { - } - - /** - * @param Member $inheritanceFrom - * - * @return $this - */ - public function setInheritanceFrom(Member $inheritanceFrom): self - { - } - - /** - * @return Member|null - */ - public function getInheritanceFrom(): ?Member - { - } - - - /** - * @param int $level - * - * @return Member - */ - public function setLevel(int $level): self - { - } - - /** - * @return int - */ - public function getLevel(): int - { - } - - - /** - * @param string $status - * - * @return Member - */ - public function setStatus(string $status): self - { - } - - /** - * @return string - */ - public function getStatus(): string - { - } - - - /** - * @param array $notes - * - * @return Member - */ - public function setNotes(array $notes): self - { - } - - /** - * @return array - */ - public function getNotes(): array - { - } - - - /** - * @param string $key - * - * @return string - */ - public function getNote(string $key): string - { - } - - /** - * @param string $key - * - * @return array - */ - public function getNoteArray(string $key): array - { - } - - /** - * @param string $key - * @param string $note - * - * @return $this - */ - public function setNote(string $key, string $note): self - { - } - - /** - * @param string $key - * @param array $note - * - * @return $this - */ - public function setNoteArray(string $key, array $note): self - { - } - - /** - * @param string $key - * @param JsonSerializable $obj - * - * @return $this - */ - public function setNoteObj(string $key, JsonSerializable $obj): self - { - } - - - /** - * @param string $displayName - * - * @return $this - */ - public function setDisplayName(string $displayName): self - { - } - - - /** - * @param int $displayUpdate - * - * @return Member - */ - public function setDisplayUpdate(int $displayUpdate): self - { - } - - /** - * @return int - */ - public function getDisplayUpdate(): int - { - } - - - /** - * @return string - */ - public function getDisplayName(): string - { - } - - - /** - * @param string $contactId - * - * @return Member - */ - public function setContactId(string $contactId): self - { - } - - /** - * @return string - */ - public function getContactId(): string - { - } - - - /** - * @param string $contactMeta - * - * @return Member - */ - public function setContactMeta(string $contactMeta): self - { - } - - /** - * @return string - */ - public function getContactMeta(): string - { - } - - - /** - * @param Circle $circle - * - * @return self - */ - public function setCircle(Circle $circle): self - { - } - - /** - * @return Circle - */ - public function getCircle(): Circle - { - } - - /** - * @return bool - */ - public function hasCircle(): bool - { - } - - - /** - * @param int $joined - * - * @return Member - */ - public function setJoined(int $joined): self - { - } - - /** - * @return int - */ - public function getJoined(): int - { - } - - - /** - * @return bool - */ - public function hasMemberships(): bool - { - } - - /** - * @param array $memberships - * - * @return self - */ - public function setMemberships(array $memberships): IEntity - { - } - - /** - * @return Membership[] - */ - public function getMemberships(): array - { - } - - - /** - * @param string $singleId - * @param bool $detailed - * - * @return Membership - * @throws MembershipNotFoundException - * @throws RequestBuilderException - */ - public function getLink(string $singleId, bool $detailed = false): Membership - { - } - - /** - * @param string $circleId - * @param bool $detailed - * - * @return Membership - * @throws MembershipNotFoundException - * @throws RequestBuilderException - * @deprecated - use getLink(); - */ - public function getMembership(string $circleId, bool $detailed = false): Membership - { - } - - - /** - * @param Member $member - * @param bool $full - * - * @return bool - */ - public function compareWith(Member $member, bool $full = true): bool - { - } - - - /** - * @param array $data - * - * @return $this - * @throws InvalidItemException - */ - public function import(array $data): IDeserializable - { - } - - - /** - * @param array $data - * @param string $prefix - * - * @return IQueryRow - * @throws MemberNotFoundException - */ - public function importFromDatabase(array $data, string $prefix = ''): IQueryRow - { - } - - - /** - * @return string[] - * @throws UnknownInterfaceException - */ - public function jsonSerialize(): array - { - } - - - /** - * @param int $level - * - * @return int - * @throws ParseMemberLevelException - */ - public static function parseLevelInt(int $level): int - { - } - - - /** - * @param string $levelString - * - * @return int - * @throws ParseMemberLevelException - */ - public static function parseLevelString(string $levelString): int - { - } - - /** - * @param string $typeString - * - * @return int - * @throws UserTypeNotFoundException - */ - public static function parseTypeString(string $typeString): int - { - } -} diff --git a/tests/stubs/oca_circles_model_probes_basicprobe.php b/tests/stubs/oca_circles_model_probes_basicprobe.php deleted file mode 100644 index befb73b2c..000000000 --- a/tests/stubs/oca_circles_model_probes_basicprobe.php +++ /dev/null @@ -1,215 +0,0 @@ - $default - * - * @return $this - */ - public function generateSelectAlias(array $fields, string $alias, string $prefix, array $default = []): self - { - } -} diff --git a/tests/stubs/oca_circles_tools_db_iqueryrow.php b/tests/stubs/oca_circles_tools_db_iqueryrow.php deleted file mode 100644 index 90814bdf9..000000000 --- a/tests/stubs/oca_circles_tools_db_iqueryrow.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ - public function getMetadata(): array - { - } -} diff --git a/tests/stubs/oca_files_versions_expiration.php b/tests/stubs/oca_files_versions_expiration.php deleted file mode 100644 index 2c93ef2da..000000000 --- a/tests/stubs/oca_files_versions_expiration.php +++ /dev/null @@ -1,65 +0,0 @@ - value mapping. - * @since 29.0.0 - */ -interface IMetadataVersionBackend { - /** - * Sets a key value pair in the metadata column corresponding to the node's version. - * - * @param Node $node the node that triggered the Metadata event listener, aka, the file version - * @param int $revision the key for the json value of the metadata column - * @param string $key the key for the json value of the metadata column - * @param string $value the value that corresponds to the key in the metadata column - * @since 29.0.0 - */ - public function setMetadataValue(Node $node, int $revision, string $key, string $value): void - { - } -} diff --git a/tests/stubs/oca_files_versions_versions_ineedsyncversionbackend.php b/tests/stubs/oca_files_versions_versions_ineedsyncversionbackend.php deleted file mode 100644 index 4f82a3db3..000000000 --- a/tests/stubs/oca_files_versions_versions_ineedsyncversionbackend.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Command; - -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Attribute\AsCommand; -use Symfony\Component\Console\Completion\CompletionInput; -use Symfony\Component\Console\Completion\CompletionSuggestions; -use Symfony\Component\Console\Completion\Suggestion; -use Symfony\Component\Console\Exception\ExceptionInterface; -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Exception\LogicException; -use Symfony\Component\Console\Helper\HelperInterface; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Base class for all commands. - * - * @author Fabien Potencier - */ -class Command -{ - // see https://tldp.org/LDP/abs/html/exitcodes.html - public const SUCCESS = 0; - public const FAILURE = 1; - public const INVALID = 2; - - /** - * @var string|null The default command name - * - * @deprecated since Symfony 6.1, use the AsCommand attribute instead - */ - protected static $defaultName; - - /** - * @var string|null The default command description - * - * @deprecated since Symfony 6.1, use the AsCommand attribute instead - */ - protected static $defaultDescription; - - public static function getDefaultName(): ?string - { - } - - public static function getDefaultDescription(): ?string - { - } - - /** - * @param string|null $name The name of the command; passing null means it must be set in configure() - * - * @throws LogicException When the command name is empty - */ - public function __construct(?string $name = null) - { - } - - /** - * Ignores validation errors. - * - * This is mainly useful for the help command. - * - * @return void - */ - public function ignoreValidationErrors() - { - } - - /** - * @return void - */ - public function setApplication(?Application $application = null) - { - } - - /** - * @return void - */ - public function setHelperSet(HelperSet $helperSet) - { - } - - /** - * Gets the helper set. - */ - public function getHelperSet(): ?HelperSet - { - } - - /** - * Gets the application instance for this command. - */ - public function getApplication(): ?Application - { - } - - /** - * Checks whether the command is enabled or not in the current environment. - * - * Override this to check for x or y and return false if the command cannot - * run properly under the current conditions. - * - * @return bool - */ - public function isEnabled() - { - } - - /** - * Configures the current command. - * - * @return void - */ - protected function configure() - { - } - - /** - * Executes the current command. - * - * This method is not abstract because you can use this class - * as a concrete class. In this case, instead of defining the - * execute() method, you set the code to execute by passing - * a Closure to the setCode() method. - * - * @return int 0 if everything went fine, or an exit code - * - * @throws LogicException When this abstract method is not implemented - * - * @see setCode() - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - } - - /** - * Interacts with the user. - * - * This method is executed before the InputDefinition is validated. - * This means that this is the only place where the command can - * interactively ask for values of missing required arguments. - * - * @return void - */ - protected function interact(InputInterface $input, OutputInterface $output) - { - } - - /** - * Initializes the command after the input has been bound and before the input - * is validated. - * - * This is mainly useful when a lot of commands extends one main command - * where some things need to be initialized based on the input arguments and options. - * - * @see InputInterface::bind() - * @see InputInterface::validate() - * - * @return void - */ - protected function initialize(InputInterface $input, OutputInterface $output) - { - } - - /** - * Runs the command. - * - * The code to execute is either defined directly with the - * setCode() method or by overriding the execute() method - * in a sub-class. - * - * @return int The command exit code - * - * @throws ExceptionInterface When input binding fails. Bypass this by calling {@link ignoreValidationErrors()}. - * - * @see setCode() - * @see execute() - */ - public function run(InputInterface $input, OutputInterface $output): int - { - } - - /** - * Adds suggestions to $suggestions for the current completion input (e.g. option or argument). - */ - public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void - { - } - - /** - * Sets the code to execute when running this command. - * - * If this method is used, it overrides the code defined - * in the execute() method. - * - * @param callable $code A callable(InputInterface $input, OutputInterface $output) - * - * @return $this - * - * @throws InvalidArgumentException - * - * @see execute() - */ - public function setCode(callable $code): static - { - } - - /** - * Merges the application definition with the command definition. - * - * This method is not part of public API and should not be used directly. - * - * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments - * - * @internal - */ - public function mergeApplicationDefinition(bool $mergeArgs = true): void - { - } - - /** - * Sets an array of argument and option instances. - * - * @return $this - */ - public function setDefinition(array|InputDefinition $definition): static - { - } - - /** - * Gets the InputDefinition attached to this Command. - */ - public function getDefinition(): InputDefinition - { - } - - /** - * Gets the InputDefinition to be used to create representations of this Command. - * - * Can be overridden to provide the original command representation when it would otherwise - * be changed by merging with the application InputDefinition. - * - * This method is not part of public API and should not be used directly. - */ - public function getNativeDefinition(): InputDefinition - { - } - - /** - * Adds an argument. - * - * @param $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL - * @param $default The default value (for InputArgument::OPTIONAL mode only) - * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion - * - * @return $this - * - * @throws InvalidArgumentException When argument mode is not valid - */ - public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null): static - { - } - - /** - * Adds an option. - * - * @param $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts - * @param $mode The option mode: One of the InputOption::VALUE_* constants - * @param $default The default value (must be null for InputOption::VALUE_NONE) - * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion - * - * @return $this - * - * @throws InvalidArgumentException If option mode is invalid or incompatible - */ - public function addOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null): static - { - } - - /** - * Sets the name of the command. - * - * This method can set both the namespace and the name if - * you separate them by a colon (:) - * - * $command->setName('foo:bar'); - * - * @return $this - * - * @throws InvalidArgumentException When the name is invalid - */ - public function setName(string $name): static - { - } - - /** - * Sets the process title of the command. - * - * This feature should be used only when creating a long process command, - * like a daemon. - * - * @return $this - */ - public function setProcessTitle(string $title): static - { - } - - /** - * Returns the command name. - */ - public function getName(): ?string - { - } - - /** - * @param bool $hidden Whether or not the command should be hidden from the list of commands - * - * @return $this - */ - public function setHidden(bool $hidden = true): static - { - } - - /** - * @return bool whether the command should be publicly shown or not - */ - public function isHidden(): bool - { - } - - /** - * Sets the description for the command. - * - * @return $this - */ - public function setDescription(string $description): static - { - } - - /** - * Returns the description for the command. - */ - public function getDescription(): string - { - } - - /** - * Sets the help for the command. - * - * @return $this - */ - public function setHelp(string $help): static - { - } - - /** - * Returns the help for the command. - */ - public function getHelp(): string - { - } - - /** - * Returns the processed help for the command replacing the %command.name% and - * %command.full_name% patterns with the real values dynamically. - */ - public function getProcessedHelp(): string - { - } - - /** - * Sets the aliases for the command. - * - * @param string[] $aliases An array of aliases for the command - * - * @return $this - * - * @throws InvalidArgumentException When an alias is invalid - */ - public function setAliases(iterable $aliases): static - { - } - - /** - * Returns the aliases for the command. - */ - public function getAliases(): array - { - } - - /** - * Returns the synopsis for the command. - * - * @param bool $short Whether to show the short version of the synopsis (with options folded) or not - */ - public function getSynopsis(bool $short = false): string - { - } - - /** - * Add a command usage example, it'll be prefixed with the command name. - * - * @return $this - */ - public function addUsage(string $usage): static - { - } - - /** - * Returns alternative usages of the command. - */ - public function getUsages(): array - { - } - - /** - * Gets a helper instance by name. - * - * @return HelperInterface - * - * @throws LogicException if no HelperSet is defined - * @throws InvalidArgumentException if the helper is not defined - */ - public function getHelper(string $name): mixed - { - } -} diff --git a/tests/stubs/symfony_component_console_helper_table.php b/tests/stubs/symfony_component_console_helper_table.php deleted file mode 100644 index 373b3ffff..000000000 --- a/tests/stubs/symfony_component_console_helper_table.php +++ /dev/null @@ -1,218 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Helper; - -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Exception\RuntimeException; -use Symfony\Component\Console\Formatter\OutputFormatter; -use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface; -use Symfony\Component\Console\Output\ConsoleSectionOutput; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Provides helpers to display a table. - * - * @author Fabien Potencier - * @author Саша Стаменковић - * @author Abdellatif Ait boudad - * @author Max Grigorian - * @author Dany Maillard - */ -class Table -{ - private const SEPARATOR_TOP = 0; - private const SEPARATOR_TOP_BOTTOM = 1; - private const SEPARATOR_MID = 2; - private const SEPARATOR_BOTTOM = 3; - private const BORDER_OUTSIDE = 0; - private const BORDER_INSIDE = 1; - private const DISPLAY_ORIENTATION_DEFAULT = 'default'; - private const DISPLAY_ORIENTATION_HORIZONTAL = 'horizontal'; - private const DISPLAY_ORIENTATION_VERTICAL = 'vertical'; - - public function __construct(OutputInterface $output) - { - } - - /** - * Sets a style definition. - * - * @return void - */ - public static function setStyleDefinition(string $name, TableStyle $style) - { - } - - /** - * Gets a style definition by name. - */ - public static function getStyleDefinition(string $name): TableStyle - { - } - - /** - * Sets table style. - * - * @return $this - */ - public function setStyle(TableStyle|string $name): static - { - } - - /** - * Gets the current table style. - */ - public function getStyle(): TableStyle - { - } - - /** - * Sets table column style. - * - * @param TableStyle|string $name The style name or a TableStyle instance - * - * @return $this - */ - public function setColumnStyle(int $columnIndex, TableStyle|string $name): static - { - } - - /** - * Gets the current style for a column. - * - * If style was not set, it returns the global table style. - */ - public function getColumnStyle(int $columnIndex): TableStyle - { - } - - /** - * Sets the minimum width of a column. - * - * @return $this - */ - public function setColumnWidth(int $columnIndex, int $width): static - { - } - - /** - * Sets the minimum width of all columns. - * - * @return $this - */ - public function setColumnWidths(array $widths): static - { - } - - /** - * Sets the maximum width of a column. - * - * Any cell within this column which contents exceeds the specified width will be wrapped into multiple lines, while - * formatted strings are preserved. - * - * @return $this - */ - public function setColumnMaxWidth(int $columnIndex, int $width): static - { - } - - /** - * @return $this - */ - public function setHeaders(array $headers): static - { - } - - /** - * @return $this - */ - public function setRows(array $rows) - { - } - - /** - * @return $this - */ - public function addRows(array $rows): static - { - } - - /** - * @return $this - */ - public function addRow(TableSeparator|array $row): static - { - } - - /** - * Adds a row to the table, and re-renders the table. - * - * @return $this - */ - public function appendRow(TableSeparator|array $row): static - { - } - - /** - * @return $this - */ - public function setRow(int|string $column, array $row): static - { - } - - /** - * @return $this - */ - public function setHeaderTitle(?string $title): static - { - } - - /** - * @return $this - */ - public function setFooterTitle(?string $title): static - { - } - - /** - * @return $this - */ - public function setHorizontal(bool $horizontal = true): static - { - } - - /** - * @return $this - */ - public function setVertical(bool $vertical = true): static - { - } - - /** - * Renders table to output. - * - * Example: - * - * +---------------+-----------------------+------------------+ - * | ISBN | Title | Author | - * +---------------+-----------------------+------------------+ - * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | - * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | - * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | - * +---------------+-----------------------+------------------+ - * - * @return void - */ - public function render() - { - } -} diff --git a/tests/stubs/symfony_component_console_input_inputargument.php b/tests/stubs/symfony_component_console_input_inputargument.php deleted file mode 100644 index 8a106813a..000000000 --- a/tests/stubs/symfony_component_console_input_inputargument.php +++ /dev/null @@ -1,107 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Input; - -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Completion\CompletionInput; -use Symfony\Component\Console\Completion\CompletionSuggestions; -use Symfony\Component\Console\Completion\Suggestion; -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Exception\LogicException; - -/** - * Represents a command line argument. - * - * @author Fabien Potencier - */ -class InputArgument -{ - public const REQUIRED = 1; - public const OPTIONAL = 2; - public const IS_ARRAY = 4; - - /** - * @param string $name The argument name - * @param int|null $mode The argument mode: a bit mask of self::REQUIRED, self::OPTIONAL and self::IS_ARRAY - * @param string $description A description text - * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only) - * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion - * - * @throws InvalidArgumentException When argument mode is not valid - */ - public function __construct(string $name, ?int $mode = null, string $description = '', string|bool|int|float|array|null $default = null, \Closure|array $suggestedValues = []) - { - } - - /** - * Returns the argument name. - */ - public function getName(): string - { - } - - /** - * Returns true if the argument is required. - * - * @return bool true if parameter mode is self::REQUIRED, false otherwise - */ - public function isRequired(): bool - { - } - - /** - * Returns true if the argument can take multiple values. - * - * @return bool true if mode is self::IS_ARRAY, false otherwise - */ - public function isArray(): bool - { - } - - /** - * Sets the default value. - * - * @return void - * - * @throws LogicException When incorrect default value is given - */ - public function setDefault(string|bool|int|float|array|null $default = null) - { - } - - /** - * Returns the default value. - */ - public function getDefault(): string|bool|int|float|array|null - { - } - - public function hasCompletion(): bool - { - } - - /** - * Adds suggestions to $suggestions for the current completion input. - * - * @see Command::complete() - */ - public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void - { - } - - /** - * Returns the description text. - */ - public function getDescription(): string - { - } -} diff --git a/tests/stubs/symfony_component_console_input_inputinterface.php b/tests/stubs/symfony_component_console_input_inputinterface.php deleted file mode 100644 index 352937c62..000000000 --- a/tests/stubs/symfony_component_console_input_inputinterface.php +++ /dev/null @@ -1,180 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Input; - -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Exception\RuntimeException; - -/** - * InputInterface is the interface implemented by all input classes. - * - * @author Fabien Potencier - * - * @method string __toString() Returns a stringified representation of the args passed to the command. - * InputArguments MUST be escaped as well as the InputOption values passed to the command. - */ -interface InputInterface -{ - /** - * Returns the first argument from the raw parameters (not parsed). - */ - public function getFirstArgument(): ?string - { - } - - /** - * Returns true if the raw parameters (not parsed) contain a value. - * - * This method is to be used to introspect the input parameters - * before they have been validated. It must be used carefully. - * Does not necessarily return the correct result for short options - * when multiple flags are combined in the same option. - * - * @param string|array $values The values to look for in the raw parameters (can be an array) - * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal - */ - public function hasParameterOption(string|array $values, bool $onlyParams = false): bool - { - } - - /** - * Returns the value of a raw option (not parsed). - * - * This method is to be used to introspect the input parameters - * before they have been validated. It must be used carefully. - * Does not necessarily return the correct result for short options - * when multiple flags are combined in the same option. - * - * @param string|array $values The value(s) to look for in the raw parameters (can be an array) - * @param string|bool|int|float|array|null $default The default value to return if no result is found - * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal - * - * @return mixed - */ - public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false) - { - } - - /** - * Binds the current Input instance with the given arguments and options. - * - * @return void - * - * @throws RuntimeException - */ - public function bind(InputDefinition $definition) - { - } - - /** - * Validates the input. - * - * @return void - * - * @throws RuntimeException When not enough arguments are given - */ - public function validate() - { - } - - /** - * Returns all the given arguments merged with the default values. - * - * @return array - */ - public function getArguments(): array - { - } - - /** - * Returns the argument value for a given argument name. - * - * @return mixed - * - * @throws InvalidArgumentException When argument given doesn't exist - */ - public function getArgument(string $name) - { - } - - /** - * Sets an argument value by name. - * - * @return void - * - * @throws InvalidArgumentException When argument given doesn't exist - */ - public function setArgument(string $name, mixed $value) - { - } - - /** - * Returns true if an InputArgument object exists by name or position. - */ - public function hasArgument(string $name): bool - { - } - - /** - * Returns all the given options merged with the default values. - * - * @return array - */ - public function getOptions(): array - { - } - - /** - * Returns the option value for a given option name. - * - * @return mixed - * - * @throws InvalidArgumentException When option given doesn't exist - */ - public function getOption(string $name) - { - } - - /** - * Sets an option value by name. - * - * @return void - * - * @throws InvalidArgumentException When option given doesn't exist - */ - public function setOption(string $name, mixed $value) - { - } - - /** - * Returns true if an InputOption object exists by name. - */ - public function hasOption(string $name): bool - { - } - - /** - * Is this input means interactive? - */ - public function isInteractive(): bool - { - } - - /** - * Sets the input interactivity. - * - * @return void - */ - public function setInteractive(bool $interactive) - { - } -} diff --git a/tests/stubs/symfony_component_console_input_inputoption.php b/tests/stubs/symfony_component_console_input_inputoption.php deleted file mode 100644 index ab5680e57..000000000 --- a/tests/stubs/symfony_component_console_input_inputoption.php +++ /dev/null @@ -1,159 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Input; - -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Completion\CompletionInput; -use Symfony\Component\Console\Completion\CompletionSuggestions; -use Symfony\Component\Console\Completion\Suggestion; -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Exception\LogicException; - -/** - * Represents a command line option. - * - * @author Fabien Potencier - */ -class InputOption -{ - /** - * Do not accept input for the option (e.g. --yell). This is the default behavior of options. - */ - public const VALUE_NONE = 1; - - /** - * A value must be passed when the option is used (e.g. --iterations=5 or -i5). - */ - public const VALUE_REQUIRED = 2; - - /** - * The option may or may not have a value (e.g. --yell or --yell=loud). - */ - public const VALUE_OPTIONAL = 4; - - /** - * The option accepts multiple values (e.g. --dir=/foo --dir=/bar). - */ - public const VALUE_IS_ARRAY = 8; - - /** - * The option may have either positive or negative value (e.g. --ansi or --no-ansi). - */ - public const VALUE_NEGATABLE = 16; - - /** - * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts - * @param int|null $mode The option mode: One of the VALUE_* constants - * @param string|bool|int|float|array|null $default The default value (must be null for self::VALUE_NONE) - * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion - * - * @throws InvalidArgumentException If option mode is invalid or incompatible - */ - public function __construct(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', string|bool|int|float|array|null $default = null, array|\Closure $suggestedValues = []) - { - } - - /** - * Returns the option shortcut. - */ - public function getShortcut(): ?string - { - } - - /** - * Returns the option name. - */ - public function getName(): string - { - } - - /** - * Returns true if the option accepts a value. - * - * @return bool true if value mode is not self::VALUE_NONE, false otherwise - */ - public function acceptValue(): bool - { - } - - /** - * Returns true if the option requires a value. - * - * @return bool true if value mode is self::VALUE_REQUIRED, false otherwise - */ - public function isValueRequired(): bool - { - } - - /** - * Returns true if the option takes an optional value. - * - * @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise - */ - public function isValueOptional(): bool - { - } - - /** - * Returns true if the option can take multiple values. - * - * @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise - */ - public function isArray(): bool - { - } - - public function isNegatable(): bool - { - } - - /** - * @return void - */ - public function setDefault(string|bool|int|float|array|null $default = null) - { - } - - /** - * Returns the default value. - */ - public function getDefault(): string|bool|int|float|array|null - { - } - - /** - * Returns the description text. - */ - public function getDescription(): string - { - } - - public function hasCompletion(): bool - { - } - - /** - * Adds suggestions to $suggestions for the current completion input. - * - * @see Command::complete() - */ - public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void - { - } - - /** - * Checks whether the given option equals this one. - */ - public function equals(self $option): bool - { - } -} diff --git a/tests/stubs/symfony_component_console_output_outputinterface.php b/tests/stubs/symfony_component_console_output_outputinterface.php deleted file mode 100644 index f0b0b2f10..000000000 --- a/tests/stubs/symfony_component_console_output_outputinterface.php +++ /dev/null @@ -1,135 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Output; - -use Symfony\Component\Console\Formatter\OutputFormatterInterface; - -/** - * OutputInterface is the interface implemented by all Output classes. - * - * @author Fabien Potencier - */ -interface OutputInterface -{ - public const VERBOSITY_QUIET = 16; - public const VERBOSITY_NORMAL = 32; - public const VERBOSITY_VERBOSE = 64; - public const VERBOSITY_VERY_VERBOSE = 128; - public const VERBOSITY_DEBUG = 256; - - public const OUTPUT_NORMAL = 1; - public const OUTPUT_RAW = 2; - public const OUTPUT_PLAIN = 4; - - /** - * Writes a message to the output. - * - * @param bool $newline Whether to add a newline - * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), - * 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL - * - * @return void - */ - public function write(string|iterable $messages, bool $newline = false, int $options = 0) - { - } - - /** - * Writes a message to the output and adds a newline at the end. - * - * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), - * 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL - * - * @return void - */ - public function writeln(string|iterable $messages, int $options = 0) - { - } - - /** - * Sets the verbosity of the output. - * - * @param self::VERBOSITY_* $level - * - * @return void - */ - public function setVerbosity(int $level) - { - } - - /** - * Gets the current verbosity of the output. - * - * @return self::VERBOSITY_* - */ - public function getVerbosity(): int - { - } - - /** - * Returns whether verbosity is quiet (-q). - */ - public function isQuiet(): bool - { - } - - /** - * Returns whether verbosity is verbose (-v). - */ - public function isVerbose(): bool - { - } - - /** - * Returns whether verbosity is very verbose (-vv). - */ - public function isVeryVerbose(): bool - { - } - - /** - * Returns whether verbosity is debug (-vvv). - */ - public function isDebug(): bool - { - } - - /** - * Sets the decorated flag. - * - * @return void - */ - public function setDecorated(bool $decorated) - { - } - - /** - * Gets the decorated flag. - */ - public function isDecorated(): bool - { - } - - /** - * @return void - */ - public function setFormatter(OutputFormatterInterface $formatter) - { - } - - /** - * Returns current output formatter instance. - */ - public function getFormatter(): OutputFormatterInterface - { - } -} diff --git a/tests/stubs/symfony_component_console_question_confirmationquestion.php b/tests/stubs/symfony_component_console_question_confirmationquestion.php deleted file mode 100644 index 0db6fe2fa..000000000 --- a/tests/stubs/symfony_component_console_question_confirmationquestion.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Question; - -/** - * Represents a yes/no question. - * - * @author Fabien Potencier - */ -class ConfirmationQuestion extends Question -{ - /** - * @param string $question The question to ask to the user - * @param bool $default The default answer to return, true or false - * @param string $trueAnswerRegex A regex to match the "yes" answer - */ - public function __construct(string $question, bool $default = true, string $trueAnswerRegex = '/^y/i') - { - } -} diff --git a/tests/stubs/symfony_component_console_question_question.php b/tests/stubs/symfony_component_console_question_question.php deleted file mode 100644 index c04b412b9..000000000 --- a/tests/stubs/symfony_component_console_question_question.php +++ /dev/null @@ -1,207 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Question; - -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Exception\LogicException; - -/** - * Represents a Question. - * - * @author Fabien Potencier - */ -class Question -{ - /** - * @param string $question The question to ask to the user - * @param string|bool|int|float|null $default The default answer to return if the user enters nothing - */ - public function __construct(string $question, string|bool|int|float|null $default = null) - { - } - - /** - * Returns the question. - */ - public function getQuestion(): string - { - } - - /** - * Returns the default answer. - */ - public function getDefault(): string|bool|int|float|null - { - } - - /** - * Returns whether the user response accepts newline characters. - */ - public function isMultiline(): bool - { - } - - /** - * Sets whether the user response should accept newline characters. - * - * @return $this - */ - public function setMultiline(bool $multiline): static - { - } - - /** - * Returns whether the user response must be hidden. - */ - public function isHidden(): bool - { - } - - /** - * Sets whether the user response must be hidden or not. - * - * @return $this - * - * @throws LogicException In case the autocompleter is also used - */ - public function setHidden(bool $hidden): static - { - } - - /** - * In case the response cannot be hidden, whether to fallback on non-hidden question or not. - */ - public function isHiddenFallback(): bool - { - } - - /** - * Sets whether to fallback on non-hidden question if the response cannot be hidden. - * - * @return $this - */ - public function setHiddenFallback(bool $fallback): static - { - } - - /** - * Gets values for the autocompleter. - */ - public function getAutocompleterValues(): ?iterable - { - } - - /** - * Sets values for the autocompleter. - * - * @return $this - * - * @throws LogicException - */ - public function setAutocompleterValues(?iterable $values): static - { - } - - /** - * Gets the callback function used for the autocompleter. - */ - public function getAutocompleterCallback(): ?callable - { - } - - /** - * Sets the callback function used for the autocompleter. - * - * The callback is passed the user input as argument and should return an iterable of corresponding suggestions. - * - * @return $this - */ - public function setAutocompleterCallback(?callable $callback = null): static - { - } - - /** - * Sets a validator for the question. - * - * @return $this - */ - public function setValidator(?callable $validator = null): static - { - } - - /** - * Gets the validator for the question. - */ - public function getValidator(): ?callable - { - } - - /** - * Sets the maximum number of attempts. - * - * Null means an unlimited number of attempts. - * - * @return $this - * - * @throws InvalidArgumentException in case the number of attempts is invalid - */ - public function setMaxAttempts(?int $attempts): static - { - } - - /** - * Gets the maximum number of attempts. - * - * Null means an unlimited number of attempts. - */ - public function getMaxAttempts(): ?int - { - } - - /** - * Sets a normalizer for the response. - * - * The normalizer can be a callable (a string), a closure or a class implementing __invoke. - * - * @return $this - */ - public function setNormalizer(callable $normalizer): static - { - } - - /** - * Gets the normalizer for the response. - * - * The normalizer can ba a callable (a string), a closure or a class implementing __invoke. - */ - public function getNormalizer(): ?callable - { - } - - /** - * @return bool - */ - protected function isAssoc(array $array) - { - } - - public function isTrimmable(): bool - { - } - - /** - * @return $this - */ - public function setTrimmable(bool $trimmable): static - { - } -} diff --git a/tests/stubs/test_testcase.php b/tests/stubs/test_testcase.php deleted file mode 100644 index cfb88fb6a..000000000 --- a/tests/stubs/test_testcase.php +++ /dev/null @@ -1,279 +0,0 @@ -=')) { - trait OnNotSuccessfulTestTrait { - protected function onNotSuccessfulTest(\Throwable $t): never { - $this->restoreAllServices(); - - // restore database connection - if (!$this->IsDatabaseAccessAllowed()) { - \OC::$server->registerService(IDBConnection::class, function () { - return self::$realDatabase; - }); - } - - parent::onNotSuccessfulTest($t); - } - } -} else { - trait OnNotSuccessfulTestTrait { - protected function onNotSuccessfulTest(\Throwable $t): void { - $this->restoreAllServices(); - - // restore database connection - if (!$this->IsDatabaseAccessAllowed()) { - \OC::$server->registerService(IDBConnection::class, function () { - return self::$realDatabase; - }); - } - - parent::onNotSuccessfulTest($t); - } - } -} - -abstract class TestCase extends \PHPUnit\Framework\TestCase { - /** @var IDBConnection */ - protected static $realDatabase = null; - - /** @var array */ - protected $services = []; - - use OnNotSuccessfulTestTrait; - - /** - * @param string $name - * @param mixed $newService - * @return bool - */ - public function overwriteService(string $name, $newService): bool - { - } - - /** - * @param string $name - * @return bool - */ - public function restoreService(string $name): bool - { - } - - public function restoreAllServices() - { - } - - protected function getTestTraits() - { - } - - protected function setUp(): void - { - } - - protected function tearDown(): void - { - } - - /** - * Allows us to test private methods/properties - * - * @param $object - * @param $methodName - * @param array $parameters - * @return mixed - */ - protected static function invokePrivate($object, $methodName, array $parameters = []) - { - } - - /** - * Returns a unique identifier as uniqid() is not reliable sometimes - * - * @param string $prefix - * @param int $length - * @return string - */ - protected static function getUniqueID($prefix = '', $length = 13) - { - } - - /** - * Filter methods - * - * Returns all methods of the given class, - * that are public or abstract and not in the ignoreMethods list, - * to be able to fill onlyMethods() with an inverted list. - * - * @param string $className - * @param string[] $filterMethods - * @return string[] - */ - public function filterClassMethods(string $className, array $filterMethods): array - { - } - - public static function tearDownAfterClass(): void - { - } - - /** - * Remove all entries from the share table - * - * @param IQueryBuilder $queryBuilder - */ - protected static function tearDownAfterClassCleanShares(IQueryBuilder $queryBuilder) - { - } - - /** - * Remove all entries from the storages table - * - * @param IQueryBuilder $queryBuilder - */ - protected static function tearDownAfterClassCleanStorages(IQueryBuilder $queryBuilder) - { - } - - /** - * Remove all entries from the filecache table - * - * @param IQueryBuilder $queryBuilder - */ - protected static function tearDownAfterClassCleanFileCache(IQueryBuilder $queryBuilder) - { - } - - /** - * Remove all unused files from the data dir - * - * @param string $dataDir - */ - protected static function tearDownAfterClassCleanStrayDataFiles($dataDir) - { - } - - /** - * Recursive delete files and folders from a given directory - * - * @param string $dir - */ - protected static function tearDownAfterClassCleanStrayDataUnlinkDir($dir) - { - } - - /** - * Clean up the list of hooks - */ - protected static function tearDownAfterClassCleanStrayHooks() - { - } - - /** - * Clean up the list of locks - */ - protected static function tearDownAfterClassCleanStrayLocks() - { - } - - /** - * Login and setup FS as a given user, - * sets the given user as the current user. - * - * @param string $user user id or empty for a generic FS - */ - protected static function loginAsUser($user = '') - { - } - - /** - * Logout the current user and tear down the filesystem. - */ - protected static function logout() - { - } - - /** - * Run all commands pushed to the bus - */ - protected function runCommands() - { - } - - /** - * Check if the given path is locked with a given type - * - * @param View $view view - * @param string $path path to check - * @param int $type lock type - * @param bool $onMountPoint true to check the mount point instead of the - * mounted storage - * - * @return boolean true if the file is locked with the - * given type, false otherwise - */ - protected function isFileLocked($view, $path, $type, $onMountPoint = false) - { - } - - protected function getGroupAnnotations(): array - { - } - - protected function IsDatabaseAccessAllowed(): bool - { - } - - /** - * @param string $expectedHtml - * @param string $template - * @param array $vars - */ - protected function assertTemplate($expectedHtml, $template, $vars = []) - { - } - - /** - * @param string $expectedHtml - * @param string $actualHtml - * @param string $message - */ - protected function assertHtmlStringEqualsHtmlString($expectedHtml, $actualHtml, $message = '') - { - } -} diff --git a/tests/stubs/test_traits_usertrait.php b/tests/stubs/test_traits_usertrait.php deleted file mode 100644 index 6a27afa83..000000000 --- a/tests/stubs/test_traits_usertrait.php +++ /dev/null @@ -1,50 +0,0 @@ -uid, null, Server::get(IEventDispatcher::class)); - } - - public function getUID(): string { - return $this->uid; - } -} - -/** - * Allow creating users in a temporary backend - */ -trait UserTrait { - /** - * @var \Test\Util\User\Dummy|UserInterface - */ - protected $userBackend; - - protected function createUser($name, $password): IUser - { - } - - protected function setUpUserTrait() - { - } - - protected function tearDownUserTrait() - { - } -} diff --git a/vendor-bin/psalm/composer.json b/vendor-bin/psalm/composer.json deleted file mode 100644 index 254fa9322..000000000 --- a/vendor-bin/psalm/composer.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "require-dev": { - "vimeo/psalm": "^6.14", - "psalm/plugin-phpunit": "^0.19.3" - }, - "config": { - "platform": { - "php": "8.2.27" - } - } -} diff --git a/vendor-bin/psalm/composer.lock b/vendor-bin/psalm/composer.lock deleted file mode 100644 index 8a9cf92de..000000000 --- a/vendor-bin/psalm/composer.lock +++ /dev/null @@ -1,3406 +0,0 @@ -{ - "_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": "f9f7139e009b603ee856af72f6b6f2e9", - "packages": [], - "packages-dev": [ - { - "name": "amphp/amp", - "version": "v3.1.1", - "source": { - "type": "git", - "url": "https://github.com/amphp/amp.git", - "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/fa0ab33a6f47a82929c38d03ca47ebb71086a93f", - "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f", - "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.1" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2025-08-27T21:42:00+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.3", - "source": { - "type": "git", - "url": "https://github.com/amphp/parallel.git", - "reference": "296b521137a54d3a02425b464e5aee4c93db2c60" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/parallel/zipball/296b521137a54d3a02425b464e5aee4c93db2c60", - "reference": "296b521137a54d3a02425b464e5aee4c93db2c60", - "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.3" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2025-11-15T06:23:42+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.4", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "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.4" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - } - ], - "time": "2025-08-20T19:15:30+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": "danog/advanced-json-rpc", - "version": "v3.2.3", - "source": { - "type": "git", - "url": "https://github.com/danog/php-advanced-json-rpc.git", - "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/ae703ea7b4811797a10590b6078de05b3b33dd91", - "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91", - "shasum": "" - }, - "require": { - "netresearch/jsonmapper": "^5", - "php": ">=8.1", - "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0 || ^6" - }, - "replace": { - "felixfbecker/php-advanced-json-rpc": "^3" - }, - "require-dev": { - "phpunit/phpunit": "^9" - }, - "type": "library", - "autoload": { - "psr-4": { - "AdvancedJsonRpc\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - }, - { - "name": "Daniil Gentili", - "email": "daniil@daniil.it" - } - ], - "description": "A more advanced JSONRPC implementation", - "support": { - "issues": "https://github.com/danog/php-advanced-json-rpc/issues", - "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.3" - }, - "time": "2026-01-12T21:07:10+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.6", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", - "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "phpunit/phpunit": "<=7.5 || >=14" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^14", - "phpstan/phpstan": "1.4.10 || 2.1.30", - "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", - "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.6" - }, - "time": "2026-02-07T07:09:04+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.3.0", - "source": { - "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "db9508f7b1474469d9d3c53b86f817e344732678" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", - "reference": "db9508f7b1474469d9d3c53b86f817e344732678", - "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": "^2.0", - "phpstan/phpstan-deprecation-rules": "^2.0.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "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.3.0" - }, - "funding": [ - { - "url": "https://github.com/theofidry", - "type": "github" - } - ], - "time": "2025-08-14T07:29:31+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.8.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/uri.git", - "reference": "4436c6ec8d458e4244448b069cc572d088230b76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/4436c6ec8d458e4244448b069cc572d088230b76", - "reference": "4436c6ec8d458e4244448b069cc572d088230b76", - "shasum": "" - }, - "require": { - "league/uri-interfaces": "^7.8", - "php": "^8.1", - "psr/http-factory": "^1" - }, - "conflict": { - "league/uri-schemes": "^1.0" - }, - "suggest": { - "ext-bcmath": "to improve IPV4 host parsing", - "ext-dom": "to convert the URI into an HTML anchor tag", - "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", - "ext-uri": "to use the PHP native URI class", - "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", - "league/uri-components": "to provide additional tools to manipulate URI objects components", - "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", - "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", - "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": [ - "URN", - "data-uri", - "file-uri", - "ftp", - "hostname", - "http", - "https", - "middleware", - "parse_str", - "parse_url", - "psr-7", - "query-string", - "querystring", - "rfc2141", - "rfc3986", - "rfc3987", - "rfc6570", - "rfc8141", - "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.8.0" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2026-01-14T17:24:56+00:00" - }, - { - "name": "league/uri-interfaces", - "version": "7.8.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/c5c5cd056110fc8afaba29fa6b72a43ced42acd4", - "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^8.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", - "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", - "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 tools for parsing and resolving RFC3987/RFC3986 URI", - "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.8.0" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2026-01-15T06:54:53+00:00" - }, - { - "name": "netresearch/jsonmapper", - "version": "v5.0.0", - "source": { - "type": "git", - "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c", - "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c", - "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/v5.0.0" - }, - "time": "2024-09-08T10:20:00+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v5.7.0", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "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.x-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.7.0" - }, - "time": "2025-12-06T11:56:16+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": "6.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "2f5cbed597cb261d1ea458f3da3a9ad32e670b1e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2f5cbed597cb261d1ea458f3da3a9ad32e670b1e", - "reference": "2f5cbed597cb261d1ea458f3da3a9ad32e670b1e", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.1", - "ext-filter": "*", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^2.0", - "phpstan/phpdoc-parser": "^2.0", - "webmozart/assert": "^1.9.1 || ^2" - }, - "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", - "shipmonk/dead-code-detector": "^0.5.1" - }, - "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/6.0.1" - }, - "time": "2026-01-20T15:30:42+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", - "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "psalm/phar": "^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev", - "dev-2.x": "2.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/2.0.0" - }, - "time": "2026-01-06T21:53:42+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "2.3.2", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", - "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.3.2" - }, - "time": "2026-01-25T14:56:51+00:00" - }, - { - "name": "psalm/plugin-phpunit", - "version": "0.19.5", - "source": { - "type": "git", - "url": "https://github.com/psalm/psalm-plugin-phpunit.git", - "reference": "143f9d5e049fffcdbc0da3fbb99f6149f9d3e2dc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/143f9d5e049fffcdbc0da3fbb99f6149f9d3e2dc", - "reference": "143f9d5e049fffcdbc0da3fbb99f6149f9d3e2dc", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "php": ">=8.1", - "vimeo/psalm": "dev-master || ^6.10.0" - }, - "conflict": { - "phpspec/prophecy": "<1.20.0", - "phpspec/prophecy-phpunit": "<2.3.0", - "phpunit/phpunit": "<8.5.1" - }, - "require-dev": { - "php": "^7.3 || ^8.0", - "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", - "squizlabs/php_codesniffer": "^3.3.1", - "weirdan/prophecy-shim": "^1.0 || ^2.0" - }, - "type": "psalm-plugin", - "extra": { - "psalm": { - "pluginClass": "Psalm\\PhpUnitPlugin\\Plugin" - } - }, - "autoload": { - "psr-4": { - "Psalm\\PhpUnitPlugin\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matt Brown", - "email": "github@muglug.com" - } - ], - "description": "Psalm plugin for PHPUnit", - "support": { - "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues", - "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.19.5" - }, - "time": "2025-03-31T18:49:55+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.8", - "source": { - "type": "git", - "url": "https://github.com/revoltphp/event-loop.git", - "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/b6fc06dce8e9b523c9946138fa5e62181934f91c", - "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c", - "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.8" - }, - "time": "2025-08-27T21:33:23+00:00" - }, - { - "name": "sebastian/diff", - "version": "6.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-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/6.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:53:05+00:00" - }, - { - "name": "spatie/array-to-xml", - "version": "3.4.4", - "source": { - "type": "git", - "url": "https://github.com/spatie/array-to-xml.git", - "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/88b2f3852a922dd73177a68938f8eb2ec70c7224", - "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224", - "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.4" - }, - "funding": [ - { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" - }, - { - "url": "https://github.com/spatie", - "type": "github" - } - ], - "time": "2025-12-15T09:00:41+00:00" - }, - { - "name": "symfony/console", - "version": "v7.4.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/41e38717ac1dd7a46b6bda7d6a82af2d98a78894", - "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2|^8.0" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.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/v7.4.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-13T11:36:38+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": "v7.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "d551b38811096d0be9c4691d406991b47c0c630a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a", - "reference": "d551b38811096d0be9c4691d406991b47c0c630a", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" - }, - "require-dev": { - "symfony/process": "^6.4|^7.0|^8.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/v7.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-11-27T13:27:24+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.33.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.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "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.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", - "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.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-27T09:58:17+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.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.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "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.33.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.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-12-23T08:48:59+00:00" - }, - { - "name": "symfony/polyfill-php84", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php84\\": "" - }, - "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 backporting some PHP 8.4+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-24T13:30:11+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v3.6.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", - "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.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-07-15T11:30:57+00:00" - }, - { - "name": "symfony/string", - "version": "v7.4.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/1c4b10461bf2ec27537b5f36105337262f5f5d6f", - "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.33", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/emoji": "^7.1|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0|^8.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/v7.4.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-12T10:54:30+00:00" - }, - { - "name": "vimeo/psalm", - "version": "6.15.1", - "source": { - "type": "git", - "url": "https://github.com/vimeo/psalm.git", - "reference": "28dc127af1b5aecd52314f6f645bafc10d0e11f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/28dc127af1b5aecd52314f6f645bafc10d0e11f9", - "reference": "28dc127af1b5aecd52314f6f645bafc10d0e11f9", - "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", - "danog/advanced-json-rpc": "^3.1", - "dnoegel/php-xdg-base-dir": "^0.1.1", - "ext-ctype": "*", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-simplexml": "*", - "ext-tokenizer": "*", - "felixfbecker/language-server-protocol": "^1.5.3", - "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", - "netresearch/jsonmapper": "^5.0", - "nikic/php-parser": "^5.0.0", - "php": "~8.1.31 || ~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0", - "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", - "spatie/array-to-xml": "^2.17.0 || ^3.0", - "symfony/console": "^6.0 || ^7.0 || ^8.0", - "symfony/filesystem": "~6.3.12 || ~6.4.3 || ^7.0.3 || ^8.0", - "symfony/polyfill-php84": "^1.31.0" - }, - "provide": { - "psalm/psalm": "self.version" - }, - "require-dev": { - "amphp/phpunit-util": "^3", - "bamarni/composer-bin-plugin": "^1.4", - "brianium/paratest": "^6.9", - "danog/class-finder": "^0.4.8", - "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 || ^8.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": "2026-02-07T19:27:16+00:00" - }, - { - "name": "webmozart/assert", - "version": "2.1.2", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", - "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-date": "*", - "ext-filter": "*", - "php": "^8.2" - }, - "suggest": { - "ext-intl": "", - "ext-simplexml": "", - "ext-spl": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-feature/2-0": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - }, - { - "name": "Woody Gilk", - "email": "woody.gilk@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/2.1.2" - }, - "time": "2026-01-13T14:02:24+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": {}, - "prefer-stable": false, - "prefer-lowest": false, - "platform": {}, - "platform-dev": {}, - "platform-overrides": { - "php": "8.2.27" - }, - "plugin-api-version": "2.6.0" -} From b31c487eb9730fc9ff3d889cb30fe54d01d2d0da Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Feb 2026 08:57:53 +0100 Subject: [PATCH 02/12] feat(phpstan): Setup Signed-off-by: provokateurin --- .github/dependabot.yml | 1 + composer.json | 3 +- lib/Controller/DelegationController.php | 4 +- lib/Controller/FolderController.php | 8 +-- lib/Folder/FolderDefinitionWithMappings.php | 12 ++-- .../FolderDefinitionWithPermissions.php | 8 +-- lib/Folder/FolderManager.php | 12 ++-- lib/Folder/FolderWithMappingsAndCache.php | 12 ++-- lib/Mount/GroupFolderStorage.php | 2 - lib/ResponseDefinitions.php | 16 ++--- lib/Service/FoldersFilter.php | 2 +- phpstan.neon | 27 +++++++ tests/Folder/FolderManagerTest.php | 2 +- vendor-bin/phpstan/composer.json | 5 ++ vendor-bin/phpstan/composer.lock | 72 +++++++++++++++++++ 15 files changed, 145 insertions(+), 41 deletions(-) create mode 100644 phpstan.neon create mode 100644 vendor-bin/phpstan/composer.json create mode 100644 vendor-bin/phpstan/composer.lock diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e548184cf..faf3f9a33 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,6 +17,7 @@ updates: - /vendor-bin/openapi-extractor - /vendor-bin/phpunit - /vendor-bin/rector + - /vendor-bin/phpstan schedule: interval: weekly day: saturday diff --git a/composer.json b/composer.json index 69b7b9299..71516f224 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "test:unit": "phpunit -c tests/phpunit.xml", "test:unit:coverage": "XDEBUG_MODE=coverage phpunit -c tests/phpunit.xml", "rector": "rector && composer cs:fix", - "openapi": "generate-spec && npm run typescript:generate" + "openapi": "generate-spec && npm run typescript:generate", + "phpstan": "phpstan analyse -c phpstan.neon" }, "config": { "allow-plugins": { diff --git a/lib/Controller/DelegationController.php b/lib/Controller/DelegationController.php index 3f072ef43..12f38c93c 100644 --- a/lib/Controller/DelegationController.php +++ b/lib/Controller/DelegationController.php @@ -28,8 +28,8 @@ use Psr\Container\ContainerInterface; /** - * @psalm-import-type GroupFoldersDelegationGroup from ResponseDefinitions - * @psalm-import-type GroupFoldersDelegationCircle from ResponseDefinitions + * @phpstan-import-type GroupFoldersDelegationGroup from ResponseDefinitions + * @phpstan-import-type GroupFoldersDelegationCircle from ResponseDefinitions */ class DelegationController extends OCSController { public function __construct( diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index 84df73abc..6dedf0cce 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -33,10 +33,10 @@ use OCP\IUserSession; /** - * @psalm-import-type GroupFoldersGroup from ResponseDefinitions - * @psalm-import-type GroupFoldersCircle from ResponseDefinitions - * @psalm-import-type GroupFoldersUser from ResponseDefinitions - * @psalm-import-type GroupFoldersFolder from ResponseDefinitions + * @phpstan-import-type GroupFoldersGroup from ResponseDefinitions + * @phpstan-import-type GroupFoldersCircle from ResponseDefinitions + * @phpstan-import-type GroupFoldersUser from ResponseDefinitions + * @phpstan-import-type GroupFoldersFolder from ResponseDefinitions */ class FolderController extends OCSController { private readonly ?IUser $user; diff --git a/lib/Folder/FolderDefinitionWithMappings.php b/lib/Folder/FolderDefinitionWithMappings.php index 1322a8d44..a06afb85a 100644 --- a/lib/Folder/FolderDefinitionWithMappings.php +++ b/lib/Folder/FolderDefinitionWithMappings.php @@ -11,13 +11,13 @@ use OCA\GroupFolders\ResponseDefinitions; /** - * @psalm-import-type GroupFoldersApplicable from ResponseDefinitions - * @psalm-import-type GroupFoldersAclManage from ResponseDefinitions + * @phpstan-import-type GroupFoldersApplicable from ResponseDefinitions + * @phpstan-import-type GroupFoldersAclManage from ResponseDefinitions */ class FolderDefinitionWithMappings extends FolderDefinition { /** - * @psalm-param array $groups - * @psalm-param list $manage + * @param array $groups + * @param list $manage */ public function __construct( int $id, @@ -35,8 +35,8 @@ public function __construct( } /** - * @psalm-param array $groups - * @psalm-param list $manage + * @param array $groups + * @param list $manage */ public static function fromFolder(FolderDefinition $folder, array $groups, array $manage): FolderDefinitionWithMappings { return new FolderDefinitionWithMappings( diff --git a/lib/Folder/FolderDefinitionWithPermissions.php b/lib/Folder/FolderDefinitionWithPermissions.php index ca69832f7..2853aec4a 100644 --- a/lib/Folder/FolderDefinitionWithPermissions.php +++ b/lib/Folder/FolderDefinitionWithPermissions.php @@ -12,8 +12,8 @@ use OCP\Files\Cache\ICacheEntry; /** - * @psalm-import-type GroupFoldersApplicable from ResponseDefinitions - * @psalm-import-type GroupFoldersAclManage from ResponseDefinitions + * @phpstan-import-type GroupFoldersApplicable from ResponseDefinitions + * @phpstan-import-type GroupFoldersAclManage from ResponseDefinitions */ class FolderDefinitionWithPermissions extends FolderDefinition { public function __construct( @@ -32,8 +32,8 @@ public function __construct( } /** - * @psalm-param array $groups - * @psalm-param list $manage + * @param array $groups + * @param list $manage */ public static function fromFolder(FolderDefinition $folder, ICacheEntry $rootCacheEntry, int $permissions): FolderDefinitionWithPermissions { return new FolderDefinitionWithPermissions( diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php index 5ceb929ef..46cbe335a 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -45,12 +45,12 @@ use Psr\Log\LoggerInterface; /** - * @psalm-import-type GroupFoldersGroup from ResponseDefinitions - * @psalm-import-type GroupFoldersCircle from ResponseDefinitions - * @psalm-import-type GroupFoldersUser from ResponseDefinitions - * @psalm-import-type GroupFoldersAclManage from ResponseDefinitions - * @psalm-import-type GroupFoldersApplicable from ResponseDefinitions - * @psalm-type InternalFolderMapping = array{ + * @phpstan-import-type GroupFoldersGroup from ResponseDefinitions + * @phpstan-import-type GroupFoldersCircle from ResponseDefinitions + * @phpstan-import-type GroupFoldersUser from ResponseDefinitions + * @phpstan-import-type GroupFoldersAclManage from ResponseDefinitions + * @phpstan-import-type GroupFoldersApplicable from ResponseDefinitions + * @phpstan-type InternalFolderMapping = array{ * folder_id: int, * mapping_type: 'user'|'group'|'circle', * mapping_id: string, diff --git a/lib/Folder/FolderWithMappingsAndCache.php b/lib/Folder/FolderWithMappingsAndCache.php index 025f6206f..fe4f585c1 100644 --- a/lib/Folder/FolderWithMappingsAndCache.php +++ b/lib/Folder/FolderWithMappingsAndCache.php @@ -12,13 +12,13 @@ use OCP\Files\Cache\ICacheEntry; /** - * @psalm-import-type GroupFoldersApplicable from ResponseDefinitions - * @psalm-import-type GroupFoldersAclManage from ResponseDefinitions + * @phpstan-import-type GroupFoldersApplicable from ResponseDefinitions + * @phpstan-import-type GroupFoldersAclManage from ResponseDefinitions */ class FolderWithMappingsAndCache extends FolderDefinitionWithMappings { /** - * @psalm-param array $groups - * @psalm-param list $manage + * @param array $groups + * @param list $manage */ public function __construct( int $id, @@ -37,8 +37,8 @@ public function __construct( } /** - * @psalm-param array $groups - * @psalm-param list $manage + * @param array $groups + * @param list $manage */ public static function fromFolderWithMapping(FolderDefinitionWithMappings $folder, ICacheEntry $rootCacheEntry): FolderWithMappingsAndCache { return new FolderWithMappingsAndCache( diff --git a/lib/Mount/GroupFolderStorage.php b/lib/Mount/GroupFolderStorage.php index b84690ae6..f3ca4ff1b 100644 --- a/lib/Mount/GroupFolderStorage.php +++ b/lib/Mount/GroupFolderStorage.php @@ -26,8 +26,6 @@ class GroupFolderStorage extends Quota implements IConstructableStorage { private readonly ?ICacheEntry $rootEntry; private readonly IUserSession $userSession; private readonly ?IUser $mountOwner; - /** @var ICache|null */ - public $cache; public function __construct(array $parameters) { parent::__construct($parameters); diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index 0fbc62ce4..c59dffab3 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -9,44 +9,44 @@ namespace OCA\GroupFolders; /** - * @psalm-type GroupFoldersDelegationGroup = array{ + * @phpstan-type GroupFoldersDelegationGroup = array{ * gid: string, * displayName: string, * } * - * @psalm-type GroupFoldersDelegationCircle = array{ + * @phpstan-type GroupFoldersDelegationCircle = array{ * singleId: string, * displayName: string, * } * - * @psalm-type GroupFoldersGroup = array{ + * @phpstan-type GroupFoldersGroup = array{ * gid: string, * displayname: string, * } * - * @psalm-type GroupFoldersCircle = array{ + * @phpstan-type GroupFoldersCircle = array{ * sid: string, * displayname: string, * } * - * @psalm-type GroupFoldersUser = array{ + * @phpstan-type GroupFoldersUser = array{ * uid: string, * displayname: string, * } * - * @psalm-type GroupFoldersAclManage = array{ + * @phpstan-type GroupFoldersAclManage = array{ * displayname: string, * id: string, * type: 'user'|'group'|'circle', * } * - * @psalm-type GroupFoldersApplicable = array{ + * @phpstan-type GroupFoldersApplicable = array{ * displayName: string, * permissions: int, * type: 'group'|'circle', * } * - * @psalm-type GroupFoldersFolder = array{ + * @phpstan-type GroupFoldersFolder = array{ * id: int, * mount_point: string, * group_details: array, diff --git a/lib/Service/FoldersFilter.php b/lib/Service/FoldersFilter.php index 07d538d6b..dbaa036f7 100644 --- a/lib/Service/FoldersFilter.php +++ b/lib/Service/FoldersFilter.php @@ -13,7 +13,7 @@ use OCP\IUserSession; /** - * @psalm-import-type GroupFoldersFolder from ResponseDefinitions + * @phpstan-import-type GroupFoldersFolder from ResponseDefinitions */ class FoldersFilter { public function __construct( diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 000000000..8e5cbc683 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later +parameters: + level: 0 + phpVersion: + min: 80200 + max: 80500 + paths: + - lib + - templates + - tests + scanDirectories: + - ../../3rdparty/doctrine/dbal/ + - ../../3rdparty/icewind/streams/ + - ../../3rdparty/stecman/symfony-console-completion/ + - ../../3rdparty/symfony/console/ + - ../../core/ + - ../../lib/ + - ../../tests/lib/ + - ../circles/lib/ + - ../dav/lib/ + - ../files/lib/ + - ../files_sharing/lib/ + - ../files_trashbin/lib/ + - ../files_versions/lib/ + - ../settings/lib/ + - vendor-bin/phpunit/vendor/phpunit/phpunit diff --git a/tests/Folder/FolderManagerTest.php b/tests/Folder/FolderManagerTest.php index 44fda0c06..eda4f24ee 100644 --- a/tests/Folder/FolderManagerTest.php +++ b/tests/Folder/FolderManagerTest.php @@ -32,7 +32,7 @@ /** * @group DB * - * @psalm-import-type GroupFoldersApplicable from ResponseDefinitions + * @phpstan-import-type GroupFoldersApplicable from ResponseDefinitions */ class FolderManagerTest extends TestCase { private FolderManager $manager; diff --git a/vendor-bin/phpstan/composer.json b/vendor-bin/phpstan/composer.json new file mode 100644 index 000000000..2c8976d51 --- /dev/null +++ b/vendor-bin/phpstan/composer.json @@ -0,0 +1,5 @@ +{ + "require-dev": { + "phpstan/phpstan": "^2.1" + } +} diff --git a/vendor-bin/phpstan/composer.lock b/vendor-bin/phpstan/composer.lock new file mode 100644 index 000000000..12795d0cc --- /dev/null +++ b/vendor-bin/phpstan/composer.lock @@ -0,0 +1,72 @@ +{ + "_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": "df9e69712d56aaef230c98a02cbe90f8", + "packages": [], + "packages-dev": [ + { + "name": "phpstan/phpstan", + "version": "2.1.38", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dfaf1f530e1663aa167bc3e52197adb221582629", + "reference": "dfaf1f530e1663aa167bc3e52197adb221582629", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2026-01-30T17:12:46+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} From 721ae4995250752b21d311136918a105c1ee5fa5 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Feb 2026 09:15:42 +0100 Subject: [PATCH 03/12] feat(phpstan): Level 1 Signed-off-by: provokateurin --- phpstan.neon | 2 +- templates/index.php | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 8e5cbc683..373898fb3 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later parameters: - level: 0 + level: 1 phpVersion: min: 80200 max: 80500 diff --git a/templates/index.php b/templates/index.php index 36dbe8b25..6b6dc2ef0 100644 --- a/templates/index.php +++ b/templates/index.php @@ -7,7 +7,10 @@

- t('Team folders')); ?> + t('Team folders')); +?>

From 2193ae70e24f3a4b4c94b9e97e563ae750f19c88 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Feb 2026 09:51:31 +0100 Subject: [PATCH 04/12] feat(phpstan): Level 2 Signed-off-by: provokateurin --- lib/CacheListener.php | 1 - lib/Command/Delete.php | 2 ++ lib/Command/Scan.php | 4 ++-- lib/Folder/FolderDefinitionWithPermissions.php | 4 ---- lib/Folder/FolderWithMappingsAndCache.php | 4 ---- lib/Trash/TrashBackend.php | 8 +++++++- lib/Versions/GroupVersionsExpireManager.php | 2 -- phpstan.neon | 2 +- 8 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/CacheListener.php b/lib/CacheListener.php index 967126851..c742d4fd0 100644 --- a/lib/CacheListener.php +++ b/lib/CacheListener.php @@ -34,7 +34,6 @@ public function onCacheEvent(ICacheEvent $event): void { return; } - /** @var Jail $storage */ $path = $storage->getJailedPath($event->getPath()); if ($path !== null) { $event->setPath($path); diff --git a/lib/Command/Delete.php b/lib/Command/Delete.php index db68c3154..e01f03e5b 100644 --- a/lib/Command/Delete.php +++ b/lib/Command/Delete.php @@ -8,6 +8,7 @@ namespace OCA\GroupFolders\Command; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -30,6 +31,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return -1; } + /** @var QuestionHelper $helper */ $helper = $this->getHelper('question'); $question = new ConfirmationQuestion('Are you sure you want to delete the Team folder ' . $folder->mountPoint . ' and all files within, this cannot be undone (y/N).', false); if ($input->getOption('force') || $helper->ask($input, $output, $question)) { diff --git a/lib/Command/Scan.php b/lib/Command/Scan.php index b7d4b329e..574fe4203 100644 --- a/lib/Command/Scan.php +++ b/lib/Command/Scan.php @@ -8,13 +8,13 @@ namespace OCA\GroupFolders\Command; +use OC\Files\Cache\Scanner; use OC\Files\ObjectStore\ObjectStoreScanner; use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions; use OCA\GroupFolders\Folder\FolderManager; use OCA\GroupFolders\Mount\FolderStorageManager; use OCA\GroupFolders\Mount\MountProvider; use OCP\Constants; -use OCP\Files\Cache\IScanner; use OCP\Files\IRootFolder; use OCP\Files\Storage\IStorageFactory; use Symfony\Component\Console\Helper\Table; @@ -117,7 +117,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } foreach ($mounts as $type => $mount) { $statsRow = ["$folderId - $type", 0, 0, 0, 0]; - /** @var IScanner&\OC\Hooks\BasicEmitter $scanner */ + /** @var Scanner&\OC\Hooks\BasicEmitter $scanner */ $scanner = $mount->getStorage()->getScanner(); $output->writeln("Scanning Team folder with id\t$folderId - $type", OutputInterface::VERBOSITY_VERBOSE); diff --git a/lib/Folder/FolderDefinitionWithPermissions.php b/lib/Folder/FolderDefinitionWithPermissions.php index 2853aec4a..69c25f317 100644 --- a/lib/Folder/FolderDefinitionWithPermissions.php +++ b/lib/Folder/FolderDefinitionWithPermissions.php @@ -31,10 +31,6 @@ public function __construct( parent::__construct($id, $mountPoint, $quota, $acl, $aclDefaultNoPermission, $storageId, $rootId, $options); } - /** - * @param array $groups - * @param list $manage - */ public static function fromFolder(FolderDefinition $folder, ICacheEntry $rootCacheEntry, int $permissions): FolderDefinitionWithPermissions { return new FolderDefinitionWithPermissions( $folder->id, diff --git a/lib/Folder/FolderWithMappingsAndCache.php b/lib/Folder/FolderWithMappingsAndCache.php index fe4f585c1..6b847df43 100644 --- a/lib/Folder/FolderWithMappingsAndCache.php +++ b/lib/Folder/FolderWithMappingsAndCache.php @@ -36,10 +36,6 @@ public function __construct( parent::__construct($id, $mountPoint, $quota, $acl, $aclDefaultNoPermission, $storageId, $rootId, $options, $groups, $manage); } - /** - * @param array $groups - * @param list $manage - */ public static function fromFolderWithMapping(FolderDefinitionWithMappings $folder, ICacheEntry $rootCacheEntry): FolderWithMappingsAndCache { return new FolderWithMappingsAndCache( $folder->id, diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index 0798bde8c..4eaae200f 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -264,7 +264,9 @@ public function moveToTrash(IStorage $storage, string $internalPath): bool { if ($result) { $originalLocation = $internalPath; if ($storage->instanceOfStorage(ISharedStorage::class)) { - $originalLocation = $storage->getWrapperStorage()->getUnjailedPath($originalLocation); + /** @var Jail $jail */ + $jail = $storage->getWrapperStorage(); + $originalLocation = $jail->getUnjailedPath($originalLocation); } $deletedBy = $this->userSession->getUser(); @@ -304,6 +306,10 @@ private function moveFromEncryptedStorage(IStorage $sourceStorage, IStorage $tar $sourceStorage = $sourceStorage->getWrapperStorage(); } + /** + * Some storages have a fourth parameter $preserveMtime + * @phpstan-ignore arguments.count + */ $result = $targetStorage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true); if ($result) { if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) { diff --git a/lib/Versions/GroupVersionsExpireManager.php b/lib/Versions/GroupVersionsExpireManager.php index 6094344a2..c85a21dcc 100644 --- a/lib/Versions/GroupVersionsExpireManager.php +++ b/lib/Versions/GroupVersionsExpireManager.php @@ -17,7 +17,6 @@ use OCP\AppFramework\Utility\ITimeFactory; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\FileInfo; -use OCP\IUser; use Psr\Log\LoggerInterface; class GroupVersionsExpireManager { @@ -49,7 +48,6 @@ public function expireFolders(array $folders): void { public function expireFolder(FolderWithMappingsAndCache $folder): void { $baseFolder = $this->versionsBackend->getVersionsFolder($folder); $files = $this->versionsBackend->getAllVersionedFiles($folder); - /** @var IUser */ $dummyUser = new User('', null, $this->dispatcher); foreach ($files as $fileId => $file) { if ($file instanceof FileInfo) { diff --git a/phpstan.neon b/phpstan.neon index 373898fb3..8394c83f5 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later parameters: - level: 1 + level: 2 phpVersion: min: 80200 max: 80500 From 7f0fe5308dba7a51507da85b7e2099c6faad5fb7 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Feb 2026 10:01:12 +0100 Subject: [PATCH 05/12] feat(phpstan): Level 3 Signed-off-by: provokateurin --- lib/Trash/TrashBackend.php | 7 ++++++- lib/Versions/VersionsBackend.php | 12 ++++++++++-- phpstan.neon | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index 4eaae200f..4b265080d 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -400,7 +400,12 @@ private function setupTrashFolder(FolderDefinitionWithPermissions $folder, ?IUse $this->mountManager->addMount($trashMount); } - return $this->rootFolder->get('/' . $uid . '/files_trashbin/groupfolders/' . $folderId); + $folder = $this->rootFolder->get('/' . $uid . '/files_trashbin/groupfolders/' . $folderId); + if (!$folder instanceof Folder) { + throw new \RuntimeException('Trash folder was not a folder.'); + } + + return $folder; } private function getUnJailedPath(Node $node): string { diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index 045035748..0dc0d07b0 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -320,7 +320,13 @@ public function getVersionsFolder(FolderDefinition $folder): Folder { ); $this->mountManager->addMount($versionMount); } - return $this->rootFolder->get($mountPoint); + + $folder = $this->rootFolder->get($mountPoint); + if (!$folder instanceof Folder) { + throw new \RuntimeException('Versions folder was not a folder.'); + } + + return $folder; } public function setMetadataValue(Node $node, int $revision, string $key, string $value): void { @@ -357,7 +363,7 @@ public function deleteVersion(IVersion $version): void { $this->groupVersionsMapper->delete($versionEntity); } - public function createVersionEntity(File $file): void { + public function createVersionEntity(File $file): null { $fileId = $file->getId(); $timestamp = $file->getMTime(); @@ -372,6 +378,8 @@ public function createVersionEntity(File $file): void { $versionEntity->setDecodedMetadata([]); $this->groupVersionsMapper->insert($versionEntity); } + + return null; } public function updateVersionEntity(File $sourceFile, int $revision, array $properties): void { diff --git a/phpstan.neon b/phpstan.neon index 8394c83f5..6f7dfcd50 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later parameters: - level: 2 + level: 3 phpVersion: min: 80200 max: 80500 From b64cba7872090fd10931aab1f3b8214820f51486 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Feb 2026 10:19:16 +0100 Subject: [PATCH 06/12] feat(phpstan): Level 4 Signed-off-by: provokateurin --- lib/CacheListener.php | 1 + lib/Command/Scan.php | 10 +--- lib/Controller/FolderController.php | 24 ++------- lib/Folder/FolderManager.php | 8 +-- .../CircleDestroyedEventListener.php | 1 + .../LoadAdditionalScriptsListener.php | 1 + lib/Listeners/NodeRenamedListener.php | 1 + lib/Mount/GroupFolderStorage.php | 1 + lib/Trash/TrashBackend.php | 54 +------------------ lib/Versions/ExpireManager.php | 5 +- lib/Versions/VersionsBackend.php | 4 -- phpstan.neon | 2 +- tests/Trash/TrashBackendTest.php | 7 --- 13 files changed, 16 insertions(+), 103 deletions(-) diff --git a/lib/CacheListener.php b/lib/CacheListener.php index c742d4fd0..bfc7f8bd7 100644 --- a/lib/CacheListener.php +++ b/lib/CacheListener.php @@ -30,6 +30,7 @@ public function onCacheEvent(ICacheEvent $event): void { if (!$storage->instanceOfStorage(GroupFolderStorage::class)) { return; } + /** @phpstan-ignore method.impossibleType */ if (!$storage->instanceOfStorage(Jail::class)) { return; } diff --git a/lib/Command/Scan.php b/lib/Command/Scan.php index 574fe4203..3d146488b 100644 --- a/lib/Command/Scan.php +++ b/lib/Command/Scan.php @@ -129,19 +129,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int $scanner->listen('\OC\Files\Cache\Scanner', 'scanFile', function (string $path) use ($output, &$statsRow): void { $output->writeln("\tFile\t/$path", OutputInterface::VERBOSITY_VERBOSE); $statsRow[2]++; - // abortIfInterrupted doesn't exist in nc14 - if (method_exists($this, 'abortIfInterrupted')) { - $this->abortIfInterrupted(); - } + $this->abortIfInterrupted(); }); $scanner->listen('\OC\Files\Cache\Scanner', 'scanFolder', function (string $path) use ($output, &$statsRow): void { $output->writeln("\tFolder\t/$path", OutputInterface::VERBOSITY_VERBOSE); $statsRow[1]++; - // abortIfInterrupted doesn't exist in nc14 - if (method_exists($this, 'abortIfInterrupted')) { - $this->abortIfInterrupted(); - } + $this->abortIfInterrupted(); }); $scanner->listen('\OC\Files\Cache\Scanner', 'normalizedNameMismatch', function ($fullPath) use ($output, &$statsRow): void { diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index 6dedf0cce..603524eba 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -113,6 +113,9 @@ private function formatFolder(FolderWithMappingsAndCache $folder): array { #[NoAdminRequired] #[FrontpageRoute(verb: 'GET', url: '/folders')] public function getFolders(bool $applicable = false, int $offset = 0, ?int $limit = null, string $orderBy = 'mount_point', string $order = 'asc'): DataResponse { + /** + * @phpstan-ignore smallerOrEqual.alwaysFalse, booleanAnd.alwaysFalse + */ if ($limit !== null && $limit <= 0) { throw new OCSBadRequestException('The limit must be greater than 0.'); } @@ -198,7 +201,7 @@ private function checkedGetFolder(int $id): FolderWithMappingsAndCache { return $folder; } - private function checkMountPointExists(string $mountpoint): ?DataResponse { + private function checkMountPointExists(string $mountpoint): void { $storageId = $this->getRootFolderStorageId(); if ($storageId === null) { throw new OCSNotFoundException('Groupfolder not found'); @@ -207,8 +210,6 @@ private function checkMountPointExists(string $mountpoint): ?DataResponse { if ($this->manager->mountPointExists($mountpoint)) { throw new OCSBadRequestException('Mount point already exists'); } - - return null; } private function getRootFolderStorageId(): ?int { @@ -551,21 +552,4 @@ public function aclMappingSearch(int $id, string $search = ''): DataResponse { 'circles' => $circles ]); } - - private function compareFolderNames(string $a, string $b): int { - if (($value = strnatcmp($a, $b)) === 0) { - return $value; - } - - // Folder names starting with '_' get pushed to the end while they are brought to the - // beginning in the frontend. Do the same here to keep it consistent with the frontend - if (str_starts_with($a, '_') && !str_starts_with($b, '_')) { - return -1; - } - if (!str_starts_with($a, '_') && str_starts_with($b, '_')) { - return 1; - } - - return $value; - } } diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php index 46cbe335a..34ee88ef9 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -305,8 +305,6 @@ private function getManageAcl(array $mappings): array { 'displayname' => $circle->getDisplayName(), ]; } - - return null; }, $mappings))); } @@ -712,11 +710,7 @@ public function getFoldersFromCircleMemberships(IUser $user, ?int $folderId = nu $query->andWhere($query->expr()->eq('f.mount_point', $query->createNamedParameter($path))); } - if (method_exists($queryHelper, 'limitToMemberships')) { - $queryHelper->limitToMemberships('a', 'circle_id', $federatedUser); - } else { - $queryHelper->limitToInheritedMembers('a', 'circle_id', $federatedUser); - } + $queryHelper->limitToMemberships('a', 'circle_id', $federatedUser); return array_map(function (array $row): FolderDefinitionWithPermissions { $folder = $this->rowToFolder($row); diff --git a/lib/Listeners/CircleDestroyedEventListener.php b/lib/Listeners/CircleDestroyedEventListener.php index 88c9236bd..fd19c9ba4 100644 --- a/lib/Listeners/CircleDestroyedEventListener.php +++ b/lib/Listeners/CircleDestroyedEventListener.php @@ -26,6 +26,7 @@ public function __construct( #[\Override] public function handle(Event $event): void { + /** @phpstan-ignore instanceof.alwaysTrue */ if (!$event instanceof CircleDestroyedEvent) { return; } diff --git a/lib/Listeners/LoadAdditionalScriptsListener.php b/lib/Listeners/LoadAdditionalScriptsListener.php index 29bca0b81..c7ebbf8d1 100644 --- a/lib/Listeners/LoadAdditionalScriptsListener.php +++ b/lib/Listeners/LoadAdditionalScriptsListener.php @@ -22,6 +22,7 @@ class LoadAdditionalScriptsListener implements IEventListener { #[\Override] public function handle(Event $event): void { + /** @phpstan-ignore instanceof.alwaysTrue, booleanAnd.alwaysFalse */ if (!$event instanceof LoadAdditionalScriptsEvent && !$event instanceof BeforeTemplateRenderedEvent) { return; } diff --git a/lib/Listeners/NodeRenamedListener.php b/lib/Listeners/NodeRenamedListener.php index 7727c50f8..b97a27aca 100644 --- a/lib/Listeners/NodeRenamedListener.php +++ b/lib/Listeners/NodeRenamedListener.php @@ -27,6 +27,7 @@ public function __construct( #[\Override] public function handle(Event $event): void { + /** @phpstan-ignore instanceof.alwaysTrue */ if (!$event instanceof NodeRenamedEvent) { return; } diff --git a/lib/Mount/GroupFolderStorage.php b/lib/Mount/GroupFolderStorage.php index f3ca4ff1b..8a1d07aa1 100644 --- a/lib/Mount/GroupFolderStorage.php +++ b/lib/Mount/GroupFolderStorage.php @@ -95,6 +95,7 @@ public function getScanner($path = '', $storage = null): IScanner { $storage = $this; } + /** @phpstan-ignore method.impossibleType */ if ($storage->instanceOfStorage(ObjectStoreStorage::class)) { $storage->scanner = new ObjectStoreScanner($storage); } elseif (!isset($storage->scanner)) { diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index 4b265080d..a8a4cb905 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -9,7 +9,6 @@ namespace OCA\GroupFolders\Trash; use OC\Encryption\Exceptions\DecryptionFailedException; -use OC\Files\ObjectStore\ObjectStoreStorage; use OC\Files\Storage\Wrapper\Encryption; use OC\Files\Storage\Wrapper\Jail; use OCA\Files_Trashbin\Expiration; @@ -255,12 +254,7 @@ public function moveToTrash(IStorage $storage, string $internalPath): bool { $time = time(); $trashName = $name . '.d' . $time; $targetInternalPath = $trashFolder->getInternalPath() . '/' . $trashName; - // until the fix from https://github.com/nextcloud/server/pull/49262 is in all versions we support we need to manually disable the optimization - if ($storage->instanceOfStorage(Encryption::class)) { - $result = $this->moveFromEncryptedStorage($storage, $trashStorage, $internalPath, $targetInternalPath); - } else { - $result = $trashStorage->moveFromStorage($storage, $internalPath, $targetInternalPath); - } + $result = $trashStorage->moveFromStorage($storage, $internalPath, $targetInternalPath); if ($result) { $originalLocation = $internalPath; if ($storage->instanceOfStorage(ISharedStorage::class)) { @@ -291,52 +285,6 @@ public function moveToTrash(IStorage $storage, string $internalPath): bool { return false; } - /** - * move from storage when we can't just move within the storage - * - * This is copied from the fallback implementation from Common::moveFromStorage - */ - private function moveFromEncryptedStorage(IStorage $sourceStorage, IStorage $targetStorage, string $sourceInternalPath, string $targetInternalPath): bool { - if (!$sourceStorage->isDeletable($sourceInternalPath)) { - return false; - } - - // the trash should be the top wrapper, remove it to prevent recursive attempts to move to trash - if ($sourceStorage instanceof Storage) { - $sourceStorage = $sourceStorage->getWrapperStorage(); - } - - /** - * Some storages have a fourth parameter $preserveMtime - * @phpstan-ignore arguments.count - */ - $result = $targetStorage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true); - if ($result) { - if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) { - /** @var ObjectStoreStorage $sourceStorage */ - $sourceStorage->setPreserveCacheOnDelete(true); - } - try { - if ($sourceStorage->is_dir($sourceInternalPath)) { - $result = $sourceStorage->rmdir($sourceInternalPath); - } else { - $result = $sourceStorage->unlink($sourceInternalPath); - } - } finally { - if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) { - /** @var ObjectStoreStorage $sourceStorage */ - $sourceStorage->setPreserveCacheOnDelete(false); - } - } - } - - return $result; - } - - private function userHasAccessToFolder(IUser $user, int $folderId): bool { - return $this->folderManager->getFoldersForUser($user, $folderId) !== []; - } - private function userHasAccessToItem( GroupTrashItem $item, int $permission = Constants::PERMISSION_READ, diff --git a/lib/Versions/ExpireManager.php b/lib/Versions/ExpireManager.php index 567d78756..b8067f08d 100644 --- a/lib/Versions/ExpireManager.php +++ b/lib/Versions/ExpireManager.php @@ -9,7 +9,6 @@ namespace OCA\GroupFolders\Versions; use OCA\Files_Versions\Expiration; -use OCA\Files_Versions\Versions\IMetadataVersion; use OCA\Files_Versions\Versions\IVersion; /** @@ -73,7 +72,7 @@ protected function getAutoExpireList(int $time, array $versions): array { if ($nextInterval === -1 || $prevTimestamp > $nextInterval) { if ($version->getTimestamp() > $nextVersion) { // Do not expire versions with a label. - if ((!($version instanceof IMetadataVersion) || $version->getMetadataValue('label') === null || $version->getMetadataValue('label') === '') && !$version->isCurrentVersion()) { + if (($version->getMetadataValue('label') === null || $version->getMetadataValue('label') === '') && !$version->isCurrentVersion()) { //distance between two version too small, mark to delete $toDelete[] = $version; } @@ -122,7 +121,7 @@ public function getExpiredVersion(array $versions, int $time, bool $quotaExceede } // Do not expire versions with a label. - if ($version instanceof IMetadataVersion && $version->getMetadataValue('label') !== null && $version->getMetadataValue('label') !== '') { + if ($version->getMetadataValue('label') !== null && $version->getMetadataValue('label') !== '') { return false; } diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index 0dc0d07b0..4a5e223c6 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -274,10 +274,6 @@ public function getAllVersionedFiles(FolderDefinitionWithMappings $folder): arra $versionsFolder = $this->getVersionsFolder($folder); $folderWithPermissions = FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL); $mount = $this->mountProvider->getMount($folderWithPermissions, '/groupfolders/' . $folder->mountPoint); - if ($mount === null) { - $this->logger->error('Tried to get all the versioned files from a non existing mountpoint'); - return []; - } $this->mountManager->addMount($mount); try { diff --git a/phpstan.neon b/phpstan.neon index 6f7dfcd50..aa4d7c04e 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later parameters: - level: 3 + level: 4 phpVersion: min: 80200 max: 80500 diff --git a/tests/Trash/TrashBackendTest.php b/tests/Trash/TrashBackendTest.php index 72c983e59..6119313d2 100644 --- a/tests/Trash/TrashBackendTest.php +++ b/tests/Trash/TrashBackendTest.php @@ -11,8 +11,6 @@ use OC\Files\SetupManager; use OC\Group\Database; -use OCA\GroupFolders\ACL\ACLManager; -use OCA\GroupFolders\ACL\ACLManagerFactory; use OCA\GroupFolders\ACL\Rule; use OCA\GroupFolders\ACL\RuleManager; use OCA\GroupFolders\ACL\UserMapping\UserMapping; @@ -38,7 +36,6 @@ class TrashBackendTest extends TestCase { private string $folderName; private TrashBackend $trashBackend; private FolderManager $folderManager; - private ACLManager $aclManager; private RuleManager $ruleManager; private int $folderId; private Folder $managerUserFolder; @@ -63,9 +60,6 @@ public function setUp(): void { $this->trashBackend = \OCP\Server::get(TrashBackend::class); $this->folderManager = \OCP\Server::get(FolderManager::class); - /** @var ACLManagerFactory $aclManagerFactory */ - $aclManagerFactory = \OCP\Server::get(ACLManagerFactory::class); - $this->aclManager = $aclManagerFactory->getACLManager($this->managerUser); $this->ruleManager = \OCP\Server::get(RuleManager::class); $this->folderId = $this->folderManager->createFolder($this->folderName); @@ -83,7 +77,6 @@ public function setUp(): void { $this->assertTrue($this->managerUserFolder->nodeExists($this->folderName)); $this->assertTrue($this->normalUserFolder->nodeExists($this->folderName)); - /** @var GroupFolderStorage $groupFolderStorage */ $groupFolderStorage = $this->managerUserFolder->get($this->folderName)->getStorage(); $this->assertTrue($groupFolderStorage->instanceOfStorage(GroupFolderStorage::class)); $this->assertEquals($this->folderId, $groupFolderStorage->getFolderId()); From 297fe0e97b6170625f0c5e0b0634768a1044ef6b Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Feb 2026 10:35:55 +0100 Subject: [PATCH 07/12] feat(phpstan): Level 5 Signed-off-by: provokateurin --- lib/AppInfo/Application.php | 9 ++++-- lib/Command/ListCommand.php | 2 +- lib/{ => Listeners}/CacheListener.php | 29 ++++++++++--------- lib/Mount/MountProvider.php | 4 +-- lib/Trash/TrashBackend.php | 3 +- lib/Versions/VersionsBackend.php | 9 ++++-- phpstan.neon | 2 +- .../CircleDestroyedEventListenerTest.php | 3 ++ tests/Listeners/NodeRenamedListenerTest.php | 3 ++ 9 files changed, 39 insertions(+), 25 deletions(-) rename lib/{ => Listeners}/CacheListener.php (56%) diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index d963acb7b..3b9636744 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -20,12 +20,12 @@ use OCA\GroupFolders\BackgroundJob\ExpireGroupPlaceholder; use OCA\GroupFolders\BackgroundJob\ExpireGroupTrash as ExpireGroupTrashJob; use OCA\GroupFolders\BackgroundJob\ExpireGroupVersions as ExpireGroupVersionsJob; -use OCA\GroupFolders\CacheListener; use OCA\GroupFolders\Command\ExpireGroup\ExpireGroupBase; use OCA\GroupFolders\Command\ExpireGroup\ExpireGroupTrash; use OCA\GroupFolders\Command\ExpireGroup\ExpireGroupVersions; use OCA\GroupFolders\Command\ExpireGroup\ExpireGroupVersionsTrash; use OCA\GroupFolders\Folder\FolderManager; +use OCA\GroupFolders\Listeners\CacheListener; use OCA\GroupFolders\Listeners\CircleDestroyedEventListener; use OCA\GroupFolders\Listeners\LoadAdditionalScriptsListener; use OCA\GroupFolders\Listeners\NodeRenamedListener; @@ -42,6 +42,8 @@ use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\TimedJob; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Cache\CacheEntryInsertedEvent; +use OCP\Files\Cache\CacheEntryUpdatedEvent; use OCP\Files\Config\IMountProviderCollection; use OCP\Files\Events\Node\NodeRenamedEvent; use OCP\Files\IRootFolder; @@ -79,6 +81,8 @@ public function register(IRegistrationContext $context): void { $context->registerEventListener(BeforeTemplateRenderedEvent::class, LoadAdditionalScriptsListener::class); $context->registerEventListener(CircleDestroyedEvent::class, CircleDestroyedEventListener::class); $context->registerEventListener(NodeRenamedEvent::class, NodeRenamedListener::class); + $context->registerEventListener(CacheEntryInsertedEvent::class, CacheListener::class, 99999); + $context->registerEventListener(CacheEntryUpdatedEvent::class, CacheListener::class, 99999); $context->registerService(MountProvider::class, function (ContainerInterface $c): MountProvider { /** @var IAppConfig $config */ @@ -187,13 +191,12 @@ public function register(IRegistrationContext $context): void { #[\Override] public function boot(IBootContext $context): void { - $context->injectFn(function (IMountProviderCollection $mountProviderCollection, CacheListener $cacheListener, IEventDispatcher $eventDispatcher): void { + $context->injectFn(function (IMountProviderCollection $mountProviderCollection, IEventDispatcher $eventDispatcher): void { $mountProviderCollection->registerProvider(Server::get(MountProvider::class)); $eventDispatcher->addListener(GroupDeletedEvent::class, function (GroupDeletedEvent $event): void { Server::get(FolderManager::class)->deleteGroup($event->getGroup()->getGID()); }); - $cacheListener->listen(); }); } } diff --git a/lib/Command/ListCommand.php b/lib/Command/ListCommand.php index 1dd64f7e1..3e9574264 100644 --- a/lib/Command/ListCommand.php +++ b/lib/Command/ListCommand.php @@ -128,6 +128,6 @@ private function permissionsToString(int $permissions): string { return 'none'; } - return implode(', ', array_filter(self::PERMISSION_NAMES, fn (int $possiblePermission): int => $possiblePermission & $permissions, ARRAY_FILTER_USE_KEY)); + return implode(', ', array_filter(self::PERMISSION_NAMES, fn (int $possiblePermission): bool => ($possiblePermission & $permissions) === $permissions, ARRAY_FILTER_USE_KEY)); } } diff --git a/lib/CacheListener.php b/lib/Listeners/CacheListener.php similarity index 56% rename from lib/CacheListener.php rename to lib/Listeners/CacheListener.php index bfc7f8bd7..6ed97c884 100644 --- a/lib/CacheListener.php +++ b/lib/Listeners/CacheListener.php @@ -1,31 +1,32 @@ eventDispatcher->addListener(CacheEntryInsertedEvent::class, $this->onCacheEvent(...), 99999); - $this->eventDispatcher->addListener(CacheEntryUpdatedEvent::class, $this->onCacheEvent(...), 99999); - } +/** + * @template-implements IEventListener + */ +class CacheListener implements IEventListener { + #[\Override] + public function handle(Event $event): void { + /** @phpstan-ignore instanceof.alwaysTrue, booleanAnd.alwaysFalse */ + if (!$event instanceof CacheEntryInsertedEvent && !$event instanceof CacheEntryUpdatedEvent) { + return; + } - public function onCacheEvent(ICacheEvent $event): void { $storage = $event->getStorage(); if (!$storage->instanceOfStorage(GroupFolderStorage::class)) { return; diff --git a/lib/Mount/MountProvider.php b/lib/Mount/MountProvider.php index e117b35b7..784e2944f 100644 --- a/lib/Mount/MountProvider.php +++ b/lib/Mount/MountProvider.php @@ -151,7 +151,7 @@ public function getMount( } public function getTrashMount( - FolderDefinitionWithPermissions $folder, + FolderDefinition $folder, string $mountPoint, IStorageFactory $loader, ?IUser $user, @@ -212,7 +212,7 @@ public function getVersionsMount( * @param 'files'|'trash'|'versions' $type */ public function getGroupFolderStorage( - FolderDefinitionWithPermissions $folder, + FolderDefinition $folder, ?IUser $user, ?ICacheEntry $rootCacheEntry, string $type = 'files', diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index a8a4cb905..71269dfe5 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -16,6 +16,7 @@ use OCA\Files_Trashbin\Trash\ITrashBackend; use OCA\Files_Trashbin\Trash\ITrashItem; use OCA\GroupFolders\ACL\ACLManagerFactory; +use OCA\GroupFolders\Folder\FolderDefinition; use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions; use OCA\GroupFolders\Folder\FolderManager; use OCA\GroupFolders\Folder\FolderWithMappingsAndCache; @@ -331,7 +332,7 @@ private function getNodeForTrashItem(IUser $user, ITrashItem $trashItem): ?Node return null; } - private function setupTrashFolder(FolderDefinitionWithPermissions $folder, ?IUser $user = null): Folder { + private function setupTrashFolder(FolderDefinition $folder, ?IUser $user = null): Folder { $folderId = $folder->id; $uid = $user ? $user->getUID() : 'dummy'; diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index 4a5e223c6..850f7bf6f 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -9,6 +9,7 @@ namespace OCA\GroupFolders\Versions; +use OC\Files\Storage\Storage; use OCA\DAV\Connector\Sabre\Exception\Forbidden; use OCA\Files_Versions\Versions\IDeletableVersionBackend; use OCA\Files_Versions\Versions\IMetadataVersion; @@ -179,8 +180,8 @@ function (GroupVersionEntity $versionEntity) use ($versionsFolder, $mountPoint, } } - if ($versionFile instanceof Folder) { - $this->logger->warning('Encountered version file that was a folder', ['fileid' => $versionFile->getId(), 'path' => $versionFile->getPath()]); + if (!$versionFile instanceof File) { + $this->logger->warning('Encountered version file that was not a file', ['fileid' => $versionFile->getId(), 'path' => $versionFile->getPath()]); $versionFile->delete(); $this->groupVersionsMapper->delete($versionEntity); @@ -286,7 +287,9 @@ public function getAllVersionedFiles(FolderDefinitionWithMappings $folder): arra $files = array_map(function (int $fileId) use ($mount): ?FileInfo { $cacheEntry = $mount->getStorage()->getCache()->get($fileId); if ($cacheEntry) { - return new \OC\Files\FileInfo($mount->getMountPoint() . '/' . $cacheEntry->getPath(), $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount); + /** @var Storage $storage */ + $storage = $mount->getStorage(); + return new \OC\Files\FileInfo($mount->getMountPoint() . '/' . $cacheEntry->getPath(), $storage, $cacheEntry->getPath(), $cacheEntry, $mount); } else { return null; } diff --git a/phpstan.neon b/phpstan.neon index aa4d7c04e..50d9b7efc 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later parameters: - level: 4 + level: 5 phpVersion: min: 80200 max: 80500 diff --git a/tests/Listeners/CircleDestroyedEventListenerTest.php b/tests/Listeners/CircleDestroyedEventListenerTest.php index 5cac85aea..ab6a35bd6 100644 --- a/tests/Listeners/CircleDestroyedEventListenerTest.php +++ b/tests/Listeners/CircleDestroyedEventListenerTest.php @@ -37,6 +37,9 @@ public function testHandleInvalid(): void { ->expects($this->never()) ->method('deleteCircle'); + /** + * @phpstan-ignore argument.type + */ $this->listener->handle($event); } diff --git a/tests/Listeners/NodeRenamedListenerTest.php b/tests/Listeners/NodeRenamedListenerTest.php index 930b9f3eb..d62e70a3c 100644 --- a/tests/Listeners/NodeRenamedListenerTest.php +++ b/tests/Listeners/NodeRenamedListenerTest.php @@ -86,6 +86,9 @@ public function testHandleInvalid(): void { ->expects($this->never()) ->method('getSource'); + /** + * @phpstan-ignore argument.type + */ $this->listener->handle($event); } From 77e1c2f2a68f1cb23b38680cc56ae4f427b6459e Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Feb 2026 12:03:41 +0100 Subject: [PATCH 08/12] feat(phpstan): Level 6 Signed-off-by: provokateurin --- lib/ACL/ACLCacheWrapper.php | 9 ++++++ lib/ACL/ACLStorageWrapper.php | 13 ++++++++ lib/ACL/Rule.php | 11 +++++++ lib/ACL/RuleManager.php | 12 +++++++ lib/ACL/UserMapping/UserMappingManager.php | 3 +- lib/AppInfo/Application.php | 3 ++ lib/AuthorizedAdminSettingMiddleware.php | 3 ++ lib/Command/ACL.php | 4 +++ lib/Command/Group.php | 3 ++ lib/Command/Scan.php | 4 +++ lib/Controller/FolderController.php | 28 ++++++++++++++-- lib/DAV/GroupFoldersHome.php | 3 ++ lib/DAV/RootCollection.php | 1 + lib/Folder/FolderDefinition.php | 3 ++ lib/Folder/FolderDefinitionWithMappings.php | 14 ++++++++ .../FolderDefinitionWithPermissions.php | 13 ++++++++ lib/Folder/FolderManager.php | 10 ++++++ lib/Folder/FolderWithMappingsAndCache.php | 15 +++++++++ .../Version1000000Date20210216085047.php | 3 ++ .../Version102020Date20180806161449.php | 3 ++ .../Version103000Date20180806161724.php | 12 +++++++ .../Version104000Date20180918132853.php | 3 ++ .../Version1401000Date20230426112001.php | 3 ++ .../Version1401000Date20230426112002.php | 3 ++ .../Version16000Date20230821085801.php | 3 ++ .../Version19000Date20240903062631.php | 3 ++ .../Version19000Date20241029123147.php | 5 ++- .../Version2000000Date20250128110101.php | 3 ++ .../Version20001Date20250612140256.php | 6 ++++ .../Version201000Date20190111132839.php | 3 ++ .../Version201000Date20190212150323.php | 3 ++ .../Version2100000Date20251104151854.php | 5 +-- .../Version21000Date20250925152053.php | 3 +- .../Version300000Date20240905185515.php | 3 ++ .../Version401001Date20190715092137.php | 3 ++ .../Version501000Date20190927102434.php | 3 ++ .../Version501000Date20191218182434.php | 3 ++ .../Version802000Date20201119112624.php | 3 ++ lib/Mount/FolderStorageManager.php | 16 +++++++++- lib/Mount/GroupFolderStorage.php | 3 ++ lib/Mount/GroupMountPoint.php | 4 +++ lib/Mount/MountProvider.php | 6 +++- lib/Mount/RootEntryCache.php | 7 ++++ lib/Mount/RootPermissionsMask.php | 6 +++- lib/Service/DelegationService.php | 3 ++ lib/Settings/Admin.php | 7 ++++ lib/Trash/TrashBackend.php | 6 ++++ lib/Trash/TrashManager.php | 32 ------------------- lib/Versions/GroupVersion.php | 3 ++ lib/Versions/GroupVersionEntity.php | 9 ++++++ lib/Versions/VersionsBackend.php | 3 ++ phpstan.neon | 2 +- tests/ACL/ACLCacheWrapperTest.php | 1 + tests/ACL/ACLScannerTest.php | 3 ++ tests/ACL/ACLStorageWrapperTest.php | 1 + tests/ACL/RuleTest.php | 7 ++++ .../LoadAdditionalScriptsListenerTest.php | 4 +++ 57 files changed, 302 insertions(+), 49 deletions(-) diff --git a/lib/ACL/ACLCacheWrapper.php b/lib/ACL/ACLCacheWrapper.php index ce341078f..83f44a855 100644 --- a/lib/ACL/ACLCacheWrapper.php +++ b/lib/ACL/ACLCacheWrapper.php @@ -24,6 +24,9 @@ public function __construct( parent::__construct($cache); } + /** + * @param array $rules + */ private function getACLPermissionsForPath(string $path, array $rules = []): int { if ($rules) { $permissions = $this->aclManager->getPermissionsForPathFromRules($this->folderId, $path, $rules); @@ -43,6 +46,9 @@ private function getACLPermissionsForPath(string $path, array $rules = []): int return $canRead ? $permissions : 0; } + /** + * @param array $rules + */ #[\Override] protected function formatCacheEntry($entry, array $rules = []): ICacheEntry|false { if (isset($entry['permissions'])) { @@ -56,6 +62,9 @@ protected function formatCacheEntry($entry, array $rules = []): ICacheEntry|fals return $entry; } + /** + * @return list + */ #[\Override] public function getFolderContentsById($fileId, ?string $mimeTypeFilter = null): array { /** @psalm-suppress TooManyArguments Remove this in a few days */ diff --git a/lib/ACL/ACLStorageWrapper.php b/lib/ACL/ACLStorageWrapper.php index 97c9039ee..231c9b457 100644 --- a/lib/ACL/ACLStorageWrapper.php +++ b/lib/ACL/ACLStorageWrapper.php @@ -9,6 +9,7 @@ namespace OCA\GroupFolders\ACL; use Icewind\Streams\IteratorDirectory; +use OC\Files\Storage\Storage; use OC\Files\Storage\Wrapper\Wrapper; use OCP\Constants; use OCP\Files\Cache\ICache; @@ -22,6 +23,9 @@ class ACLStorageWrapper extends Wrapper implements IConstructableStorage { private readonly int $folderId; private readonly int $storageId; + /** + * @param array{storage: Storage, acl_manager: ACLManager, in_share: bool, folder_id: int, storage_id: int} $arguments + */ public function __construct(array $arguments) { parent::__construct($arguments); $this->aclManager = $arguments['acl_manager']; @@ -208,6 +212,9 @@ public function getCache(string $path = '', ?IStorage $storage = null): ICache { return new ACLCacheWrapper($sourceCache, $this->aclManager, $this->folderId, $this->inShare); } + /** + * @return ?array + */ #[\Override] public function getMetaData(string $path): ?array { $data = parent::getMetaData($path); @@ -244,6 +251,9 @@ public function is_file(string $path): bool { && parent::is_file($path); } + /** + * @return array|false + */ #[\Override] public function stat(string $path): array|false { if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) { @@ -331,6 +341,9 @@ public function getDirectDownload(string $path): array|false { return parent::getDirectDownload($path); } + /** + * @return \Traversable> + */ #[\Override] public function getDirectoryContent(string $directory): \Traversable { $content = $this->getWrapperStorage()->getDirectoryContent($directory); diff --git a/lib/ACL/Rule.php b/lib/ACL/Rule.php index c82613466..669100b6e 100644 --- a/lib/ACL/Rule.php +++ b/lib/ACL/Rule.php @@ -109,6 +109,16 @@ public function xmlSerialize(Writer $writer): void { $writer->write($data); } + /** + * @return array{ + * mapping: array{ + * type: 'user'|'group'|'dummy'|'circle', + * id: string, + * }, + * mask: int, + * permissions: int, + * } + */ #[\Override] public function jsonSerialize(): array { return [ @@ -138,6 +148,7 @@ public static function xmlDeserialize(Reader $reader): Rule { /** * merge multiple rules that apply on the same file where allow overwrites deny + * @param Rule[] $rules */ public static function mergeRules(array $rules): Rule { // or'ing the masks to get a new mask that masks all set permissions diff --git a/lib/ACL/RuleManager.php b/lib/ACL/RuleManager.php index 5787e5313..e0944ead4 100644 --- a/lib/ACL/RuleManager.php +++ b/lib/ACL/RuleManager.php @@ -25,6 +25,9 @@ public function __construct( ) { } + /** + * @param array{mapping_type?: 'user'|'group'|'dummy'|'circle', mapping_id: string, fileid: int, mask: int, permissions: int} $data + */ private function createRule(array $data): ?Rule { if (!isset($data['mapping_type'])) { return null; @@ -196,6 +199,11 @@ public function getAllRulesForPaths(int $storageId, array $filePaths): array { return $this->rulesByPath($rows); } + /** + * @param list $rows + * @param array> $result + * @return array> + */ private function rulesByPath(array $rows, array $result = []): array { foreach ($rows as $row) { $rule = $this->createRule($row); @@ -210,6 +218,10 @@ private function rulesByPath(array $rows, array $result = []): array { return $result; } + /** + * @param list $rows + * @return array>> + */ private function rulesByFileId(array $rows): array { $result = []; foreach ($rows as $row) { diff --git a/lib/ACL/UserMapping/UserMappingManager.php b/lib/ACL/UserMapping/UserMappingManager.php index c22c4e2ff..381a8ba37 100644 --- a/lib/ACL/UserMapping/UserMappingManager.php +++ b/lib/ACL/UserMapping/UserMappingManager.php @@ -33,7 +33,7 @@ public function __construct( #[\Override] public function getMappingsForUser(IUser $user, bool $userAssignable = true): array { $groupMappings = array_values(array_map(fn (IGroup $group): UserMapping => new UserMapping('group', $group->getGID(), $group->getDisplayName()), $this->groupManager->getUserGroups($user))); - $circleMappings = array_values(array_map(fn (Circle $circle): UserMapping => new UserMapping('circle', $circle->getSingleId(), $circle->getDisplayName()), $this->getUserCircles($user->getUID()))); + $circleMappings = array_map(fn (Circle $circle): UserMapping => new UserMapping('circle', $circle->getSingleId(), $circle->getDisplayName()), $this->getUserCircles($user->getUID())); return array_merge([ new UserMapping('user', $user->getUID(), $user->getDisplayName()), @@ -91,6 +91,7 @@ private function getCircle(string $groupId): ?Circle { /** * returns list of circles a user is member of + * @return list */ private function getUserCircles(string $userId): array { $circlesManager = $this->getCirclesManager(); diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 3b9636744..fb89eb42f 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -62,6 +62,9 @@ class Application extends App implements IBootstrap { public const APP_ID = 'groupfolders'; + /** + * @param array $urlParams + */ public function __construct(array $urlParams = []) { parent::__construct(self::APP_ID, $urlParams); } diff --git a/lib/AuthorizedAdminSettingMiddleware.php b/lib/AuthorizedAdminSettingMiddleware.php index 30464e726..5184459cc 100644 --- a/lib/AuthorizedAdminSettingMiddleware.php +++ b/lib/AuthorizedAdminSettingMiddleware.php @@ -38,6 +38,9 @@ public function beforeController(Controller $controller, string $methodName): vo } } + /** + * @return JSONResponse|TemplateResponse + */ #[\Override] public function afterException(Controller $controller, string $methodName, Exception $exception): Response { /** @var Http::STATUS_* $code */ diff --git a/lib/Command/ACL.php b/lib/Command/ACL.php index e6189283e..6a6f0e92f 100644 --- a/lib/Command/ACL.php +++ b/lib/Command/ACL.php @@ -236,6 +236,9 @@ private function printPermissions(InputInterface $input, OutputInterface $output } } + /** + * @return array{'user'|'group'|'circle', string} + */ private function convertMappingOptions(InputInterface $input): array { if ($input->getOption('user')) { return ['user', $input->getOption('user')]; @@ -252,6 +255,7 @@ private function convertMappingOptions(InputInterface $input): array { /** * @param list $permissions + * @return array{int, int} */ private function parsePermissions(array $permissions): array { $mask = 0; diff --git a/lib/Command/Group.php b/lib/Command/Group.php index ab40c944f..1b976072e 100644 --- a/lib/Command/Group.php +++ b/lib/Command/Group.php @@ -83,6 +83,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int return -1; } + /** + * @param list $input + */ private function getNewPermissions(array $input): int { $permissions = 1; $values = self::PERMISSION_VALUES; diff --git a/lib/Command/Scan.php b/lib/Command/Scan.php index 3d146488b..b88a9cbde 100644 --- a/lib/Command/Scan.php +++ b/lib/Command/Scan.php @@ -164,6 +164,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 0; } + /** + * @param list $headers + * @param list $rows + */ protected function showSummary(array $headers, array $rows, OutputInterface $output): void { $table = new Table($output); $table diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index 603524eba..3b4d14264 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -37,6 +37,23 @@ * @phpstan-import-type GroupFoldersCircle from ResponseDefinitions * @phpstan-import-type GroupFoldersUser from ResponseDefinitions * @phpstan-import-type GroupFoldersFolder from ResponseDefinitions + * @phpstan-import-type GroupFoldersAclManage from ResponseDefinitions + * @phpstan-type GroupFoldersFolderXML = array{ + * id: int, + * mount_point: string, + * groups: list, + * quota: int, + * size: int, + * acl: bool, + * acl_default_no_permission: bool, + * manage: list, + * sortIndex?: int, + * } */ class FolderController extends OCSController { private readonly ?IUser $user; @@ -485,16 +502,17 @@ public function renameFolder(int $id, string $mountpoint): DataResponse { /** * Overwrite response builder to customize xml handling to deal with spaces in folder names + * @param DataResponse, array{}> $data + * @return V1Response> */ private function buildOCSResponseXML(string $format, DataResponse $data): V1Response { - /** @var array $folderData */ $folderData = $data->getData(); if (isset($folderData['id'])) { // single folder response $folderData = $this->folderDataForXML($folderData); } elseif (isset($folderData['folder'])) { // single folder response - $folderData['folder'] = $this->folderDataForXML(['folder']); + $folderData['folder'] = $this->folderDataForXML($folderData['folder']); } elseif (count($folderData) && isset(current($folderData)['id'])) { // folder list $folderData = array_map($this->folderDataForXML(...), $folderData); @@ -505,8 +523,12 @@ private function buildOCSResponseXML(string $format, DataResponse $data): V1Resp return new V1Response($data, $format); } + /** + * @param GroupFoldersFolder $data + * @return GroupFoldersFolderXML + */ private function folderDataForXML(array $data): array { - $groups = $data['group_details'] ?? []; + $groups = $data['group_details']; unset($data['group_details']); $data['groups'] = []; foreach ($groups as $id => $group) { diff --git a/lib/DAV/GroupFoldersHome.php b/lib/DAV/GroupFoldersHome.php index 63b80f1f0..ed7d85da0 100644 --- a/lib/DAV/GroupFoldersHome.php +++ b/lib/DAV/GroupFoldersHome.php @@ -19,6 +19,9 @@ use Sabre\DAV\ICollection; class GroupFoldersHome implements ICollection { + /** + * @param array{uri: string} $principalInfo + */ public function __construct( private array $principalInfo, private readonly FolderManager $folderManager, diff --git a/lib/DAV/RootCollection.php b/lib/DAV/RootCollection.php index 6b814c5d9..42e81321a 100644 --- a/lib/DAV/RootCollection.php +++ b/lib/DAV/RootCollection.php @@ -30,6 +30,7 @@ public function __construct( * The passed array contains principal information, and is guaranteed to * at least contain a uri item. Other properties may or may not be * supplied by the authentication backend. + * @param array{uri: string} $principalInfo */ #[\Override] public function getChildForPrincipal(array $principalInfo): GroupFoldersHome { diff --git a/lib/Folder/FolderDefinition.php b/lib/Folder/FolderDefinition.php index 61a27aaaf..6a821e40e 100644 --- a/lib/Folder/FolderDefinition.php +++ b/lib/Folder/FolderDefinition.php @@ -9,6 +9,9 @@ namespace OCA\GroupFolders\Folder; class FolderDefinition { + /** + * @param array{separate-storage?: bool} $options + */ public function __construct( public readonly int $id, public readonly string $mountPoint, diff --git a/lib/Folder/FolderDefinitionWithMappings.php b/lib/Folder/FolderDefinitionWithMappings.php index a06afb85a..eb08b6a45 100644 --- a/lib/Folder/FolderDefinitionWithMappings.php +++ b/lib/Folder/FolderDefinitionWithMappings.php @@ -53,12 +53,26 @@ public static function fromFolder(FolderDefinition $folder, array $groups, array ); } + /** + * @return array{ + * id: int, + * mount_point: string, + * quota: int, + * acl: bool, + * acl_default_no_permission: bool, + * storage_id: int, + * root_id: int, + * groups: array, + * manage: list, + * } + */ public function toArray(): array { return [ 'id' => $this->id, 'mount_point' => $this->mountPoint, 'quota' => $this->quota, 'acl' => $this->acl, + 'acl_default_no_permission' => $this->aclDefaultNoPermission, 'storage_id' => $this->storageId, 'root_id' => $this->rootId, 'groups' => $this->groups, diff --git a/lib/Folder/FolderDefinitionWithPermissions.php b/lib/Folder/FolderDefinitionWithPermissions.php index 69c25f317..6327d6311 100644 --- a/lib/Folder/FolderDefinitionWithPermissions.php +++ b/lib/Folder/FolderDefinitionWithPermissions.php @@ -46,6 +46,19 @@ public static function fromFolder(FolderDefinition $folder, ICacheEntry $rootCac ); } + /** + * @return array{ + * id: int, + * mount_point: string, + * permissions: int, + * quota: int, + * acl: bool, + * acl_default_no_permission: bool, + * storage_id: int, + * root_id: int, + * root_cache_entry: ICacheEntry, + * } + */ public function toArray(): array { return [ 'id' => $this->id, diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php index 34ee88ef9..ab908e2be 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -219,6 +219,7 @@ public function getAllFoldersForUserWithSize(IUser $user): array { } /** + * @param list $folderIds * @return array> * @throws Exception */ @@ -362,6 +363,7 @@ public function getFolderByPath(string $path): int { } /** + * @param list $folderIds * @return array> * @throws Exception */ @@ -597,6 +599,10 @@ public function searchUsers(int $id, string $search = '', int $limit = 10, int $ return array_values($users); } + /** + * @param array{folder_id?: int, options?: string} $row + * @return array{separate-storage?: bool} + */ private function getFolderOptions(array $row): array { if (!isset($row['options'])) { return []; @@ -616,6 +622,9 @@ private function getFolderOptions(array $row): array { return $options; } + /** + * @param array{folder_id: int, mount_point: string, quota: int, acl: bool, acl_default_no_permission: bool, storage_id: int, root_id: int} $row + */ private function rowToFolder(array $row): FolderDefinition { return new FolderDefinition( (int)$row['folder_id'], @@ -749,6 +758,7 @@ public function trimMountpoint(string $mountpoint): string { } /** + * @param array{separate-storage?: bool} $options * @throws Exception */ public function createFolder(string $mountPoint, array $options = [], bool $aclDefaultNoPermission = false): int { diff --git a/lib/Folder/FolderWithMappingsAndCache.php b/lib/Folder/FolderWithMappingsAndCache.php index 6b847df43..b78dbfc4b 100644 --- a/lib/Folder/FolderWithMappingsAndCache.php +++ b/lib/Folder/FolderWithMappingsAndCache.php @@ -52,6 +52,21 @@ public static function fromFolderWithMapping(FolderDefinitionWithMappings $folde ); } + /** + * @return array{ + * id: int, + * mount_point: string, + * quota: int, + * acl: bool, + * acl_default_no_permission: bool, + * storage_id: int, + * root_id: int, + * root_cache_entry: ICacheEntry, + * groups: array, + * manage: list, + * options: array{separate-storage?: bool}, + * } + */ #[\Override] public function toArray(): array { return [ diff --git a/lib/Migration/Version1000000Date20210216085047.php b/lib/Migration/Version1000000Date20210216085047.php index 76f9a47bd..7ba0854e8 100644 --- a/lib/Migration/Version1000000Date20210216085047.php +++ b/lib/Migration/Version1000000Date20210216085047.php @@ -13,6 +13,9 @@ use OCP\Migration\SimpleMigrationStep; class Version1000000Date20210216085047 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version102020Date20180806161449.php b/lib/Migration/Version102020Date20180806161449.php index 9804184d1..f69288545 100644 --- a/lib/Migration/Version102020Date20180806161449.php +++ b/lib/Migration/Version102020Date20180806161449.php @@ -14,6 +14,9 @@ use OCP\Migration\SimpleMigrationStep; class Version102020Date20180806161449 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version103000Date20180806161724.php b/lib/Migration/Version103000Date20180806161724.php index 7221ff465..a60099c54 100644 --- a/lib/Migration/Version103000Date20180806161724.php +++ b/lib/Migration/Version103000Date20180806161724.php @@ -14,6 +14,9 @@ use OCP\Migration\SimpleMigrationStep; class Version103000Date20180806161724 extends SimpleMigrationStep { + /** + * @var list> + */ private array $applicableData = []; public function __construct( @@ -21,6 +24,9 @@ public function __construct( ) { } + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void { /** @var ISchemaWrapper $schema */ @@ -36,6 +42,9 @@ public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array } } + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ @@ -73,6 +82,9 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op return $schema; } + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void { if (count($this->applicableData)) { diff --git a/lib/Migration/Version104000Date20180918132853.php b/lib/Migration/Version104000Date20180918132853.php index 69784cf2e..a7e707380 100644 --- a/lib/Migration/Version104000Date20180918132853.php +++ b/lib/Migration/Version104000Date20180918132853.php @@ -23,6 +23,9 @@ public function description(): string { return 'Adds table to store trashbin information for Team folders'; } + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version1401000Date20230426112001.php b/lib/Migration/Version1401000Date20230426112001.php index 99b86ab9e..6ee4c4b7d 100644 --- a/lib/Migration/Version1401000Date20230426112001.php +++ b/lib/Migration/Version1401000Date20230426112001.php @@ -14,6 +14,9 @@ use OCP\Migration\SimpleMigrationStep; class Version1401000Date20230426112001 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version1401000Date20230426112002.php b/lib/Migration/Version1401000Date20230426112002.php index 2d4aa129b..b0dace58a 100644 --- a/lib/Migration/Version1401000Date20230426112002.php +++ b/lib/Migration/Version1401000Date20230426112002.php @@ -13,6 +13,9 @@ use OCP\Migration\SimpleMigrationStep; class Version1401000Date20230426112002 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version16000Date20230821085801.php b/lib/Migration/Version16000Date20230821085801.php index c652456aa..c823f0c8d 100644 --- a/lib/Migration/Version16000Date20230821085801.php +++ b/lib/Migration/Version16000Date20230821085801.php @@ -16,6 +16,9 @@ use OCP\Migration\SimpleMigrationStep; class Version16000Date20230821085801 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version19000Date20240903062631.php b/lib/Migration/Version19000Date20240903062631.php index d1e69a9de..6bea851e5 100644 --- a/lib/Migration/Version19000Date20240903062631.php +++ b/lib/Migration/Version19000Date20240903062631.php @@ -15,6 +15,9 @@ use OCP\Migration\SimpleMigrationStep; class Version19000Date20240903062631 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version19000Date20241029123147.php b/lib/Migration/Version19000Date20241029123147.php index ca7e49546..c7911bd84 100644 --- a/lib/Migration/Version19000Date20241029123147.php +++ b/lib/Migration/Version19000Date20241029123147.php @@ -16,9 +16,8 @@ class Version19000Date20241029123147 extends SimpleMigrationStep { /** - * @param Closure(): ISchemaWrapper $schemaClosure - */ - #[\Override] + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); diff --git a/lib/Migration/Version2000000Date20250128110101.php b/lib/Migration/Version2000000Date20250128110101.php index 94c7bd696..3b2489ad8 100644 --- a/lib/Migration/Version2000000Date20250128110101.php +++ b/lib/Migration/Version2000000Date20250128110101.php @@ -16,6 +16,9 @@ #[AddIndex('group_folders_groups', IndexType::INDEX, 'adding index on single circle id for better select')] class Version2000000Date20250128110101 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version20001Date20250612140256.php b/lib/Migration/Version20001Date20250612140256.php index 2c905fb02..44805aab4 100644 --- a/lib/Migration/Version20001Date20250612140256.php +++ b/lib/Migration/Version20001Date20250612140256.php @@ -28,6 +28,9 @@ public function __construct( ) { } + /** + * @phpstan-ignore missingType.iterableValue + */ #[Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ @@ -49,6 +52,9 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt return null; } + /** + * @phpstan-ignore missingType.iterableValue + */ #[Override] public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { $storageId = $this->getJailedGroupFolderStorageId(); diff --git a/lib/Migration/Version201000Date20190111132839.php b/lib/Migration/Version201000Date20190111132839.php index 080c933b1..21225fa69 100644 --- a/lib/Migration/Version201000Date20190111132839.php +++ b/lib/Migration/Version201000Date20190111132839.php @@ -23,6 +23,9 @@ public function description(): string { return 'Adds table to store ACL information for Team folders'; } + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version201000Date20190212150323.php b/lib/Migration/Version201000Date20190212150323.php index 6cefccfc2..fb0c3613c 100644 --- a/lib/Migration/Version201000Date20190212150323.php +++ b/lib/Migration/Version201000Date20190212150323.php @@ -13,6 +13,9 @@ use OCP\Migration\SimpleMigrationStep; class Version201000Date20190212150323 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version2100000Date20251104151854.php b/lib/Migration/Version2100000Date20251104151854.php index 64786caa2..339ffd615 100644 --- a/lib/Migration/Version2100000Date20251104151854.php +++ b/lib/Migration/Version2100000Date20251104151854.php @@ -18,10 +18,7 @@ class Version2100000Date20251104151854 extends SimpleMigrationStep { /** - * @param IOutput $output - * @param Closure(): ISchemaWrapper $schemaClosure - * @param array $options - * @return null|ISchemaWrapper + * @phpstan-ignore missingType.iterableValue */ #[Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { diff --git a/lib/Migration/Version21000Date20250925152053.php b/lib/Migration/Version21000Date20250925152053.php index 4dc0f189c..9eeaacf4f 100644 --- a/lib/Migration/Version21000Date20250925152053.php +++ b/lib/Migration/Version21000Date20250925152053.php @@ -11,7 +11,6 @@ use Closure; use OCA\GroupFolders\Folder\FolderManager; -use OCP\DB\ISchemaWrapper; use OCP\Migration\IOutput; use OCP\Migration\SimpleMigrationStep; use Override; @@ -26,7 +25,7 @@ public function __construct( } /** - * @param Closure(): ISchemaWrapper $schemaClosure + * @phpstan-ignore missingType.iterableValue */ #[Override] public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { diff --git a/lib/Migration/Version300000Date20240905185515.php b/lib/Migration/Version300000Date20240905185515.php index e1b9c259f..0bbf109ad 100644 --- a/lib/Migration/Version300000Date20240905185515.php +++ b/lib/Migration/Version300000Date20240905185515.php @@ -19,6 +19,9 @@ * Adds the delete_by column to the group_folders_trash table */ class Version300000Date20240905185515 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version401001Date20190715092137.php b/lib/Migration/Version401001Date20190715092137.php index eb71d647c..34077cbb0 100644 --- a/lib/Migration/Version401001Date20190715092137.php +++ b/lib/Migration/Version401001Date20190715092137.php @@ -13,6 +13,9 @@ use OCP\Migration\SimpleMigrationStep; class Version401001Date20190715092137 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version501000Date20190927102434.php b/lib/Migration/Version501000Date20190927102434.php index 7bdd77348..55a6da734 100644 --- a/lib/Migration/Version501000Date20190927102434.php +++ b/lib/Migration/Version501000Date20190927102434.php @@ -13,6 +13,9 @@ use OCP\Migration\SimpleMigrationStep; class Version501000Date20190927102434 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version501000Date20191218182434.php b/lib/Migration/Version501000Date20191218182434.php index 88c38fdee..8c3a6d1ba 100644 --- a/lib/Migration/Version501000Date20191218182434.php +++ b/lib/Migration/Version501000Date20191218182434.php @@ -13,6 +13,9 @@ use OCP\Migration\SimpleMigrationStep; class Version501000Date20191218182434 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version802000Date20201119112624.php b/lib/Migration/Version802000Date20201119112624.php index 4d4b1d407..4065a370c 100644 --- a/lib/Migration/Version802000Date20201119112624.php +++ b/lib/Migration/Version802000Date20201119112624.php @@ -13,6 +13,9 @@ use OCP\Migration\SimpleMigrationStep; class Version802000Date20201119112624 extends SimpleMigrationStep { + /** + * @phpstan-ignore missingType.iterableValue + */ #[\Override] public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ diff --git a/lib/Mount/FolderStorageManager.php b/lib/Mount/FolderStorageManager.php index ded11a817..423450460 100644 --- a/lib/Mount/FolderStorageManager.php +++ b/lib/Mount/FolderStorageManager.php @@ -12,6 +12,7 @@ use OC\Files\ObjectStore\ObjectStoreStorage; use OC\Files\ObjectStore\PrimaryObjectStoreConfig; use OC\Files\Storage\Local; +use OC\Files\Storage\Storage; use OC\Files\Storage\Wrapper\Jail; use OCA\GroupFolders\ACL\ACLManagerFactory; use OCA\GroupFolders\ACL\ACLStorageWrapper; @@ -27,6 +28,7 @@ class FolderStorageManager { private readonly bool $enableEncryption; + /** @var array */ private array $cachedFolders = []; public function __construct( @@ -40,6 +42,7 @@ public function __construct( } /** + * @param array $options * @return array{storage_id: int, root_id: int} */ public function initRootAndStorageForFolder(int $folderId, bool $separateStorage, array $options): array { @@ -61,6 +64,7 @@ public function initRootAndStorageForFolder(int $folderId, bool $separateStorage /** * @param 'files'|'trash'|'versions'|'' $type + * @param array $options */ public function getBaseStorageForFolder( int $folderId, @@ -81,6 +85,7 @@ public function getBaseStorageForFolder( /** * @param 'files'|'trash'|'versions'|'' $type + * @param array $options */ private function getBaseStorageForFolderSeparate( int $folderId, @@ -92,8 +97,10 @@ private function getBaseStorageForFolderSeparate( array $options = [], ): IStorage { if ($this->primaryObjectStoreConfig->hasObjectStore()) { + /** @var Storage $storage */ $storage = $this->getBaseStorageForFolderSeparateStorageObject($folderId, $init, $options['bucket'] ?? null); } else { + /** @var Storage $storage */ $storage = $this->getBaseStorageForFolderSeparateStorageLocal($folderId, $init); } @@ -226,6 +233,7 @@ private function getBaseStorageForFolderRootJail( } catch (NotFoundException) { $storageFolder = $parentFolder->newFolder((string)$folderId); } + /** @var Storage $rootStorage */ $rootStorage = $storageFolder->getStorage(); $rootPath = $storageFolder->getInternalPath(); @@ -279,6 +287,9 @@ private function getObjectStorageKey(int $folderId): string { return 'default'; } + /** + * @param array $objectStoreConfig + */ private function getObjectStorageBucket(int $folderId, array $objectStoreConfig, ?string $overwriteBucket = null): string { $bucketKey = 'object_store_bucket_' . $folderId; $bucket = $this->appConfig->getValueString(Application::APP_ID, $bucketKey); @@ -296,7 +307,10 @@ private function getObjectStorageBucket(int $folderId, array $objectStoreConfig, return $bucket; } - // logic taken from OC\Files\ObjectStore\Mapper which we can't use because it requires an IUser + /** + * logic taken from OC\Files\ObjectStore\Mapper which we can't use because it requires an IUser + * @param array $objectStoreConfig + */ private function calculateBucketNum(string $key, array $objectStoreConfig): string { $numBuckets = $objectStoreConfig['arguments']['num_buckets'] ?? 64; diff --git a/lib/Mount/GroupFolderStorage.php b/lib/Mount/GroupFolderStorage.php index 8a1d07aa1..e5d149b1a 100644 --- a/lib/Mount/GroupFolderStorage.php +++ b/lib/Mount/GroupFolderStorage.php @@ -27,6 +27,9 @@ class GroupFolderStorage extends Quota implements IConstructableStorage { private readonly IUserSession $userSession; private readonly ?IUser $mountOwner; + /** + * @param array{folder: FolderDefinition, rootCacheEntry: ?ICacheEntry, userSession: IUserSession, mountOwner: ?IUser} $parameters + */ public function __construct(array $parameters) { parent::__construct($parameters); $this->folder = $parameters['folder']; diff --git a/lib/Mount/GroupMountPoint.php b/lib/Mount/GroupMountPoint.php index 2588a6b7d..c3493be4d 100644 --- a/lib/Mount/GroupMountPoint.php +++ b/lib/Mount/GroupMountPoint.php @@ -18,6 +18,10 @@ use OCP\Files\Storage\IStorageFactory; class GroupMountPoint extends MountPoint implements ISystemMountPoint, IShareOwnerlessMount { + /** + * @param ?array $arguments + * @param ?array $mountOptions + */ public function __construct( private readonly FolderDefinition $folder, IStorage $storage, diff --git a/lib/Mount/MountProvider.php b/lib/Mount/MountProvider.php index 784e2944f..ca22d59a2 100644 --- a/lib/Mount/MountProvider.php +++ b/lib/Mount/MountProvider.php @@ -13,6 +13,7 @@ use OC\Files\Storage\Wrapper\PermissionsMask; use OCA\GroupFolders\ACL\ACLManager; use OCA\GroupFolders\ACL\ACLManagerFactory; +use OCA\GroupFolders\ACL\Rule; use OCA\GroupFolders\Folder\FolderDefinition; use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions; use OCA\GroupFolders\Folder\FolderManager; @@ -110,6 +111,9 @@ private function getCurrentUID(): ?string { return $user?->getUID(); } + /** + * @param array $rootRules + */ public function getMount( FolderDefinitionWithPermissions $folder, string $mountPoint, @@ -306,7 +310,7 @@ public function getMountsForPath(string $setupPathHint, bool $forChildren, array $loader, $user, $aclManager, - $folder->acl ? $aclManager->getRulesByFileIds([$folder->rootId]) : [], + $folder->acl ? $aclManager->getRulesByFileIds([$folder->rootId])[$folder->storageId] ?? [] : [], ); } } diff --git a/lib/Mount/RootEntryCache.php b/lib/Mount/RootEntryCache.php index 675d291f2..e78fb844f 100644 --- a/lib/Mount/RootEntryCache.php +++ b/lib/Mount/RootEntryCache.php @@ -38,12 +38,19 @@ public function getId($file): int { return parent::getId($file); } + + /** + * @param array $data + */ #[\Override] public function update($id, array $data): void { $this->rootEntry = null; parent::update($id, $data); } + /** + * @param array $data + */ #[\Override] public function insert($file, array $data): int { $this->rootEntry = null; diff --git a/lib/Mount/RootPermissionsMask.php b/lib/Mount/RootPermissionsMask.php index c767b20ef..1c8026520 100644 --- a/lib/Mount/RootPermissionsMask.php +++ b/lib/Mount/RootPermissionsMask.php @@ -8,6 +8,7 @@ namespace OCA\GroupFolders\Mount; +use OC\Files\Storage\Storage; use OC\Files\Storage\Wrapper\Wrapper; use OCA\GroupFolders\Folder\FolderDefinition; use OCP\Constants; @@ -25,7 +26,7 @@ class RootPermissionsMask extends Wrapper { private readonly FolderDefinition $folder; /** - * @param array $arguments ['storage' => $storage, 'mask' => $mask] + * @param array{storage: Storage, mask: int, folder: FolderDefinition} $arguments * * $storage: The storage the permissions mask should be applied on * $mask: The permission bits that should be kept, a combination of the \OCP\Constant::PERMISSION_ constants @@ -85,6 +86,9 @@ public function getPermissions(string $path): int { return $this->storage->getPermissions($path); } + /** + * @return ?array + */ #[\Override] public function getMetaData(string $path): ?array { $data = parent::getMetaData($path); diff --git a/lib/Service/DelegationService.php b/lib/Service/DelegationService.php index 5e7722266..a170d7b56 100644 --- a/lib/Service/DelegationService.php +++ b/lib/Service/DelegationService.php @@ -65,6 +65,9 @@ public function hasOnlyApiAccess(): bool { ]); } + /** + * @param list $settingClasses + */ private function getAccessLevel(array $settingClasses): bool { $user = $this->userSession->getUser(); if ($user === null) { diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index e7ef5fe2b..169d69990 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -12,6 +12,7 @@ use OCA\GroupFolders\Service\ApplicationService; use OCA\GroupFolders\Service\DelegationService; use OCP\App\IAppManager; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; use OCP\Settings\IDelegatedSettings; @@ -26,6 +27,9 @@ public function __construct( ) { } + /** + * @return TemplateResponse + */ #[\Override] public function getForm(): TemplateResponse { Util::addStyle(Application::APP_ID, Application::APP_ID . '-settings'); @@ -69,6 +73,9 @@ public function getName(): ?string { return null; } + /** + * @return array + */ #[\Override] public function getAuthorizedAppConfig(): array { return []; diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index 71269dfe5..b1774f522 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -195,6 +195,9 @@ public function restoreItem(ITrashItem $item): void { ); } + /** + * @return array{IStorage, string} + */ private function unwrapJails(IStorage $storage, string $internalPath): array { $unJailedInternalPath = $internalPath; $unJailedStorage = $storage; @@ -489,6 +492,9 @@ public function cleanTrashFolder(FolderDefinitionWithPermissions $folder): void $this->trashManager->emptyTrashbin($folder->id); } + /** + * @return array{int, int|float} + */ public function expire(Expiration $expiration): array { $size = 0; $count = 0; diff --git a/lib/Trash/TrashManager.php b/lib/Trash/TrashManager.php index 9934abcaf..0f4725d11 100644 --- a/lib/Trash/TrashManager.php +++ b/lib/Trash/TrashManager.php @@ -55,38 +55,6 @@ public function addTrashItem(int $folderId, string $name, int $deletedTime, stri $query->executeStatement(); } - public function getTrashItemByFileId(int $fileId): ?array { - $query = $this->connection->getQueryBuilder(); - - $query->select(['trash_id', 'name', 'deleted_time', 'original_location', 'folder_id', 'deleted_by']) - ->from('group_folders_trash') - ->where($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); - - $row = $query->executeQuery()->fetch(); - if ($row === false) { - return null; - } - - return $row; - } - - public function getTrashItemByFileName(int $folderId, string $name, int $deletedTime): ?array { - $query = $this->connection->getQueryBuilder(); - - $query->select(['trash_id', 'name', 'deleted_time', 'original_location', 'folder_id', 'deleted_by']) - ->from('group_folders_trash') - ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT))) - ->andWhere($query->expr()->eq('name', $query->createNamedParameter($name))) - ->andWhere($query->expr()->eq('deleted_time', $query->createNamedParameter($deletedTime, IQueryBuilder::PARAM_INT))); - - $row = $query->executeQuery()->fetch(); - if ($row === false) { - return null; - } - - return $row; - } - public function removeItem(int $folderId, string $name, int $deletedTime): void { $query = $this->connection->getQueryBuilder(); diff --git a/lib/Versions/GroupVersion.php b/lib/Versions/GroupVersion.php index f42e8b24e..17c7ed4fe 100644 --- a/lib/Versions/GroupVersion.php +++ b/lib/Versions/GroupVersion.php @@ -16,6 +16,9 @@ use OCP\IUser; class GroupVersion extends Version { + /** + * @param array $metadata + */ public function __construct( int $timestamp, int $revisionId, diff --git a/lib/Versions/GroupVersionEntity.php b/lib/Versions/GroupVersionEntity.php index 7cd289ec5..55fb75452 100644 --- a/lib/Versions/GroupVersionEntity.php +++ b/lib/Versions/GroupVersionEntity.php @@ -42,6 +42,9 @@ public function __construct() { $this->addType('metadata', Types::STRING); } + /** + * @return array{id: int, file_id: int, timestamp: int, size: int, mimetype: int, metadata: string} + */ #[\Override] public function jsonSerialize(): array { return [ @@ -54,10 +57,16 @@ public function jsonSerialize(): array { ]; } + /** + * @return array + */ public function getDecodedMetadata(): array { return json_decode($this->metadata ?? '', true, 512, JSON_THROW_ON_ERROR) ?? []; } + /** + * @param array $value + */ public function setDecodedMetadata(array $value): void { $this->metadata = json_encode($value, JSON_THROW_ON_ERROR); $this->markFieldUpdated('metadata'); diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index 850f7bf6f..4c19aeee3 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -381,6 +381,9 @@ public function createVersionEntity(File $file): null { return null; } + /** + * @param array{timestamp?: int, size?: int, mimetype?: int} $properties + */ public function updateVersionEntity(File $sourceFile, int $revision, array $properties): void { $versionEntity = $this->groupVersionsMapper->findVersionForFileId($sourceFile->getId(), $revision); diff --git a/phpstan.neon b/phpstan.neon index 50d9b7efc..d9ce207c4 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later parameters: - level: 5 + level: 6 phpVersion: min: 80200 max: 80500 diff --git a/tests/ACL/ACLCacheWrapperTest.php b/tests/ACL/ACLCacheWrapperTest.php index 1d97641cf..895ce0881 100644 --- a/tests/ACL/ACLCacheWrapperTest.php +++ b/tests/ACL/ACLCacheWrapperTest.php @@ -23,6 +23,7 @@ class ACLCacheWrapperTest extends TestCase { private ACLManager&MockObject $aclManager; private ICache&MockObject $source; private ACLCacheWrapper $cache; + /** @var array */ private array $aclPermissions = []; #[\Override] diff --git a/tests/ACL/ACLScannerTest.php b/tests/ACL/ACLScannerTest.php index e72ea020c..73bc715e1 100644 --- a/tests/ACL/ACLScannerTest.php +++ b/tests/ACL/ACLScannerTest.php @@ -20,6 +20,9 @@ * @group DB */ class ACLScannerTest extends TestCase { + /** + * @param array $rules + */ private function getAclManager(array $rules): ACLManager&MockObject { $manager = $this->getMockBuilder(ACLManager::class) ->disableOriginalConstructor() diff --git a/tests/ACL/ACLStorageWrapperTest.php b/tests/ACL/ACLStorageWrapperTest.php index 6b8b48cbe..1a19b40a0 100644 --- a/tests/ACL/ACLStorageWrapperTest.php +++ b/tests/ACL/ACLStorageWrapperTest.php @@ -20,6 +20,7 @@ class ACLStorageWrapperTest extends TestCase { private ACLManager&MockObject $aclManager; private IStorage $source; private ACLStorageWrapper $storage; + /** @var array */ private array $aclPermissions = []; #[\Override] diff --git a/tests/ACL/RuleTest.php b/tests/ACL/RuleTest.php index 10717959a..1c8783344 100644 --- a/tests/ACL/RuleTest.php +++ b/tests/ACL/RuleTest.php @@ -13,6 +13,9 @@ use Test\TestCase; class RuleTest extends TestCase { + /** + * @return list> + */ public static function permissionsProvider(): array { return [ [0b00000000, 0b00000000, 0b00000000, 0b00000000], @@ -31,6 +34,9 @@ public function testApplyPermissions(int $input, int $mask, int $permissions, in $this->assertEquals($expected, $rule->applyPermissions($input)); } + /** + * @return list>, int, int}> + */ public static function mergeRulesProvider(): array { return [ [[ @@ -55,6 +61,7 @@ public static function mergeRulesProvider(): array { /** * @dataProvider mergeRulesProvider + * @param list $inputs */ public function testMergeRules(array $inputs, int $expectedMask, int $expectedPermissions): void { $inputRules = array_map(fn (array $input): Rule => new Rule($this->createMock(IUserMapping::class), 0, $input[0], $input[1]), $inputs); diff --git a/tests/Listeners/LoadAdditionalScriptsListenerTest.php b/tests/Listeners/LoadAdditionalScriptsListenerTest.php index a183af27a..01992b59c 100644 --- a/tests/Listeners/LoadAdditionalScriptsListenerTest.php +++ b/tests/Listeners/LoadAdditionalScriptsListenerTest.php @@ -26,6 +26,9 @@ protected function setUp(): void { $this->listener = new LoadAdditionalScriptsListener(); } + /** + * @return list}> + */ public static function handleProvider(): array { $expectedScripts = [ 'groupfolders/l10n/en', @@ -41,6 +44,7 @@ public static function handleProvider(): array { /** * @dataProvider handleProvider * @param class-string $class + * @param list $expectedScripts */ public function testHandle(string $class, array $expectedScripts): void { $event = $this->createMock($class); From d66f67c3bf1a84b7fb9ae4df3f9d38279401347f Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Feb 2026 15:18:35 +0100 Subject: [PATCH 09/12] feat(phpstan): Level 7 Signed-off-by: provokateurin --- lib/ACL/ACLCacheWrapper.php | 2 +- lib/ACL/ACLStorageWrapper.php | 3 --- lib/ACL/RuleManager.php | 7 ++++++ lib/Command/ACL.php | 8 ++----- lib/Controller/FolderController.php | 22 ++++++++++++++----- lib/Folder/FolderManager.php | 9 ++++++-- .../Version103000Date20180806161724.php | 2 +- lib/ResponseDefinitions.php | 2 +- lib/Service/FoldersFilter.php | 8 +++---- lib/Trash/TrashBackend.php | 11 +++++++--- lib/Versions/VersionsBackend.php | 21 +++++++++++++++--- openapi.json | 3 ++- phpstan.neon | 2 +- tests/ACL/ACLScannerTest.php | 10 +++++---- tests/ACL/ACLStorageWrapperTest.php | 2 +- tests/ACL/RuleManagerTest.php | 5 ++++- 16 files changed, 79 insertions(+), 38 deletions(-) diff --git a/lib/ACL/ACLCacheWrapper.php b/lib/ACL/ACLCacheWrapper.php index 83f44a855..e22091d8e 100644 --- a/lib/ACL/ACLCacheWrapper.php +++ b/lib/ACL/ACLCacheWrapper.php @@ -63,7 +63,7 @@ protected function formatCacheEntry($entry, array $rules = []): ICacheEntry|fals } /** - * @return list + * @return array */ #[\Override] public function getFolderContentsById($fileId, ?string $mimeTypeFilter = null): array { diff --git a/lib/ACL/ACLStorageWrapper.php b/lib/ACL/ACLStorageWrapper.php index 231c9b457..03beb207e 100644 --- a/lib/ACL/ACLStorageWrapper.php +++ b/lib/ACL/ACLStorageWrapper.php @@ -251,9 +251,6 @@ public function is_file(string $path): bool { && parent::is_file($path); } - /** - * @return array|false - */ #[\Override] public function stat(string $path): array|false { if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) { diff --git a/lib/ACL/RuleManager.php b/lib/ACL/RuleManager.php index e0944ead4..279d10077 100644 --- a/lib/ACL/RuleManager.php +++ b/lib/ACL/RuleManager.php @@ -61,6 +61,7 @@ public function getRulesForFilesById(IUser $user, array $fileIds): array { $query->expr()->eq('mapping_id', $query->createNamedParameter($userMapping->getId())) ), $userMappings))); + /** @var list $rows */ $rows = $query->executeQuery()->fetchAll(); $result = []; @@ -97,6 +98,7 @@ public function getRulesForFilesByPath(IUser $user, int $storageId, array $fileP $query->expr()->eq('mapping_id', $query->createNamedParameter($userMapping->getId())) ), $userMappings))); + /** @var list $rows */ $rows = array_merge($rows, $query->executeQuery()->fetchAll()); } @@ -130,6 +132,7 @@ public function getRulesForFilesByIds(IUser $user, array $fileIds): array { $query->expr()->eq('a.mapping_id', $query->createNamedParameter($userMapping->getId())) ), $userMappings))); + /** @var list $rows */ $rows = array_merge($rows, $query->executeQuery()->fetchAll()); } @@ -164,6 +167,7 @@ public function getRulesForFilesByParent(IUser $user, int $storageId, int $paren ) ); + /** @var list $rows */ $rows = $query->executeQuery()->fetchAll(); $result = []; @@ -196,6 +200,7 @@ public function getAllRulesForPaths(int $storageId, array $filePaths): array { $rows = $query->executeQuery()->fetchAll(); + /** @var list $rows */ return $this->rulesByPath($rows); } @@ -256,6 +261,7 @@ public function getAllRulesForPrefix(int $storageId, string $prefix): array { $rows = $query->executeQuery()->fetchAll(); + /** @var list $rows */ return $this->rulesByPath($rows); } @@ -284,6 +290,7 @@ public function getRulesForPrefix(IUser $user, int $storageId, string $prefix): $rows = $query->executeQuery()->fetchAll(); + /** @var list $rows */ return $this->rulesByPath($rows); } diff --git a/lib/Command/ACL.php b/lib/Command/ACL.php index 6a6f0e92f..bc9874441 100644 --- a/lib/Command/ACL.php +++ b/lib/Command/ACL.php @@ -134,6 +134,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln(' argument has to be an array'); return -3; } + /** @var list $permissionStrings */ $mount = $this->mountProvider->getMount( FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL), @@ -157,11 +158,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int } foreach ($permissionStrings as $permission) { - if (!is_string($permission)) { - $output->writeln(' argument has to be an array of strings'); - return -3; - } - if ($permission[0] !== '+' && $permission[0] !== '-') { $output->writeln('incorrect format for permissions "' . $permission . '"'); return -3; @@ -206,7 +202,7 @@ private function printPermissions(InputInterface $input, OutputInterface $output $items = array_combine($paths, $rules); ksort($items); - $output->writeln(json_encode($items, $outputFormat === parent::OUTPUT_FORMAT_JSON_PRETTY ? JSON_PRETTY_PRINT : 0)); + $output->writeln(json_encode($items, JSON_THROW_ON_ERROR | ($outputFormat === parent::OUTPUT_FORMAT_JSON_PRETTY ? JSON_PRETTY_PRINT : 0))); break; default: $items = array_map(function (array $rulesForPath, string $path) use ($jailPathLength): array { diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index 3b4d14264..e1b05d57f 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -52,7 +52,7 @@ * acl: bool, * acl_default_no_permission: bool, * manage: list, - * sortIndex?: int, + * sortIndex?: non-negative-int, * } */ class FolderController extends OCSController { @@ -152,6 +152,7 @@ public function getFolders(bool $applicable = false, int $offset = 0, ?int $limi $folders = []; $i = 0; + /** @var string $id */ foreach ($this->manager->getAllFoldersWithSize($offset, $limit, $orderBy, $order) as $id => $folder) { // Make them string-indexed for OpenAPI JSON output // JavaScript doesn't preserve JSON object key orders, so we need to manually add this information. @@ -509,17 +510,26 @@ private function buildOCSResponseXML(string $format, DataResponse $data): V1Resp $folderData = $data->getData(); if (isset($folderData['id'])) { // single folder response - $folderData = $this->folderDataForXML($folderData); + /** @var GroupFoldersFolder $folderDataIn */ + $folderDataIn = $folderData; + $folderData = $this->folderDataForXML($folderDataIn); + $data->setData($folderData); } elseif (isset($folderData['folder'])) { // single folder response - $folderData['folder'] = $this->folderDataForXML($folderData['folder']); + /** @var array{folder: GroupFoldersFolder} $folderDataIn */ + $folderDataIn = $folderData; + $folderData['folder'] = $this->folderDataForXML($folderDataIn['folder']); + /** @var DataResponse $data */ + $data->setData($folderData); } elseif (count($folderData) && isset(current($folderData)['id'])) { // folder list - $folderData = array_map($this->folderDataForXML(...), $folderData); + /** @var list $folderDataIn */ + $folderDataIn = $folderData; + $folderData = array_map($this->folderDataForXML(...), $folderDataIn); + /** @var DataResponse, array{}> $data */ + $data->setData($folderData); } - $data->setData($folderData); - return new V1Response($data, $format); } diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php index ab908e2be..7f3d7eaea 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -85,6 +85,7 @@ public function getAllFolders(): array { $query->select('folder_id', 'mount_point', 'quota', 'acl', 'acl_default_no_permission', 'storage_id', 'root_id', 'options') ->from('group_folders', 'f'); + /** @var list $rows */ $rows = $query->executeQuery()->fetchAll(); $folderMap = []; @@ -155,6 +156,7 @@ public function getAllFoldersWithSize(int $offset = 0, ?int $limit = null, strin $query->addOrderBy('mount_point', 'ASC'); } + /** @var list $rows */ $rows = $query->executeQuery()->fetchAll(); $folderIds = array_map(static fn (array $row): int => (int)$row['folder_id'], $rows); @@ -195,6 +197,7 @@ public function getAllFoldersForUserWithSize(IUser $user): array { ->selectAlias('a.permissions', 'group_permissions') ->where($query->expr()->in('a.group_id', $query->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY))); + /** @var list $rows */ $rows = $query->executeQuery()->fetchAll(); $folderIds = array_map(static fn (array $row): int => (int)$row['folder_id'], $rows); @@ -315,6 +318,7 @@ public function getFolder(int $id): ?FolderWithMappingsAndCache { $query->where($query->expr()->eq('f.folder_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $result = $query->executeQuery(); + /** @var array{folder_id: int, mount_point: string, quota: int, acl: bool, acl_default_no_permission: bool, storage_id: int, root_id: int, options: string}|false $row */ $row = $result->fetch(); $result->closeCursor(); if ($row === false) { @@ -325,7 +329,6 @@ public function getFolder(int $id): ?FolderWithMappingsAndCache { $folderMappings = $this->getFolderMappings($id); $folder = $this->rowToFolder($row); - $id = $folder->id; return FolderWithMappingsAndCache::fromFolderWithMapping( FolderDefinitionWithMappings::fromFolder( $folder, @@ -623,7 +626,7 @@ private function getFolderOptions(array $row): array { } /** - * @param array{folder_id: int, mount_point: string, quota: int, acl: bool, acl_default_no_permission: bool, storage_id: int, root_id: int} $row + * @param array{folder_id: int, mount_point: string, quota: int, acl: bool, acl_default_no_permission: bool, storage_id: int, root_id: int, options?: string} $row */ private function rowToFolder(array $row): FolderDefinition { return new FolderDefinition( @@ -674,6 +677,7 @@ public function getFoldersForGroups(array $groupIds, ?int $folderId = null, ?str } return array_map(function (array $row): FolderDefinitionWithPermissions { + /** @var array{folder_id: int, mount_point: string, quota: int, acl: bool, acl_default_no_permission: bool, storage_id: int, root_id: int, options: string, fileid: int, storage: int, path: string, name: string, mimetype: string, mimepart: string, size: int, mtime: int, storage_mtime: int, etag: string, encrypted: bool, parent: int, permissions: int, group_permissions: int} $row */ $folder = $this->rowToFolder($row); return FolderDefinitionWithPermissions::fromFolder( $folder, @@ -722,6 +726,7 @@ public function getFoldersFromCircleMemberships(IUser $user, ?int $folderId = nu $queryHelper->limitToMemberships('a', 'circle_id', $federatedUser); return array_map(function (array $row): FolderDefinitionWithPermissions { + /** @var array{folder_id: int, mount_point: string, quota: int, acl: bool, acl_default_no_permission: bool, storage_id: int, root_id: int, options: string, fileid: int, storage: int, path: string, name: string, mimetype: string, mimepart: string, size: int, mtime: int, storage_mtime: int, etag: string, encrypted: bool, parent: int, permissions: int, group_permissions: int} $row */ $folder = $this->rowToFolder($row); return FolderDefinitionWithPermissions::fromFolder( $folder, diff --git a/lib/Migration/Version103000Date20180806161724.php b/lib/Migration/Version103000Date20180806161724.php index a60099c54..bacb4c07f 100644 --- a/lib/Migration/Version103000Date20180806161724.php +++ b/lib/Migration/Version103000Date20180806161724.php @@ -15,7 +15,7 @@ class Version103000Date20180806161724 extends SimpleMigrationStep { /** - * @var list> + * @var mixed[] */ private array $applicableData = []; diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index c59dffab3..fdbe579a6 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -56,7 +56,7 @@ * acl: bool, * acl_default_no_permission: bool, * manage: list, - * sortIndex?: int, + * sortIndex?: non-negative-int, * } */ class ResponseDefinitions { diff --git a/lib/Service/FoldersFilter.php b/lib/Service/FoldersFilter.php index dbaa036f7..6bfba5585 100644 --- a/lib/Service/FoldersFilter.php +++ b/lib/Service/FoldersFilter.php @@ -23,8 +23,8 @@ public function __construct( } /** - * @param GroupFoldersFolder[] $folders List of all folders - * @return GroupFoldersFolder[] + * @param array $folders List of all folders + * @return array */ public function getForApiUser(array $folders): array { $user = $this->userSession->getUser(); @@ -32,7 +32,7 @@ public function getForApiUser(array $folders): array { return []; } - return array_values(array_filter($folders, function (array $folder) use ($user): bool { + return array_filter($folders, function (array $folder) use ($user): bool { foreach ($folder['manage'] as $manager) { if ($manager['type'] === 'group') { if ($this->groupManager->isInGroup($user->getUid(), $manager['id'])) { @@ -44,6 +44,6 @@ public function getForApiUser(array $folders): array { } return false; - })); + }); } } diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index b1774f522..a5f3077d7 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -37,6 +37,7 @@ use OCP\IUserManager; use OCP\IUserSession; use Psr\Log\LoggerInterface; +use RuntimeException; class TrashBackend implements ITrashBackend { private ?VersionsBackend $versionsBackend = null; @@ -250,6 +251,10 @@ public function moveToTrash(IStorage $storage, string $internalPath): bool { /** @var GroupFolderStorage $storage */ $name = basename($internalPath); $fileEntry = $storage->getCache()->get($internalPath); + if ($fileEntry === false) { + throw new RuntimeException('Failed to get cache entry.'); + } + $folder = $storage->getFolder(); $folderId = $storage->getFolderId(); @@ -397,7 +402,7 @@ private function getTrashForFolders(IUser $user, array $folders): array { $itemsForFolder = array_map(function (Node $item) use ($user, $folder, $indexedRows): \OCA\GroupFolders\Trash\GroupTrashItem { $pathParts = pathinfo($item->getName()); - $timestamp = (int)substr($pathParts['extension'], 1); + $timestamp = (int)substr($pathParts['extension'] ?? '', 1); $name = $pathParts['filename']; $key = $folder->id . '/' . $name . '/' . $timestamp; @@ -442,10 +447,10 @@ private function getTrashForFolders(IUser $user, array $folders): array { return true; }); } - $items = array_merge($items, $itemsForFolder); + $items[] = $itemsForFolder; } - return $items; + return array_values(array_merge(...$items)); } /** diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index 4c19aeee3..3216f5e37 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -40,6 +40,7 @@ use OCP\IUser; use OCP\IUserSession; use Psr\Log\LoggerInterface; +use RuntimeException; class VersionsBackend implements IVersionBackend, IMetadataVersionBackend, IDeletableVersionBackend, INeedSyncVersionBackend, IVersionsImporterBackend { public function __construct( @@ -223,8 +224,13 @@ public function createVersion(IUser $user, FileInfo $file): void { $versionInternalPath = $versionsFolder->getInternalPath() . '/' . $revision; $sourceInternalPath = $file->getInternalPath(); + $sourceCacheEntry = $sourceCache->get($sourceInternalPath); + if ($sourceCacheEntry === false) { + throw new RuntimeException('Failed to get source cache entry'); + } + $versionMount->getStorage()->copyFromStorage($sourceMount->getStorage(), $sourceInternalPath, $versionInternalPath); - $versionMount->getStorage()->getCache()->copyFromCache($sourceCache, $sourceCache->get($sourceInternalPath), $versionInternalPath); + $versionMount->getStorage()->getCache()->copyFromCache($sourceCache, $sourceCacheEntry, $versionInternalPath); } public function rollback(IVersion $version): void { @@ -247,8 +253,13 @@ public function rollback(IVersion $version): void { $targetInternalPath = $version->getSourceFile()->getInternalPath(); $versionInternalPath = $version->getVersionFile()->getInternalPath(); + $versionCacheEntry = $versionCache->get($versionInternalPath); + if ($versionCacheEntry === false) { + throw new RuntimeException('Failed to get version cache entry'); + } + $targetMount->getStorage()->copyFromStorage($versionMount->getStorage(), $versionInternalPath, $targetInternalPath); - $targetCache->copyFromCache($versionCache, $versionCache->get($versionInternalPath), $targetInternalPath); + $targetCache->copyFromCache($versionCache, $versionCacheEntry, $targetInternalPath); } public function read(IVersion $version) { @@ -428,7 +439,11 @@ public function importVersionsForFile(IUser $user, Node $source, Node $target, a if ($version->getTimestamp() !== $source->getMTime()) { $backend = $version->getBackend(); $versionFile = $backend->getVersionFile($user, $source, $version->getRevisionId()); - $versionsFolder->newFile($version->getRevisionId(), $versionFile->fopen('r')); + $versionFileHandle = $versionFile->fopen('r'); + if ($versionFileHandle === false) { + throw new RuntimeException('Failed to open version file.'); + } + $versionsFolder->newFile((string)$version->getRevisionId(), $versionFileHandle); } // 2. Create the entity in the database diff --git a/openapi.json b/openapi.json index c449b5c16..0a1ba5c9f 100644 --- a/openapi.json +++ b/openapi.json @@ -189,7 +189,8 @@ }, "sortIndex": { "type": "integer", - "format": "int64" + "format": "int64", + "minimum": 0 } } }, diff --git a/phpstan.neon b/phpstan.neon index d9ce207c4..1f390ed76 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later parameters: - level: 6 + level: 7 phpVersion: min: 80200 max: 80500 diff --git a/tests/ACL/ACLScannerTest.php b/tests/ACL/ACLScannerTest.php index 73bc715e1..8984d420c 100644 --- a/tests/ACL/ACLScannerTest.php +++ b/tests/ACL/ACLScannerTest.php @@ -46,7 +46,9 @@ public function testScanAclStorage(): void { $cache->calculateFolderSize('foo/bar'); $cache->calculateFolderSize('foo'); - $this->assertEquals(-1, $cache->get('foo/bar')->getSize()); + $cacheEntry = $cache->get('foo/bar'); + $this->assertNotFalse($cacheEntry); + $this->assertEquals(-1, $cacheEntry->getSize()); $acls = $this->getAclManager([ 'foo/bar' => 0, @@ -65,9 +67,9 @@ public function testScanAclStorage(): void { $aclCache = $aclStorage->getCache(); $scanner->scan(''); - $this->assertEquals(0, $cache->get('foo/bar')->getSize()); - - $this->assertEquals(31, $cache->get('foo/bar')->getPermissions()); + $cacheEntry = $cache->get('foo/bar'); + $this->assertNotFalse($cacheEntry); + $this->assertEquals(31, $cacheEntry->getPermissions()); $this->assertEquals(false, $aclCache->get('foo/bar')); } } diff --git a/tests/ACL/ACLStorageWrapperTest.php b/tests/ACL/ACLStorageWrapperTest.php index 1a19b40a0..1a719ce8b 100644 --- a/tests/ACL/ACLStorageWrapperTest.php +++ b/tests/ACL/ACLStorageWrapperTest.php @@ -63,7 +63,7 @@ public function testOpenDir(): void { $dh = $this->storage->opendir('foo'); $result = []; - while ($file = readdir($dh)) { + while ($file = readdir($dh ?: null)) { $result[] = $file; } diff --git a/tests/ACL/RuleManagerTest.php b/tests/ACL/RuleManagerTest.php index 4b9dc835b..4018c1688 100644 --- a/tests/ACL/RuleManagerTest.php +++ b/tests/ACL/RuleManagerTest.php @@ -41,7 +41,10 @@ protected function setUp(): void { $this->userMappingManager = $this->createMock(IUserMappingManager::class); $this->userMappingManager->expects($this->any()) ->method('mappingFromId') - ->willReturnCallback(fn (string $type, string $id): UserMapping => /** @var 'user'|'group'|'dummy' $type */ new UserMapping($type, $id)); + ->willReturnCallback(function (string $type, string $id): UserMapping { + /** @var 'user'|'group'|'dummy' $type */ + return new UserMapping($type, $id); + }); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->ruleManager = new RuleManager(Server::get(IDBConnection::class), $this->userMappingManager, $this->eventDispatcher); From 2a4baf9ab3e435148d965813660138187d7fd4da Mon Sep 17 00:00:00 2001 From: provokateurin Date: Mon, 9 Feb 2026 15:21:02 +0100 Subject: [PATCH 10/12] feat(phpstan): Level 8 Signed-off-by: provokateurin --- lib/ACL/ACLStorageWrapper.php | 2 +- lib/Command/ACL.php | 15 +++++- lib/Command/Scan.php | 7 ++- lib/Controller/DelegationController.php | 5 +- lib/DAV/ACLPlugin.php | 19 ++++++- lib/Mount/RootPermissionsMask.php | 4 +- lib/Trash/TrashBackend.php | 16 ++++-- lib/Versions/GroupVersionEntity.php | 2 +- lib/Versions/VersionsBackend.php | 72 +++++++++++++++++++------ phpstan.neon | 2 +- 10 files changed, 114 insertions(+), 30 deletions(-) diff --git a/lib/ACL/ACLStorageWrapper.php b/lib/ACL/ACLStorageWrapper.php index 03beb207e..3f840f2bf 100644 --- a/lib/ACL/ACLStorageWrapper.php +++ b/lib/ACL/ACLStorageWrapper.php @@ -80,7 +80,7 @@ public function isSharable(string $path): bool { #[\Override] public function getPermissions(string $path): int { - return $this->storage->getPermissions($path) & $this->getACLPermissionsForPath($path); + return $this->getWrapperStorage()->getPermissions($path) & $this->getACLPermissionsForPath($path); } #[\Override] diff --git a/lib/Command/ACL.php b/lib/Command/ACL.php index bc9874441..194e3ce6c 100644 --- a/lib/Command/ACL.php +++ b/lib/Command/ACL.php @@ -20,6 +20,7 @@ use OCP\Constants; use OCP\Files\IRootFolder; use OCP\IUserManager; +use RuntimeException; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputArgument; @@ -140,7 +141,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL), '/dummy/files/' . $folder->mountPoint, ); - $id = $mount->getStorage()->getCache()->getId($path); + + $storage = $mount->getStorage(); + if ($storage === null) { + throw new RuntimeException('Failed to get storage for mount.'); + } + + $id = $storage->getCache()->getId($path); if ($id === -1) { $output->writeln('Path not found in folder: ' . $path . ''); return -1; @@ -185,8 +192,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function printPermissions(InputInterface $input, OutputInterface $output, FolderWithMappingsAndCache $folder): void { $rootPath = $folder->rootCacheEntry->getPath(); + $numericStorageId = $this->rootFolder->getMountPoint()->getNumericStorageId(); + if ($numericStorageId === null) { + throw new RuntimeException('Failed to get numeric storage id for mount.'); + } $rules = $this->ruleManager->getAllRulesForPrefix( - $this->rootFolder->getMountPoint()->getNumericStorageId(), + $numericStorageId, $rootPath ); $jailPathLength = strlen($rootPath) + 1; diff --git a/lib/Command/Scan.php b/lib/Command/Scan.php index b88a9cbde..3ed87c2f1 100644 --- a/lib/Command/Scan.php +++ b/lib/Command/Scan.php @@ -17,6 +17,7 @@ use OCP\Constants; use OCP\Files\IRootFolder; use OCP\Files\Storage\IStorageFactory; +use RuntimeException; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -117,8 +118,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int } foreach ($mounts as $type => $mount) { $statsRow = ["$folderId - $type", 0, 0, 0, 0]; + $storage = $mount->getStorage(); + if ($storage === null) { + throw new RuntimeException('Failed to get storage for mount.'); + } /** @var Scanner&\OC\Hooks\BasicEmitter $scanner */ - $scanner = $mount->getStorage()->getScanner(); + $scanner = $storage->getScanner(); $output->writeln("Scanning Team folder with id\t$folderId - $type", OutputInterface::VERBOSITY_VERBOSE); if ($scanner instanceof ObjectStoreScanner) { diff --git a/lib/Controller/DelegationController.php b/lib/Controller/DelegationController.php index 12f38c93c..8279f3774 100644 --- a/lib/Controller/DelegationController.php +++ b/lib/Controller/DelegationController.php @@ -22,6 +22,7 @@ use OCP\IConfig; use OCP\IGroupManager; use OCP\IRequest; +use OCP\IUser; use OCP\IUserSession; use OCP\Server; use Psr\Container\ContainerExceptionInterface; @@ -96,7 +97,9 @@ public function getAllCircles(): DataResponse { // As admin, get all circles, // As non-admin, only returns circles current user is members of. - if ($this->groupManager->isAdmin($this->userSession->getUser()->getUID())) { + /** @var IUser $user Not a public route, so a user must be present. */ + $user = $this->userSession->getUser(); + if ($this->groupManager->isAdmin($user->getUID())) { $circlesManager->startSuperSession(); } else { $circlesManager->startSession(); diff --git a/lib/DAV/ACLPlugin.php b/lib/DAV/ACLPlugin.php index da5f1f6d4..bd3300459 100644 --- a/lib/DAV/ACLPlugin.php +++ b/lib/DAV/ACLPlugin.php @@ -21,6 +21,7 @@ use OCP\IUser; use OCP\IUserSession; use OCP\Log\Audit\CriticalActionPerformedEvent; +use RuntimeException; use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\INode; use Sabre\DAV\PropFind; @@ -158,9 +159,14 @@ public function propFind(PropFind $propFind, INode $node): void { } } + $fileInfoId = $fileInfo->getId(); + if ($fileInfoId === null) { + throw new RuntimeException('Failed to get id of fileinfo.'); + } + return array_map(fn (IUserMapping $mapping, int $permissions, int $mask): Rule => new Rule( $mapping, - $fileInfo->getId(), + $fileInfoId, $mask, $permissions ), $mappings, $inheritedPermissionsByMapping, $inheritedMaskByMapping); @@ -216,6 +222,10 @@ public function propPatch(string $path, PropPatch $propPatch): void { // Mapping the old property to the new property. $propPatch->handle(self::ACL_LIST, function (array $rawRules) use ($path): bool { + if ($this->server === null) { + return false; + } + $node = $this->server->tree->getNodeForPath($path); if (!$node instanceof Node) { return false; @@ -233,10 +243,15 @@ public function propPatch(string $path, PropPatch $propPatch): void { $path = trim($mount->getSourcePath() . '/' . $fileInfo->getInternalPath(), '/'); + $fileInfoId = $fileInfo->getId(); + if ($fileInfoId === null) { + throw new RuntimeException('Failed to get id of fileinfo.'); + } + // populate fileid in rules $rules = array_values(array_map(fn (Rule $rule): Rule => new Rule( $rule->getUserMapping(), - $fileInfo->getId(), + $fileInfoId, $rule->getMask(), $rule->getPermissions() ), $rawRules)); diff --git a/lib/Mount/RootPermissionsMask.php b/lib/Mount/RootPermissionsMask.php index 1c8026520..c9acd187b 100644 --- a/lib/Mount/RootPermissionsMask.php +++ b/lib/Mount/RootPermissionsMask.php @@ -80,10 +80,10 @@ public function isSharable(string $path): bool { #[\Override] public function getPermissions(string $path): int { if ($path === '') { - return $this->storage->getPermissions($path) & $this->mask; + return $this->getWrapperStorage()->getPermissions($path) & $this->mask; } - return $this->storage->getPermissions($path); + return $this->getWrapperStorage()->getPermissions($path); } /** diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index a5f3077d7..94d77782e 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -84,7 +84,11 @@ public function listTrashFolder(ITrashItem $folder): array { } $content = $folderNode->getDirectoryListing(); - $this->aclManagerFactory->getACLManager($user)->preloadRulesForFolder($folder->getGroupFolderStorageId(), $folder->getId()); + $folderId = $folder->getId(); + if ($folderId === null) { + throw new RuntimeException('Failed to get id of folder.'); + } + $this->aclManagerFactory->getACLManager($user)->preloadRulesForFolder($folder->getGroupFolderStorageId(), $folderId); return array_values(array_filter(array_map(function (Node $node) use ($folder, $user): ?GroupTrashItem { $item = new GroupTrashItem( @@ -347,6 +351,10 @@ private function setupTrashFolder(FolderDefinition $folder, ?IUser $user = null) $mountPoint = '/' . $uid . '/files_trashbin/groupfolders/' . $folderId; $mount = $this->mountManager->find($mountPoint); + if ($mount === null) { + throw new \RuntimeException('Failed to get mount for mountpoint.'); + } + if ($mount->getMountPoint() !== $mountPoint) { $trashMount = $this->mountProvider->getTrashMount( $folder, @@ -359,7 +367,7 @@ private function setupTrashFolder(FolderDefinition $folder, ?IUser $user = null) $folder = $this->rootFolder->get('/' . $uid . '/files_trashbin/groupfolders/' . $folderId); if (!$folder instanceof Folder) { - throw new \RuntimeException('Trash folder was not a folder.'); + throw new RuntimeException('Trash folder was not a folder.'); } return $folder; @@ -406,8 +414,8 @@ private function getTrashForFolders(IUser $user, array $folders): array { $name = $pathParts['filename']; $key = $folder->id . '/' . $name . '/' . $timestamp; - $originalLocation = isset($indexedRows[$key]) ? $indexedRows[$key]['original_location'] : ''; - $deletedBy = isset($indexedRows[$key]) ? $indexedRows[$key]['deleted_by'] : ''; + $originalLocation = $indexedRows[$key]['original_location'] ?: ''; + $deletedBy = $indexedRows[$key]['deleted_by'] ?: ''; return new GroupTrashItem( $this, diff --git a/lib/Versions/GroupVersionEntity.php b/lib/Versions/GroupVersionEntity.php index 55fb75452..60840e94c 100644 --- a/lib/Versions/GroupVersionEntity.php +++ b/lib/Versions/GroupVersionEntity.php @@ -43,7 +43,7 @@ public function __construct() { } /** - * @return array{id: int, file_id: int, timestamp: int, size: int, mimetype: int, metadata: string} + * @return array{id: ?int, file_id: ?int, timestamp: ?int, size: ?int, mimetype: ?int, metadata: ?string} */ #[\Override] public function jsonSerialize(): array { diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index 3216f5e37..f13a59c6e 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -106,7 +106,11 @@ public function getVersionsForFile(IUser $user, FileInfo $file): array { // Insert the entry in the DB for the current version. $versionEntity = new GroupVersionEntity(); - $versionEntity->setFileId($file->getId()); + $fileId = $file->getId(); + if ($fileId === null) { + throw new RuntimeException('Failed to get id of file.'); + } + $versionEntity->setFileId($fileId); $versionEntity->setTimestamp($file->getMTime()); $versionEntity->setSize($file->getSize()); $versionEntity->setMimetype($this->mimeTypeLoader->getId($file->getMimetype())); @@ -121,7 +125,11 @@ public function getVersionsForFile(IUser $user, FileInfo $file): array { } $versionEntity = new GroupVersionEntity(); - $versionEntity->setFileId($file->getId()); + $fileId = $file->getId(); + if ($fileId === null) { + throw new RuntimeException('Failed to get id of file.'); + } + $versionEntity->setFileId($fileId); // HACK: before this commit, versions were created with the current timestamp instead of the version's mtime. // This means that the name of some versions is the exact mtime of the next version. This behavior is now fixed. // To prevent occasional conflicts between the last version and the current one, we decrement the last version mtime. @@ -157,7 +165,12 @@ private function getVersionsForFileFromDB(FileInfo $fileInfo, IUser $user): arra } $versionsFolder = $this->getVersionFolderForFile($fileInfo); - $versionEntities = $this->groupVersionsMapper->findAllVersionsForFileId($fileInfo->getId()); + $fileInfoId = $fileInfo->getId(); + if ($fileInfoId === null) { + throw new RuntimeException('Failed to get id of fileinfo.'); + } + + $versionEntities = $this->groupVersionsMapper->findAllVersionsForFileId($fileInfoId); $mappedVersions = array_map( function (GroupVersionEntity $versionEntity) use ($versionsFolder, $mountPoint, $fileInfo, $user, $folder): ?GroupVersion { $currentVersion = false; @@ -195,7 +208,7 @@ function (GroupVersionEntity $versionEntity) use ($versionsFolder, $mountPoint, $versionEntity->getTimestamp(), $fileInfo->getName(), $versionEntity->getSize(), - $this->mimeTypeLoader->getMimetypeById($versionEntity->getMimetype()), + $this->mimeTypeLoader->getMimetypeById($versionEntity->getMimetype()) ?? '', $mountPoint->getInternalPath($fileInfo->getPath()), $fileInfo, $this, @@ -218,7 +231,11 @@ public function createVersion(IUser $user, FileInfo $file): void { $versionMount = $versionsFolder->getMountPoint(); $sourceMount = $file->getMountPoint(); - $sourceCache = $sourceMount->getStorage()->getCache(); + $sourceStorage = $sourceMount->getStorage(); + if ($sourceStorage === null) { + throw new \RuntimeException('Failed to get storage for source mount.'); + } + $sourceCache = $sourceStorage->getCache(); $revision = $file->getMtime(); $versionInternalPath = $versionsFolder->getInternalPath() . '/' . $revision; @@ -229,8 +246,12 @@ public function createVersion(IUser $user, FileInfo $file): void { throw new RuntimeException('Failed to get source cache entry'); } - $versionMount->getStorage()->copyFromStorage($sourceMount->getStorage(), $sourceInternalPath, $versionInternalPath); - $versionMount->getStorage()->getCache()->copyFromCache($sourceCache, $sourceCacheEntry, $versionInternalPath); + $versionStorage = $versionMount->getStorage(); + if ($versionStorage === null) { + throw new \RuntimeException('Failed to get storage for version mount.'); + } + $versionStorage->copyFromStorage($sourceStorage, $sourceInternalPath, $versionInternalPath); + $versionStorage->getCache()->copyFromCache($sourceCache, $sourceCacheEntry, $versionInternalPath); } public function rollback(IVersion $version): void { @@ -246,9 +267,17 @@ public function rollback(IVersion $version): void { /** @var GroupMountPoint $targetMount */ $targetMount = $version->getSourceFile()->getMountPoint(); - $targetCache = $targetMount->getStorage()->getCache(); + $targetStorage = $targetMount->getStorage(); + if ($targetStorage === null) { + throw new \RuntimeException('Failed to get storage for target mount.'); + } + $targetCache = $targetStorage->getCache(); $versionMount = $version->getVersionFile()->getMountPoint(); - $versionCache = $versionMount->getStorage()->getCache(); + $versionStorage = $versionMount->getStorage(); + if ($versionStorage === null) { + throw new \RuntimeException('Failed to get storage for version mount.'); + } + $versionCache = $versionStorage->getCache(); $targetInternalPath = $version->getSourceFile()->getInternalPath(); $versionInternalPath = $version->getVersionFile()->getInternalPath(); @@ -258,7 +287,7 @@ public function rollback(IVersion $version): void { throw new RuntimeException('Failed to get version cache entry'); } - $targetMount->getStorage()->copyFromStorage($versionMount->getStorage(), $versionInternalPath, $targetInternalPath); + $targetStorage->copyFromStorage($versionStorage, $versionInternalPath, $targetInternalPath); $targetCache->copyFromCache($versionCache, $versionCacheEntry, $targetInternalPath); } @@ -296,10 +325,14 @@ public function getAllVersionedFiles(FolderDefinitionWithMappings $folder): arra $fileIds = array_map(fn (Node $node): int => (int)$node->getName(), $contents); $files = array_map(function (int $fileId) use ($mount): ?FileInfo { - $cacheEntry = $mount->getStorage()->getCache()->get($fileId); + /** @var ?Storage $storage */ + $storage = $mount->getStorage(); + if ($storage === null) { + throw new \RuntimeException('Failed to get storage for mount.'); + } + + $cacheEntry = $storage->getCache()->get($fileId); if ($cacheEntry) { - /** @var Storage $storage */ - $storage = $mount->getStorage(); return new \OC\Files\FileInfo($mount->getMountPoint() . '/' . $cacheEntry->getPath(), $storage, $cacheEntry->getPath(), $cacheEntry, $mount); } else { return null; @@ -321,6 +354,10 @@ public function deleteAllVersionsForFile(FolderDefinition $folder, int $fileId): public function getVersionsFolder(FolderDefinition $folder): Folder { $mountPoint = '/dummy/files_versions/groupfolders/' . $folder->id; $mount = $this->mountManager->find($mountPoint); + if ($mount === null) { + throw new \RuntimeException('Failed to get mount for mountpoint.'); + } + // check that $mount is the version mount, and not a mount for a parent folder if ($mount->getMountPoint() !== $mountPoint) { $versionMount = $this->mountProvider->getVersionsMount( @@ -333,7 +370,7 @@ public function getVersionsFolder(FolderDefinition $folder): Folder { $folder = $this->rootFolder->get($mountPoint); if (!$folder instanceof Folder) { - throw new \RuntimeException('Versions folder was not a folder.'); + throw new RuntimeException('Versions folder was not a folder.'); } return $folder; @@ -366,8 +403,13 @@ public function deleteVersion(IVersion $version): void { /** @var Folder $versionsFolder */ $versionsFolder->get((string)$version->getRevisionId())->delete(); + $versionFileId = $version->getSourceFile()->getId(); + if ($versionFileId === null) { + throw new RuntimeException('Failed to get id of file.'); + } + $versionEntity = $this->groupVersionsMapper->findVersionForFileId( - $version->getSourceFile()->getId(), + $versionFileId, $version->getTimestamp(), ); $this->groupVersionsMapper->delete($versionEntity); diff --git a/phpstan.neon b/phpstan.neon index 1f390ed76..cea0b85eb 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later parameters: - level: 7 + level: 8 phpVersion: min: 80200 max: 80500 From 10d78cd9998bdcbc34486b6e56269327ef2fec0c Mon Sep 17 00:00:00 2001 From: provokateurin Date: Wed, 11 Feb 2026 09:29:35 +0100 Subject: [PATCH 11/12] feat(phpstan): Level 9 Signed-off-by: provokateurin --- lib/ACL/ACLCacheWrapper.php | 2 +- lib/ACL/ACLStorageWrapper.php | 10 ++++- lib/AppInfo/Application.php | 3 +- lib/Command/ACL.php | 22 ++++++++--- lib/Command/Create.php | 4 +- lib/Command/FolderCommand.php | 8 ++-- lib/Command/Group.php | 6 ++- lib/Command/ListCommand.php | 1 + lib/Command/Rename.php | 7 +++- lib/Command/Trashbin/Cleanup.php | 6 ++- lib/DAV/GroupFoldersHome.php | 1 + lib/Folder/FolderManager.php | 21 ++++++---- .../Version103000Date20180806161724.php | 6 ++- .../Version20001Date20250612140256.php | 4 +- lib/Mount/CacheRootPermissionsMask.php | 1 + lib/Mount/FolderStorageManager.php | 39 +++++++++++++++---- lib/Mount/MountProvider.php | 10 +++-- lib/Mount/RootPermissionsMask.php | 5 ++- lib/Trash/TrashManager.php | 5 ++- lib/Versions/GroupVersionEntity.php | 1 + phpstan.neon | 3 +- 21 files changed, 121 insertions(+), 44 deletions(-) diff --git a/lib/ACL/ACLCacheWrapper.php b/lib/ACL/ACLCacheWrapper.php index e22091d8e..1a8aea0f2 100644 --- a/lib/ACL/ACLCacheWrapper.php +++ b/lib/ACL/ACLCacheWrapper.php @@ -51,7 +51,7 @@ private function getACLPermissionsForPath(string $path, array $rules = []): int */ #[\Override] protected function formatCacheEntry($entry, array $rules = []): ICacheEntry|false { - if (isset($entry['permissions'])) { + if (isset($entry['permissions']) && is_int($entry['permissions']) && is_string($entry['path'])) { $entry['scan_permissions'] ??= $entry['permissions']; $entry['permissions'] &= $this->getACLPermissionsForPath($entry['path'], $rules); if (!$entry['permissions']) { diff --git a/lib/ACL/ACLStorageWrapper.php b/lib/ACL/ACLStorageWrapper.php index 3f840f2bf..274bdc054 100644 --- a/lib/ACL/ACLStorageWrapper.php +++ b/lib/ACL/ACLStorageWrapper.php @@ -219,7 +219,7 @@ public function getCache(string $path = '', ?IStorage $storage = null): ICache { public function getMetaData(string $path): ?array { $data = parent::getMetaData($path); - if (is_array($data) && isset($data['permissions'])) { + if (is_array($data) && isset($data['permissions']) && is_int($data['permissions'])) { $data['scan_permissions'] ??= $data['permissions']; $data['permissions'] &= $this->getACLPermissionsForPath($path); } @@ -345,6 +345,14 @@ public function getDirectDownload(string $path): array|false { public function getDirectoryContent(string $directory): \Traversable { $content = $this->getWrapperStorage()->getDirectoryContent($directory); foreach ($content as $data) { + if (!isset($data['permissions']) || !is_int($data['permissions'])) { + throw new \RuntimeException('permissions is not an integer.'); + } + + if (!isset($data['name']) || !is_string($data['name'])) { + throw new \RuntimeException('name is not a string.'); + } + $data['scan_permissions'] ??= $data['permissions']; $data['permissions'] &= $this->getACLPermissionsForPath(rtrim($directory, '/') . '/' . $data['name']); diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index fb89eb42f..2c5d70e80 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -51,6 +51,7 @@ use OCP\Files\Storage\IStorageFactory; use OCP\Group\Events\GroupDeletedEvent; use OCP\IAppConfig; +use OCP\IContainer; use OCP\IDBConnection; use OCP\IRequest; use OCP\IUserManager; @@ -174,7 +175,7 @@ public function register(IRegistrationContext $context): void { return new ExpireGroupPlaceholder($c->get(ITimeFactory::class)); }); - $context->registerService(\OCA\GroupFolders\BackgroundJob\ExpireGroupTrash::class, function (ContainerInterface $c): TimedJob { + $context->registerService(\OCA\GroupFolders\BackgroundJob\ExpireGroupTrash::class, function (IContainer $c): TimedJob { if (interface_exists(\OCA\Files_Trashbin\Trash\ITrashBackend::class)) { return new ExpireGroupTrashJob( $c->get(TrashBackend::class), diff --git a/lib/Command/ACL.php b/lib/Command/ACL.php index 194e3ce6c..f7ced3219 100644 --- a/lib/Command/ACL.php +++ b/lib/Command/ACL.php @@ -71,6 +71,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->folderManager->setFolderACL($folder->id, false); } elseif ($input->getOption('test')) { if ($input->getOption('user') && ($input->getArgument('path'))) { + /** @var string $mappingId */ $mappingId = $input->getOption('user'); $user = $this->userManager->get($mappingId); if (!$user) { @@ -78,6 +79,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return -1; } + /** @var string $path */ $path = $input->getArgument('path'); $aclManager = $this->aclManagerFactory->getACLManager($user); if ($this->folderManager->getFolderPermissionsForUser($user, $folder->id) === 0) { @@ -247,14 +249,22 @@ private function printPermissions(InputInterface $input, OutputInterface $output * @return array{'user'|'group'|'circle', string} */ private function convertMappingOptions(InputInterface $input): array { - if ($input->getOption('user')) { - return ['user', $input->getOption('user')]; + /** @var ?string $user */ + $user = $input->getOption('user'); + if ($user !== null) { + return ['user', $user]; } - if ($input->getOption('group')) { - return ['group', $input->getOption('group')]; + + /** @var ?string $group */ + $group = $input->getOption('group'); + if ($group !== null) { + return ['group', $group]; } - if ($input->getOption('team')) { - return ['circle', $input->getOption('team')]; + + /** @var ?string $team */ + $team = $input->getOption('team'); + if ($team !== null) { + return ['circle', $team]; } throw new InvalidArgumentException('invalid mapping options'); diff --git a/lib/Command/Create.php b/lib/Command/Create.php index 9ac57105d..a8c56c029 100644 --- a/lib/Command/Create.php +++ b/lib/Command/Create.php @@ -35,7 +35,9 @@ protected function configure(): void { #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { - $name = $this->folderManager->trimMountpoint((string)$input->getArgument('name')); + /** @var string $name */ + $name = $input->getArgument('name'); + $name = $this->folderManager->trimMountpoint($name); // Check if the folder name is valid if (empty($name)) { diff --git a/lib/Command/FolderCommand.php b/lib/Command/FolderCommand.php index b2adff85d..f3058063f 100644 --- a/lib/Command/FolderCommand.php +++ b/lib/Command/FolderCommand.php @@ -32,10 +32,12 @@ public function __construct( } protected function getFolder(InputInterface $input, OutputInterface $output): ?FolderWithMappingsAndCache { - $folderId = (int)$input->getArgument('folder_id'); - if ((string)$folderId !== $input->getArgument('folder_id')) { + /** @var string $folderIdString */ + $folderIdString = $input->getArgument('folder_id'); + $folderId = (int)$folderIdString; + if ((string)$folderId !== $folderIdString) { // Protect against removing folderId === 0 when typing a string (e.g. folder name instead of folder id) - $output->writeln('Folder id argument is not an integer. Got ' . $input->getArgument('folder_id') . ''); + $output->writeln('Folder id argument is not an integer. Got ' . $folderIdString . ''); return null; } diff --git a/lib/Command/Group.php b/lib/Command/Group.php index 1b976072e..8eb3ff847 100644 --- a/lib/Command/Group.php +++ b/lib/Command/Group.php @@ -55,15 +55,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int return -1; } + /** @var string $groupString */ $groupString = $input->getArgument('group'); $group = $this->groupManager->get($groupString); if ($input->getOption('delete')) { $this->folderManager->removeApplicableGroup($folder->id, $groupString); return 0; } elseif ($group || $this->folderManager->isACircle($groupString)) { - $permissionsString = $input->getArgument('permissions'); + /** @var list $permissionsString */ + $permissionsString = (array)$input->getArgument('permissions'); $permissions = $this->getNewPermissions($permissionsString); - if ($permissions) { + if ($permissions > 0) { if (!isset($folder->groups[$groupString])) { $this->folderManager->addApplicableGroup($folder->id, $groupString); } diff --git a/lib/Command/ListCommand.php b/lib/Command/ListCommand.php index 3e9574264..5102ff916 100644 --- a/lib/Command/ListCommand.php +++ b/lib/Command/ListCommand.php @@ -49,6 +49,7 @@ protected function configure(): void { #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { + /** @var ?string $userId */ $userId = $input->getOption('user'); $groups = $this->groupManager->search(''); $groupNames = []; diff --git a/lib/Command/Rename.php b/lib/Command/Rename.php index 00efabab1..0ec5d97a7 100644 --- a/lib/Command/Rename.php +++ b/lib/Command/Rename.php @@ -28,8 +28,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int return -1; } + /** @var string $name */ + $name = $input->getArgument('name'); + // Check if the new name is valid - $name = $this->folderManager->trimMountpoint((string)$input->getArgument('name')); + $name = $this->folderManager->trimMountpoint($name); if (empty($name)) { $output->writeln('Folder name cannot be empty'); return 1; @@ -46,7 +49,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 1; } - $this->folderManager->renameFolder($folder->id, $input->getArgument('name')); + $this->folderManager->renameFolder($folder->id, $name); return 0; } diff --git a/lib/Command/Trashbin/Cleanup.php b/lib/Command/Trashbin/Cleanup.php index 6d7625312..db3abd446 100644 --- a/lib/Command/Trashbin/Cleanup.php +++ b/lib/Command/Trashbin/Cleanup.php @@ -57,8 +57,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int $folders = $this->folderManager->getAllFoldersWithSize(); $folders = array_map(fn (FolderWithMappingsAndCache $folder): FolderDefinitionWithPermissions => FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL), $folders); - if ($input->getArgument('folder_id') !== null) { - $folderId = (int)$input->getArgument('folder_id'); + /** @var ?string $folderId */ + $folderId = $input->getArgument('folder_id'); + if ($folderId !== null) { + $folderId = (int)$folderId; foreach ($folders as $folder) { if ($folder->id === $folderId) { diff --git a/lib/DAV/GroupFoldersHome.php b/lib/DAV/GroupFoldersHome.php index ed7d85da0..ac699f627 100644 --- a/lib/DAV/GroupFoldersHome.php +++ b/lib/DAV/GroupFoldersHome.php @@ -37,6 +37,7 @@ public function delete(): never { #[\Override] public function getName(): string { + /** @var string $name */ [, $name] = \Sabre\Uri\split($this->principalInfo['uri']); return $name; } diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php index 7f3d7eaea..0abde4c52 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -350,10 +350,11 @@ public function getFolderAclEnabled(int $id): bool { ->from('group_folders', 'f') ->where($query->expr()->eq('folder_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $result = $query->executeQuery(); + /** @var array{acl: int} $row */ $row = $result->fetch(); $result->closeCursor(); - return (bool)($row['acl'] ?? false); + return $row['acl'] === 1; } public function getFolderByPath(string $path): int { @@ -386,6 +387,7 @@ private function getAllApplicable(?array $folderIds = null): array { $queryHelper?->addCircleDetails('g', 'circle_id'); + /** @var list $rows */ $rows = $query->executeQuery()->fetchAll(); $applicableMap = []; @@ -414,7 +416,7 @@ private function getAllApplicable(?array $folderIds = null): array { } $entry = [ - 'displayName' => $circle?->getDisplayName() ?? $row['circle_id'], + 'displayName' => $circle?->getDisplayName() ?? $entityId, 'permissions' => (int)$row['permissions'], 'type' => 'circle', ]; @@ -505,10 +507,11 @@ public function mountPointExists(string $mountPoint): bool { ->setMaxResults(1); $result = $query->executeQuery(); - $exists = (int)$result->fetchOne() > 0; + /** @var int $count */ + $count = $result->fetchOne(); $result->closeCursor(); - return $exists; + return $count > 0; } /** @@ -522,6 +525,7 @@ private function getManagerMappings(int $folderId): array { ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT))); $managerMappings = []; + /** @var list $rows */ $rows = $query->executeQuery()->fetchAll(); foreach ($rows as $manageRule) { $managerMappings[] = new UserMapping($manageRule['mapping_type'], $manageRule['mapping_id']); @@ -622,6 +626,7 @@ private function getFolderOptions(array $row): array { return []; } + /** @var array{separate-storage?: bool} $options */ return $options; } @@ -673,11 +678,11 @@ public function getFoldersForGroups(array $groupIds, ?int $folderId = null, ?str $result = []; foreach (array_chunk($groupIds, 1000) as $chunk) { $query->setParameter('groupIds', $chunk, IQueryBuilder::PARAM_STR_ARRAY); + /** @var list $result */ $result = array_merge($result, $query->executeQuery()->fetchAll()); } return array_map(function (array $row): FolderDefinitionWithPermissions { - /** @var array{folder_id: int, mount_point: string, quota: int, acl: bool, acl_default_no_permission: bool, storage_id: int, root_id: int, options: string, fileid: int, storage: int, path: string, name: string, mimetype: string, mimepart: string, size: int, mtime: int, storage_mtime: int, etag: string, encrypted: bool, parent: int, permissions: int, group_permissions: int} $row */ $folder = $this->rowToFolder($row); return FolderDefinitionWithPermissions::fromFolder( $folder, @@ -725,15 +730,17 @@ public function getFoldersFromCircleMemberships(IUser $user, ?int $folderId = nu $queryHelper->limitToMemberships('a', 'circle_id', $federatedUser); + /** @var list $rows */ + $rows = $query->executeQuery()->fetchAll(); + return array_map(function (array $row): FolderDefinitionWithPermissions { - /** @var array{folder_id: int, mount_point: string, quota: int, acl: bool, acl_default_no_permission: bool, storage_id: int, root_id: int, options: string, fileid: int, storage: int, path: string, name: string, mimetype: string, mimepart: string, size: int, mtime: int, storage_mtime: int, etag: string, encrypted: bool, parent: int, permissions: int, group_permissions: int} $row */ $folder = $this->rowToFolder($row); return FolderDefinitionWithPermissions::fromFolder( $folder, Cache::cacheEntryFromData($row, $this->mimeTypeLoader), $row['group_permissions'] ); - }, $query->executeQuery()->fetchAll()); + }, $rows); } public function trimMountpoint(string $mountpoint): string { diff --git a/lib/Migration/Version103000Date20180806161724.php b/lib/Migration/Version103000Date20180806161724.php index bacb4c07f..cb3adb1d6 100644 --- a/lib/Migration/Version103000Date20180806161724.php +++ b/lib/Migration/Version103000Date20180806161724.php @@ -15,7 +15,7 @@ class Version103000Date20180806161724 extends SimpleMigrationStep { /** - * @var mixed[] + * @var list */ private array $applicableData = []; @@ -38,7 +38,9 @@ public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $query->select(['folder_id', 'permissions', 'group_id']) ->from('group_folders_applicable'); $result = $query->executeQuery(); - $this->applicableData = $result->fetchAll(\PDO::FETCH_ASSOC); + /** @var list $data */ + $data = $result->fetchAll(\PDO::FETCH_ASSOC); + $this->applicableData = $data; } } diff --git a/lib/Migration/Version20001Date20250612140256.php b/lib/Migration/Version20001Date20250612140256.php index 44805aab4..cf0b19774 100644 --- a/lib/Migration/Version20001Date20250612140256.php +++ b/lib/Migration/Version20001Date20250612140256.php @@ -114,6 +114,7 @@ private function getJailedRootIds(int $storageId): array { $rootIds = []; while ($row = $result->fetch()) { + /** @var array{name: string, fileid: int} $row */ if (is_numeric($row['name'])) { $rootIds[(int)$row['name']] = (int)$row['fileid']; } @@ -128,11 +129,12 @@ private function getJailedGroupFolderRootId(int $storageId): ?int { ->where($query->expr()->eq('path_hash', $query->createNamedParameter(md5('__groupfolders')))) ->andWhere($query->expr()->eq('storage', $query->createNamedParameter($storageId))); + /** @var int|false $id */ $id = $query->executeQuery()->fetchOne(); if ($id === false) { return null; } else { - return (int)$id; + return $id; } } diff --git a/lib/Mount/CacheRootPermissionsMask.php b/lib/Mount/CacheRootPermissionsMask.php index 3d0fc95d4..78133cef2 100644 --- a/lib/Mount/CacheRootPermissionsMask.php +++ b/lib/Mount/CacheRootPermissionsMask.php @@ -26,6 +26,7 @@ protected function formatCacheEntry($entry): ICacheEntry|false { $isRoot = $entry->getId() === $this->rootId; if (isset($entry['permissions']) && $isRoot) { $entry['scan_permissions'] ??= $entry['permissions']; + /** @phpstan-ignore assignOp.invalid */ $entry['permissions'] &= $this->mask; } diff --git a/lib/Mount/FolderStorageManager.php b/lib/Mount/FolderStorageManager.php index 423450460..39b98a703 100644 --- a/lib/Mount/FolderStorageManager.php +++ b/lib/Mount/FolderStorageManager.php @@ -8,6 +8,7 @@ namespace OCA\GroupFolders\Mount; +use Exception; use OC\Files\Cache\Cache; use OC\Files\ObjectStore\ObjectStoreStorage; use OC\Files\ObjectStore\PrimaryObjectStoreConfig; @@ -97,8 +98,12 @@ private function getBaseStorageForFolderSeparate( array $options = [], ): IStorage { if ($this->primaryObjectStoreConfig->hasObjectStore()) { + $bucket = $options['bucket'] ?? null; + if ($bucket !== null && !is_string($bucket)) { + throw new Exception('bucket is not a string.'); + } /** @var Storage $storage */ - $storage = $this->getBaseStorageForFolderSeparateStorageObject($folderId, $init, $options['bucket'] ?? null); + $storage = $this->getBaseStorageForFolderSeparateStorageObject($folderId, $init, $bucket); } else { /** @var Storage $storage */ $storage = $this->getBaseStorageForFolderSeparateStorageLocal($folderId, $init); @@ -132,7 +137,7 @@ private function getBaseStorageForFolderSeparateStorageLocal( int $folderId, bool $init = false, ): IStorage { - $dataDirectory = $this->config->getSystemValue('datadirectory'); + $dataDirectory = $this->config->getSystemValueString('datadirectory'); $rootPath = $dataDirectory . '/__groupfolders/' . $folderId; if ($init) { $result = mkdir($rootPath . '/files', recursive: true); @@ -297,8 +302,18 @@ private function getObjectStorageBucket(int $folderId, array $objectStoreConfig, if ($overwriteBucket !== null) { $bucket = $overwriteBucket; } else { - $bucketBase = $objectStoreConfig['arguments']['bucket'] ?? ''; - $bucket = $bucketBase . $this->calculateBucketNum((string)$folderId, $objectStoreConfig); + $arguments = $objectStoreConfig['arguments']; + if (!is_array($arguments)) { + throw new Exception('arguments is not an array.'); + } + + $bucketBase = $arguments['bucket']; + if ($bucketBase !== null && !is_string($bucketBase)) { + throw new Exception('bucket is not a string.'); + } + $bucketBase ??= ''; + + $bucket = $bucketBase . $this->calculateBucketNum((string)$folderId, $arguments); } $this->appConfig->setValueString(Application::APP_ID, $bucketKey, $bucket); @@ -309,14 +324,22 @@ private function getObjectStorageBucket(int $folderId, array $objectStoreConfig, /** * logic taken from OC\Files\ObjectStore\Mapper which we can't use because it requires an IUser - * @param array $objectStoreConfig + * @param array $arguments */ - private function calculateBucketNum(string $key, array $objectStoreConfig): string { - $numBuckets = $objectStoreConfig['arguments']['num_buckets'] ?? 64; + private function calculateBucketNum(string $key, array $arguments): string { + $numBuckets = $arguments['num_buckets']; + if ($numBuckets !== null && !is_int($numBuckets)) { + throw new Exception('num_buckets is not an integer.'); + } + $numBuckets ??= 64; // Get the bucket config and shift if provided. // Allow us to prevent writing in old filled buckets - $minBucket = (int)($objectStoreConfig['arguments']['min_bucket'] ?? 0); + $minBucket = $arguments['min_bucket']; + if ($minBucket !== null && !is_int($minBucket)) { + throw new Exception('min_bucket is not an integer.'); + } + $minBucket ??= 0; $hash = md5($key); $num = hexdec(substr($hash, 0, 4)); diff --git a/lib/Mount/MountProvider.php b/lib/Mount/MountProvider.php index ca22d59a2..45f8222af 100644 --- a/lib/Mount/MountProvider.php +++ b/lib/Mount/MountProvider.php @@ -17,6 +17,7 @@ use OCA\GroupFolders\Folder\FolderDefinition; use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions; use OCA\GroupFolders\Folder\FolderManager; +use OCA\Richdocuments\Db\WopiMapper; use OCP\Constants; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Cache\ICacheEntry; @@ -95,8 +96,8 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader): array { private function getCurrentUID(): ?string { try { // wopi requests are not logged in, instead we need to get the editor user from the access token - if (str_contains($this->request->getRawPathInfo(), 'apps/richdocuments/wopi') && class_exists('OCA\Richdocuments\Db\WopiMapper')) { - $wopiMapper = Server::get('OCA\Richdocuments\Db\WopiMapper'); + if (str_contains($this->request->getRawPathInfo(), 'apps/richdocuments/wopi') && class_exists(WopiMapper::class)) { + $wopiMapper = Server::get(WopiMapper::class); $token = $this->request->getParam('access_token'); if ($token) { $wopi = $wopiMapper->getPathForToken($token); @@ -126,6 +127,7 @@ public function getMount( if ($aclManager && $folder->acl && $user) { $aclRootPermissions = $aclManager->getPermissionsForPathFromRules($folder->id, $cacheEntry->getPath(), $rootRules); + /** @phpstan-ignore assignOp.invalid */ $cacheEntry['permissions'] &= $aclRootPermissions; } @@ -261,7 +263,9 @@ private function findConflictsForUser(IUser $user, array $mountPoints): array { $paths = []; foreach (array_chunk($pathHashes, 1000) as $chunk) { $query->setParameter('chunk', $chunk, IQueryBuilder::PARAM_STR_ARRAY); - array_push($paths, ...$query->executeQuery()->fetchAll(PDO::FETCH_COLUMN)); + /** @var array $rows */ + $rows = $query->executeQuery()->fetchAll(PDO::FETCH_COLUMN); + array_push($paths, ...$rows); } return array_map(function (string $path): string { diff --git a/lib/Mount/RootPermissionsMask.php b/lib/Mount/RootPermissionsMask.php index c9acd187b..d5d097186 100644 --- a/lib/Mount/RootPermissionsMask.php +++ b/lib/Mount/RootPermissionsMask.php @@ -87,13 +87,14 @@ public function getPermissions(string $path): int { } /** - * @return ?array + * @return ?array */ #[\Override] public function getMetaData(string $path): ?array { + /** @var ?array{permissions?: int, scan_permissions?: int} $data */ $data = parent::getMetaData($path); - if (is_array($data) && $path === '' && isset($data['permissions'])) { + if ($data !== null && $path === '' && isset($data['permissions'])) { $data['scan_permissions'] ??= $data['permissions']; $data['permissions'] &= $this->mask; } diff --git a/lib/Trash/TrashManager.php b/lib/Trash/TrashManager.php index 0f4725d11..b36a86c7f 100644 --- a/lib/Trash/TrashManager.php +++ b/lib/Trash/TrashManager.php @@ -29,6 +29,9 @@ public function listTrashForFolders(array $folderIds): array { ->orderBy('deleted_time') ->where($query->expr()->in('folder_id', $query->createNamedParameter($folderIds, IQueryBuilder::PARAM_INT_ARRAY))); + /** @var list $rows */ + $rows = $query->executeQuery()->fetchAll(); + return array_map(fn (array $row): array => [ 'trash_id' => (int)$row['trash_id'], 'name' => (string)$row['name'], @@ -37,7 +40,7 @@ public function listTrashForFolders(array $folderIds): array { 'folder_id' => (int)$row['folder_id'], 'file_id' => $row['file_id'] !== null ? (int)$row['file_id'] : null, 'deleted_by' => $row['deleted_by'] !== null ? (string)$row['deleted_by'] : null, - ], $query->executeQuery()->fetchAll()); + ], $rows); } public function addTrashItem(int $folderId, string $name, int $deletedTime, string $originalLocation, int $fileId, string $deletedBy): void { diff --git a/lib/Versions/GroupVersionEntity.php b/lib/Versions/GroupVersionEntity.php index 60840e94c..57989576c 100644 --- a/lib/Versions/GroupVersionEntity.php +++ b/lib/Versions/GroupVersionEntity.php @@ -61,6 +61,7 @@ public function jsonSerialize(): array { * @return array */ public function getDecodedMetadata(): array { + /** @phpstan-ignore return.type */ return json_decode($this->metadata ?? '', true, 512, JSON_THROW_ON_ERROR) ?? []; } diff --git a/phpstan.neon b/phpstan.neon index cea0b85eb..3008aae35 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later parameters: - level: 8 + level: 9 phpVersion: min: 80200 max: 80500 @@ -23,5 +23,6 @@ parameters: - ../files_sharing/lib/ - ../files_trashbin/lib/ - ../files_versions/lib/ + - ../richdocuments/lib/ - ../settings/lib/ - vendor-bin/phpunit/vendor/phpunit/phpunit From 71976599a4dd1ff7c22fb2cc45a01ee6b2b71f19 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 12 Feb 2026 09:40:13 +0100 Subject: [PATCH 12/12] feat(phpstan): Level 10 Signed-off-by: provokateurin --- lib/ACL/ACLStorageWrapper.php | 3 --- lib/ACL/Rule.php | 32 ++++++++++++++++++++++++++++---- lib/Command/Scan.php | 2 +- lib/DAV/ACLPlugin.php | 2 ++ lib/Listeners/CacheListener.php | 1 + lib/Trash/TrashBackend.php | 10 +++------- phpstan.neon | 2 +- templates/index.php | 5 ++++- 8 files changed, 40 insertions(+), 17 deletions(-) diff --git a/lib/ACL/ACLStorageWrapper.php b/lib/ACL/ACLStorageWrapper.php index 274bdc054..237b7a43d 100644 --- a/lib/ACL/ACLStorageWrapper.php +++ b/lib/ACL/ACLStorageWrapper.php @@ -212,9 +212,6 @@ public function getCache(string $path = '', ?IStorage $storage = null): ICache { return new ACLCacheWrapper($sourceCache, $this->aclManager, $this->folderId, $this->inShare); } - /** - * @return ?array - */ #[\Override] public function getMetaData(string $path): ?array { $data = parent::getMetaData($path); diff --git a/lib/ACL/Rule.php b/lib/ACL/Rule.php index 669100b6e..874be5a8d 100644 --- a/lib/ACL/Rule.php +++ b/lib/ACL/Rule.php @@ -135,14 +135,38 @@ public function jsonSerialize(): array { public static function xmlDeserialize(Reader $reader): Rule { $elements = \Sabre\Xml\Deserializer\keyValue($reader); + $mappingType = $elements[self::MAPPING_TYPE]; + if (!is_string($mappingType)) { + throw new \RuntimeException(self::MAPPING_TYPE . ' is not a string.'); + } + + if (!in_array($mappingType, ['user', 'group', 'circle'])) { + throw new \RuntimeException(self::MAPPING_TYPE . ' does not have a valid value.'); + } + + $mappingId = $elements[self::MAPPING_ID]; + if (!is_string($mappingId)) { + throw new \RuntimeException(self::MAPPING_ID . ' is not a string.'); + } + + $mask = $elements[self::MASK]; + if (!is_string($mask)) { + throw new \RuntimeException(self::MASK . ' is not a string.'); + } + + $permission = $elements[self::PERMISSIONS]; + if (!is_string($permission)) { + throw new \RuntimeException(self::PERMISSIONS . ' is not a string.'); + } + return new Rule( new UserMapping( - $elements[self::MAPPING_TYPE], - $elements[self::MAPPING_ID] + $mappingType, + $mappingId, ), -1, - (int)$elements[self::MASK], - (int)$elements[self::PERMISSIONS] + (int)$mask, + (int)$permission, ); } diff --git a/lib/Command/Scan.php b/lib/Command/Scan.php index 3ed87c2f1..5be722281 100644 --- a/lib/Command/Scan.php +++ b/lib/Command/Scan.php @@ -143,7 +143,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->abortIfInterrupted(); }); - $scanner->listen('\OC\Files\Cache\Scanner', 'normalizedNameMismatch', function ($fullPath) use ($output, &$statsRow): void { + $scanner->listen('\OC\Files\Cache\Scanner', 'normalizedNameMismatch', function (string $fullPath) use ($output, &$statsRow): void { $output->writeln("\tEntry \"" . $fullPath . '" will not be accessible due to incompatible encoding'); $statsRow[3]++; }); diff --git a/lib/DAV/ACLPlugin.php b/lib/DAV/ACLPlugin.php index bd3300459..b5780150f 100644 --- a/lib/DAV/ACLPlugin.php +++ b/lib/DAV/ACLPlugin.php @@ -249,6 +249,7 @@ public function propPatch(string $path, PropPatch $propPatch): void { } // populate fileid in rules + /** @var Rule[] $rawRules */ $rules = array_values(array_map(fn (Rule $rule): Rule => new Rule( $rule->getUserMapping(), $fileInfoId, @@ -277,6 +278,7 @@ public function propPatch(string $path, PropPatch $propPatch): void { throw new BadRequest($this->l10n->t('You cannot remove your own read permission.')); } + /** @var list $existingRules */ $existingRules = array_reduce( $this->ruleManager->getAllRulesForPaths($mount->getNumericStorageId(), [$path]), array_merge(...), diff --git a/lib/Listeners/CacheListener.php b/lib/Listeners/CacheListener.php index 6ed97c884..9cb18c542 100644 --- a/lib/Listeners/CacheListener.php +++ b/lib/Listeners/CacheListener.php @@ -36,6 +36,7 @@ public function handle(Event $event): void { return; } + /** @var Jail $storage @phpstan-ignore varTag.nativeType */ $path = $storage->getJailedPath($event->getPath()); if ($path !== null) { $event->setPath($path); diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index 94d77782e..2707a242f 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -153,8 +153,8 @@ public function restoreItem(ITrashItem $item): void { $info = pathinfo($originalLocation); $i = 1; - $gen = function (array $info, int $i): string { - $target = $info['dirname']; + do { + $target = $info['dirname'] ?? ''; if ($target === '.') { $target = ''; } @@ -166,11 +166,7 @@ public function restoreItem(ITrashItem $item): void { $target .= '.' . $info['extension']; } - return $target; - }; - - do { - $originalLocation = $gen($info, $i); + $originalLocation = $target; $i++; } while ($targetFolder->nodeExists($originalLocation)); } diff --git a/phpstan.neon b/phpstan.neon index 3008aae35..a8c954dc6 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later parameters: - level: 9 + level: 10 phpVersion: min: 80200 max: 80500 diff --git a/templates/index.php b/templates/index.php index 6b6dc2ef0..604749b79 100644 --- a/templates/index.php +++ b/templates/index.php @@ -3,12 +3,15 @@ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ + +use OCP\IL10N; + ?>

t('Team folders')); ?>