├── .github └── workflows │ ├── appstore-build-publish.yml │ ├── integration.yml │ ├── lint-info-xml.yml │ ├── lint-php-cs.yml │ ├── lint-php.yml │ ├── phpunit-mysql.yml │ ├── phpunit-oci.yml │ ├── phpunit-pgsql.yml │ ├── phpunit-sqlite.yml │ ├── psalm-matrix.yml │ └── update-nextcloud-ocp.yml ├── .gitignore ├── .nextcloudignore ├── .php-cs-fixer.dist.php ├── CHANGELOG.md ├── README.md ├── appinfo └── info.xml ├── composer.json ├── composer.lock ├── krankerl.toml ├── lib ├── AppInfo │ └── Application.php ├── INotifyBackendProvider.php ├── LoadBackendListener.php └── Storage │ ├── INotifyBackend.php │ ├── INotifyWrapper.php │ └── NotifyHandler.php ├── psalm.xml ├── tests ├── Storage │ └── NotifyHandlerTest.php ├── bootstrap.php ├── phpunit.xml └── stub.phpstub └── vendor-bin ├── cs-fixer ├── composer.json └── composer.lock ├── phpunit ├── composer.json └── composer.lock └── psalm ├── composer.json └── composer.lock /.github/workflows/appstore-build-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | name: Build and publish app release 7 | 8 | on: 9 | release: 10 | types: [published] 11 | 12 | jobs: 13 | build_and_publish: 14 | runs-on: ubuntu-latest 15 | 16 | # Only allowed to be run on nextcloud-releases repositories 17 | if: ${{ github.repository_owner == 'icewind1991' }} 18 | 19 | steps: 20 | - name: Check actor permission 21 | uses: skjnldsv/check-actor-permission@69e92a3c4711150929bca9fcf34448c5bf5526e7 # v3.0 22 | with: 23 | require: write 24 | 25 | - name: Set app env 26 | run: | 27 | # Split and keep last 28 | echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV 29 | echo "APP_VERSION=${GITHUB_REF##*/}" >> $GITHUB_ENV 30 | 31 | - name: Checkout 32 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 33 | with: 34 | path: ${{ env.APP_NAME }} 35 | 36 | - name: Get appinfo data 37 | id: appinfo 38 | uses: skjnldsv/xpath-action@7e6a7c379d0e9abc8acaef43df403ab4fc4f770c # master 39 | with: 40 | filename: ${{ env.APP_NAME }}/appinfo/info.xml 41 | expression: "//info//dependencies//nextcloud/@min-version" 42 | 43 | - name: Read package.json node and npm engines version 44 | uses: skjnldsv/read-package-engines-version-actions@8205673bab74a63eb9b8093402fd9e0e018663a1 # v2.2 45 | id: versions 46 | # Continue if no package.json 47 | continue-on-error: true 48 | with: 49 | path: ${{ env.APP_NAME }} 50 | fallbackNode: '^20' 51 | fallbackNpm: '^10' 52 | 53 | - name: Set up node ${{ steps.versions.outputs.nodeVersion }} 54 | # Skip if no package.json 55 | if: ${{ steps.versions.outputs.nodeVersion }} 56 | uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v3 57 | with: 58 | node-version: ${{ steps.versions.outputs.nodeVersion }} 59 | 60 | - name: Set up npm ${{ steps.versions.outputs.npmVersion }} 61 | # Skip if no package.json 62 | if: ${{ steps.versions.outputs.npmVersion }} 63 | run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}" 64 | 65 | - name: Get php version 66 | id: php-versions 67 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 68 | with: 69 | filename: ${{ env.APP_NAME }}/appinfo/info.xml 70 | 71 | - name: Set up php ${{ steps.php-versions.outputs.php-min }} 72 | uses: shivammathur/setup-php@a4e22b60bbb9c1021113f2860347b0759f66fe5d # v2 73 | with: 74 | php-version: ${{ steps.php-versions.outputs.php-min }} 75 | coverage: none 76 | env: 77 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 78 | 79 | - name: Check composer.json 80 | id: check_composer 81 | uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 82 | with: 83 | files: "${{ env.APP_NAME }}/composer.json" 84 | 85 | - name: Install composer dependencies 86 | if: steps.check_composer.outputs.files_exists == 'true' 87 | run: | 88 | cd ${{ env.APP_NAME }} 89 | composer install --no-dev 90 | 91 | - name: Build ${{ env.APP_NAME }} 92 | # Skip if no package.json 93 | if: ${{ steps.versions.outputs.nodeVersion }} 94 | env: 95 | CYPRESS_INSTALL_BINARY: 0 96 | run: | 97 | cd ${{ env.APP_NAME }} 98 | npm ci 99 | npm run build 100 | 101 | - name: Check Krankerl config 102 | id: krankerl 103 | uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 104 | with: 105 | files: ${{ env.APP_NAME }}/krankerl.toml 106 | 107 | - name: Install Krankerl 108 | if: steps.krankerl.outputs.files_exists == 'true' 109 | run: | 110 | wget https://github.com/ChristophWurst/krankerl/releases/download/v0.14.0/krankerl_0.14.0_amd64.deb 111 | sudo dpkg -i krankerl_0.14.0_amd64.deb 112 | 113 | - name: Package ${{ env.APP_NAME }} ${{ env.APP_VERSION }} with krankerl 114 | if: steps.krankerl.outputs.files_exists == 'true' 115 | run: | 116 | cd ${{ env.APP_NAME }} 117 | krankerl package 118 | 119 | - name: Package ${{ env.APP_NAME }} ${{ env.APP_VERSION }} with makefile 120 | if: steps.krankerl.outputs.files_exists != 'true' 121 | run: | 122 | cd ${{ env.APP_NAME }} 123 | make appstore 124 | 125 | - name: Checkout server ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} 126 | continue-on-error: true 127 | id: server-checkout 128 | run: | 129 | NCVERSION=${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} 130 | wget --quiet https://download.nextcloud.com/server/releases/latest-$NCVERSION.zip 131 | unzip latest-$NCVERSION.zip 132 | 133 | - name: Checkout server master fallback 134 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 135 | if: ${{ steps.server-checkout.outcome != 'success' }} 136 | with: 137 | submodules: true 138 | repository: nextcloud/server 139 | path: nextcloud 140 | 141 | - name: Sign app 142 | run: | 143 | # Extracting release 144 | cd ${{ env.APP_NAME }}/build/artifacts 145 | tar -xvf ${{ env.APP_NAME }}.tar.gz 146 | cd ../../../ 147 | # Setting up keys 148 | echo "${{ secrets.APP_PRIVATE_KEY }}" > ${{ env.APP_NAME }}.key 149 | wget --quiet "https://github.com/nextcloud/app-certificate-requests/raw/master/${{ env.APP_NAME }}/${{ env.APP_NAME }}.crt" 150 | # Signing 151 | php nextcloud/occ integrity:sign-app --privateKey=../${{ env.APP_NAME }}.key --certificate=../${{ env.APP_NAME }}.crt --path=../${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }} 152 | # Rebuilding archive 153 | cd ${{ env.APP_NAME }}/build/artifacts 154 | tar -zcvf ${{ env.APP_NAME }}.tar.gz ${{ env.APP_NAME }} 155 | 156 | - name: Attach tarball to github release 157 | uses: svenstaro/upload-release-action@04733e069f2d7f7f0b4aebc4fbdbce8613b03ccd # v2 158 | id: attach_to_release 159 | with: 160 | repo_token: ${{ secrets.GITHUB_TOKEN }} 161 | file: ${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }}.tar.gz 162 | asset_name: ${{ env.APP_NAME }}-${{ env.APP_VERSION }}.tar.gz 163 | tag: ${{ github.ref }} 164 | overwrite: true 165 | 166 | - name: Upload app to Nextcloud appstore 167 | uses: nextcloud-releases/nextcloud-appstore-push-action@a011fe619bcf6e77ddebc96f9908e1af4071b9c1 # v1 168 | with: 169 | app_name: ${{ env.APP_NAME }} 170 | appstore_token: ${{ secrets.APPSTORE_TOKEN }} 171 | download_url: ${{ steps.attach_to_release.outputs.browser_download_url }} 172 | app_private_key: ${{ secrets.APP_PRIVATE_KEY }} 173 | -------------------------------------------------------------------------------- /.github/workflows/integration.yml: -------------------------------------------------------------------------------- 1 | name: Integration tests 2 | 3 | on: pull_request 4 | 5 | permissions: 6 | contents: read 7 | 8 | concurrency: 9 | group: integration-${{ github.head_ref || github.run_id }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | matrix: 14 | runs-on: ubuntu-latest 15 | outputs: 16 | matrix: ${{ steps.versions.outputs.matrix }} 17 | steps: 18 | - name: Checkout app 19 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 20 | - name: Get version matrix 21 | id: versions 22 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 23 | 24 | changes: 25 | runs-on: ubuntu-latest 26 | 27 | outputs: 28 | src: ${{ steps.changes.outputs.src}} 29 | 30 | steps: 31 | - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 32 | id: changes 33 | continue-on-error: true 34 | with: 35 | filters: | 36 | src: 37 | - '.github/workflows/**' 38 | - 'appinfo/**' 39 | - 'lib/**' 40 | - 'templates/**' 41 | - 'tests/**' 42 | - 'vendor/**' 43 | - 'vendor-bin/**' 44 | - '.php-cs-fixer.dist.php' 45 | - 'composer.json' 46 | - 'composer.lock' 47 | 48 | integration: 49 | runs-on: ubuntu-latest 50 | 51 | needs: [changes, matrix] 52 | if: needs.changes.outputs.src != 'false' 53 | 54 | strategy: 55 | matrix: ${{ fromJson(needs.matrix.outputs.matrix) }} 56 | 57 | name: Integrations - PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} 58 | 59 | steps: 60 | - name: Set app env 61 | run: | 62 | # Split and keep last 63 | echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV 64 | 65 | - name: Checkout server 66 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 67 | with: 68 | submodules: true 69 | repository: nextcloud/server 70 | ref: ${{ matrix.server-versions }} 71 | 72 | - name: Checkout app 73 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 74 | with: 75 | path: apps/${{ env.APP_NAME }} 76 | 77 | - name: Set up php ${{ matrix.php-versions }} 78 | uses: shivammathur/setup-php@81cd5ae0920b34eef300e1775313071038a53429 # v2 79 | with: 80 | php-version: ${{ matrix.php-versions }} 81 | # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation 82 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite, inotify 83 | coverage: none 84 | ini-file: development 85 | env: 86 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 87 | 88 | - name: Check composer file existence 89 | id: check_composer 90 | uses: andstor/file-existence-action@20b4d2e596410855db8f9ca21e96fbe18e12930b # v2 91 | with: 92 | files: apps/${{ env.APP_NAME }}/composer.json 93 | 94 | - name: Set up dependencies 95 | # Only run if phpunit config file exists 96 | if: steps.check_composer.outputs.files_exists == 'true' 97 | working-directory: apps/${{ env.APP_NAME }} 98 | run: composer i 99 | 100 | - name: Set up Nextcloud 101 | env: 102 | DB_PORT: 4444 103 | run: | 104 | mkdir data 105 | ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin 106 | ./occ app:enable --force files_external 107 | 108 | - name: Setup local external storage 109 | run: | 110 | mkdir /tmp/ext 111 | ./occ files_external:create local local null::null 112 | ./occ files_external:config 1 datadir /tmp/ext 113 | ./occ files_external:list 114 | 115 | - name: Check that local external storage doesn't support notify without the app 116 | run: | 117 | set +e 118 | 119 | # notify command runs forever, so use timeout to distinguish between "failed to start" and "started running" 120 | timeout 2 ./occ files_external:notify 1 121 | status=$? 122 | echo "exit code: $status" 123 | 124 | set -e 125 | 126 | [ $status -eq 1 ] 127 | 128 | - name: Enable app 129 | run: | 130 | ./occ app:enable --force ${{ env.APP_NAME }} 131 | 132 | - name: Check that local external storage supports notify with the app 133 | run: | 134 | set +e 135 | 136 | # notify command runs forever, so use timeout to distinguish between "failed to start" and "started running" 137 | output=$(timeout 2 ./occ files_external:notify 1 -v ) 138 | status=$? 139 | echo "exit code: $status" 140 | echo "output: $output" 141 | 142 | set -e 143 | 144 | [ $status -eq 124 ] 145 | [[ $output == *"Self-test successful"* ]] 146 | 147 | - name: Print logs 148 | if: always() 149 | run: | 150 | cat data/nextcloud.log 151 | 152 | summary: 153 | permissions: 154 | contents: none 155 | runs-on: ubuntu-latest 156 | needs: [changes, integration] 157 | 158 | if: always() 159 | 160 | name: integration-summary 161 | 162 | steps: 163 | - name: Summary status 164 | run: if ${{ needs.changes.outputs.src != 'false' && needs.integration.result != 'success' }}; then exit 1; fi 165 | -------------------------------------------------------------------------------- /.github/workflows/lint-info-xml.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | name: Lint info.xml 7 | 8 | on: pull_request 9 | 10 | permissions: 11 | contents: read 12 | 13 | concurrency: 14 | group: lint-info-xml-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | xml-linters: 19 | runs-on: ubuntu-latest 20 | 21 | name: info.xml lint 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 25 | 26 | - name: Download schema 27 | run: wget https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/api/v1/release/info.xsd 28 | 29 | - name: Lint info.xml 30 | uses: ChristophWurst/xmllint-action@36f2a302f84f8c83fceea0b9c59e1eb4a616d3c1 # v1.2 31 | with: 32 | xml-file: ./appinfo/info.xml 33 | xml-schema-file: ./info.xsd 34 | -------------------------------------------------------------------------------- /.github/workflows/lint-php-cs.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | name: Lint php-cs 7 | 8 | on: pull_request 9 | 10 | permissions: 11 | contents: read 12 | 13 | concurrency: 14 | group: lint-php-cs-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | lint: 19 | runs-on: ubuntu-latest 20 | 21 | name: php-cs 22 | 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 26 | 27 | - name: Get php version 28 | id: versions 29 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 30 | 31 | - name: Set up php${{ steps.versions.outputs.php-available }} 32 | uses: shivammathur/setup-php@a4e22b60bbb9c1021113f2860347b0759f66fe5d # v2 33 | with: 34 | php-version: ${{ steps.versions.outputs.php-available }} 35 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite 36 | coverage: none 37 | ini-file: development 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | 41 | - name: Install dependencies 42 | run: composer i 43 | 44 | - name: Lint 45 | run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 ) 46 | -------------------------------------------------------------------------------- /.github/workflows/lint-php.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | name: Lint php 7 | 8 | on: pull_request 9 | 10 | permissions: 11 | contents: read 12 | 13 | concurrency: 14 | group: lint-php-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | matrix: 19 | runs-on: ubuntu-latest 20 | outputs: 21 | php-versions: ${{ steps.versions.outputs.php-versions }} 22 | steps: 23 | - name: Checkout app 24 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 25 | - name: Get version matrix 26 | id: versions 27 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.0.0 28 | 29 | php-lint: 30 | runs-on: ubuntu-latest 31 | needs: matrix 32 | strategy: 33 | matrix: 34 | php-versions: ${{fromJson(needs.matrix.outputs.php-versions)}} 35 | 36 | name: php-lint 37 | 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 41 | 42 | - name: Set up php ${{ matrix.php-versions }} 43 | uses: shivammathur/setup-php@a4e22b60bbb9c1021113f2860347b0759f66fe5d # v2 44 | with: 45 | php-version: ${{ matrix.php-versions }} 46 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite 47 | coverage: none 48 | ini-file: development 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | 52 | - name: Lint 53 | run: composer run lint 54 | 55 | summary: 56 | permissions: 57 | contents: none 58 | runs-on: ubuntu-latest 59 | needs: php-lint 60 | 61 | if: always() 62 | 63 | name: php-lint-summary 64 | 65 | steps: 66 | - name: Summary status 67 | run: if ${{ needs.php-lint.result != 'success' && needs.php-lint.result != 'skipped' }}; then exit 1; fi 68 | -------------------------------------------------------------------------------- /.github/workflows/phpunit-mysql.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | name: PHPUnit MySQL 7 | 8 | on: pull_request 9 | 10 | permissions: 11 | contents: read 12 | 13 | concurrency: 14 | group: phpunit-mysql-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | matrix: 19 | runs-on: ubuntu-latest 20 | outputs: 21 | matrix: ${{ steps.versions.outputs.sparse-matrix }} 22 | steps: 23 | - name: Checkout app 24 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 25 | 26 | - name: Get version matrix 27 | id: versions 28 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 29 | with: 30 | matrix: '{"mysql-versions": ["8.1"]}' 31 | 32 | changes: 33 | runs-on: ubuntu-latest 34 | 35 | outputs: 36 | src: ${{ steps.changes.outputs.src}} 37 | 38 | steps: 39 | - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 40 | id: changes 41 | continue-on-error: true 42 | with: 43 | filters: | 44 | src: 45 | - '.github/workflows/**' 46 | - 'appinfo/**' 47 | - 'lib/**' 48 | - 'templates/**' 49 | - 'tests/**' 50 | - 'vendor/**' 51 | - 'vendor-bin/**' 52 | - '.php-cs-fixer.dist.php' 53 | - 'composer.json' 54 | - 'composer.lock' 55 | 56 | phpunit-mysql: 57 | runs-on: ubuntu-latest 58 | 59 | needs: [changes, matrix] 60 | if: needs.changes.outputs.src != 'false' 61 | 62 | strategy: 63 | matrix: ${{ fromJson(needs.matrix.outputs.matrix) }} 64 | 65 | name: MySQL ${{ matrix.mysql-versions }} PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} 66 | 67 | services: 68 | mysql: 69 | image: ghcr.io/nextcloud/continuous-integration-mysql-${{ matrix.mysql-versions }}:latest 70 | ports: 71 | - 4444:3306/tcp 72 | env: 73 | MYSQL_ROOT_PASSWORD: rootpassword 74 | options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 10 75 | 76 | steps: 77 | - name: Set app env 78 | run: | 79 | # Split and keep last 80 | echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV 81 | 82 | - name: Checkout server 83 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 84 | with: 85 | submodules: true 86 | repository: nextcloud/server 87 | ref: ${{ matrix.server-versions }} 88 | 89 | - name: Checkout app 90 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 91 | with: 92 | path: apps/${{ env.APP_NAME }} 93 | 94 | - name: Set up php ${{ matrix.php-versions }} 95 | uses: shivammathur/setup-php@a4e22b60bbb9c1021113f2860347b0759f66fe5d # v2 96 | with: 97 | php-version: ${{ matrix.php-versions }} 98 | # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation 99 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, mysql, pdo_mysql, inotify 100 | coverage: none 101 | ini-file: development 102 | env: 103 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 104 | 105 | - name: Enable ONLY_FULL_GROUP_BY MySQL option 106 | run: | 107 | echo "SET GLOBAL sql_mode=(SELECT CONCAT(@@sql_mode,',ONLY_FULL_GROUP_BY'));" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword 108 | echo "SELECT @@sql_mode;" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword 109 | 110 | - name: Check composer file existence 111 | id: check_composer 112 | uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 113 | with: 114 | files: apps/${{ env.APP_NAME }}/composer.json 115 | 116 | - name: Set up dependencies 117 | # Only run if phpunit config file exists 118 | if: steps.check_composer.outputs.files_exists == 'true' 119 | working-directory: apps/${{ env.APP_NAME }} 120 | run: | 121 | composer i --no-dev 122 | rm composer.lock 123 | composer require phpunit/phpunit ^9 124 | 125 | - name: Set up Nextcloud 126 | env: 127 | DB_PORT: 4444 128 | run: | 129 | mkdir data 130 | ./occ maintenance:install --verbose --database=mysql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin 131 | ./occ app:enable --force ${{ env.APP_NAME }} 132 | 133 | - name: Check PHPUnit script is defined 134 | id: check_phpunit 135 | continue-on-error: true 136 | working-directory: apps/${{ env.APP_NAME }} 137 | run: | 138 | composer run --list | grep "^ test:unit " | wc -l | grep 1 139 | 140 | - name: PHPUnit 141 | # Only run if phpunit config file exists 142 | if: steps.check_phpunit.outcome == 'success' 143 | working-directory: apps/${{ env.APP_NAME }} 144 | run: composer run test:unit 145 | 146 | - name: Check PHPUnit integration script is defined 147 | id: check_integration 148 | continue-on-error: true 149 | working-directory: apps/${{ env.APP_NAME }} 150 | run: | 151 | composer run --list | grep "^ test:integration " | wc -l | grep 1 152 | 153 | - name: Run Nextcloud 154 | # Only run if phpunit integration config file exists 155 | if: steps.check_integration.outcome == 'success' 156 | run: php -S localhost:8080 & 157 | 158 | - name: PHPUnit integration 159 | # Only run if phpunit integration config file exists 160 | if: steps.check_integration.outcome == 'success' 161 | working-directory: apps/${{ env.APP_NAME }} 162 | run: composer run test:integration 163 | 164 | - name: Print logs 165 | if: always() 166 | run: | 167 | cat data/nextcloud.log 168 | 169 | - name: Skipped 170 | # Fail the action when neither unit nor integration tests ran 171 | if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure' 172 | run: | 173 | echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts' 174 | exit 1 175 | 176 | summary: 177 | permissions: 178 | contents: none 179 | runs-on: ubuntu-latest 180 | needs: [changes, phpunit-mysql] 181 | 182 | if: always() 183 | 184 | name: phpunit-mysql-summary 185 | 186 | steps: 187 | - name: Summary status 188 | run: if ${{ needs.changes.outputs.src != 'false' && needs.phpunit-mysql.result != 'success' }}; then exit 1; fi 189 | -------------------------------------------------------------------------------- /.github/workflows/phpunit-oci.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | name: PHPUnit OCI 7 | 8 | on: pull_request 9 | 10 | permissions: 11 | contents: read 12 | 13 | concurrency: 14 | group: phpunit-oci-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | matrix: 19 | runs-on: ubuntu-latest 20 | outputs: 21 | php-version: ${{ steps.versions.outputs.php-available-list }} 22 | server-max: ${{ steps.versions.outputs.branches-max-list }} 23 | steps: 24 | - name: Checkout app 25 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 26 | 27 | - name: Get version matrix 28 | id: versions 29 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 30 | 31 | changes: 32 | runs-on: ubuntu-latest 33 | 34 | outputs: 35 | src: ${{ steps.changes.outputs.src}} 36 | 37 | steps: 38 | - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 39 | id: changes 40 | continue-on-error: true 41 | with: 42 | filters: | 43 | src: 44 | - '.github/workflows/**' 45 | - 'appinfo/**' 46 | - 'lib/**' 47 | - 'templates/**' 48 | - 'tests/**' 49 | - 'vendor/**' 50 | - 'vendor-bin/**' 51 | - '.php-cs-fixer.dist.php' 52 | - 'composer.json' 53 | - 'composer.lock' 54 | 55 | phpunit-oci: 56 | runs-on: ubuntu-22.04 57 | 58 | needs: [changes, matrix] 59 | if: needs.changes.outputs.src != 'false' 60 | 61 | strategy: 62 | matrix: 63 | php-versions: ${{ fromJson(needs.matrix.outputs.php-version) }} 64 | server-versions: ${{ fromJson(needs.matrix.outputs.server-max) }} 65 | 66 | name: OCI PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} 67 | 68 | services: 69 | oracle: 70 | image: ghcr.io/gvenzl/oracle-xe:11 71 | 72 | # Provide passwords and other environment variables to container 73 | env: 74 | ORACLE_RANDOM_PASSWORD: true 75 | APP_USER: autotest 76 | APP_USER_PASSWORD: owncloud 77 | 78 | # Forward Oracle port 79 | ports: 80 | - 1521:1521/tcp 81 | 82 | # Provide healthcheck script options for startup 83 | options: >- 84 | --health-cmd healthcheck.sh 85 | --health-interval 10s 86 | --health-timeout 5s 87 | --health-retries 10 88 | 89 | steps: 90 | - name: Set app env 91 | run: | 92 | # Split and keep last 93 | echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV 94 | 95 | - name: Checkout server 96 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 97 | with: 98 | submodules: true 99 | repository: nextcloud/server 100 | ref: ${{ matrix.server-versions }} 101 | 102 | - name: Checkout app 103 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 104 | with: 105 | path: apps/${{ env.APP_NAME }} 106 | 107 | - name: Set up php ${{ matrix.php-versions }} 108 | uses: shivammathur/setup-php@a4e22b60bbb9c1021113f2860347b0759f66fe5d # v2 109 | with: 110 | php-version: ${{ matrix.php-versions }} 111 | # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation 112 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, oci8, inotify 113 | coverage: none 114 | ini-file: development 115 | env: 116 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 117 | 118 | - name: Check composer file existence 119 | id: check_composer 120 | uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 121 | with: 122 | files: apps/${{ env.APP_NAME }}/composer.json 123 | 124 | - name: Set up dependencies 125 | # Only run if phpunit config file exists 126 | if: steps.check_composer.outputs.files_exists == 'true' 127 | working-directory: apps/${{ env.APP_NAME }} 128 | run: composer i 129 | 130 | - name: Set up Nextcloud 131 | env: 132 | DB_PORT: 1521 133 | run: | 134 | mkdir data 135 | ./occ maintenance:install --verbose --database=oci --database-name=XE --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=autotest --database-pass=owncloud --admin-user admin --admin-pass admin 136 | ./occ app:enable --force ${{ env.APP_NAME }} 137 | 138 | - name: Check PHPUnit script is defined 139 | id: check_phpunit 140 | continue-on-error: true 141 | working-directory: apps/${{ env.APP_NAME }} 142 | run: | 143 | composer run --list | grep "^ test:unit " | wc -l | grep 1 144 | 145 | - name: PHPUnit 146 | # Only run if phpunit config file exists 147 | if: steps.check_phpunit.outcome == 'success' 148 | working-directory: apps/${{ env.APP_NAME }} 149 | run: composer run test:unit 150 | 151 | - name: Check PHPUnit integration script is defined 152 | id: check_integration 153 | continue-on-error: true 154 | working-directory: apps/${{ env.APP_NAME }} 155 | run: | 156 | composer run --list | grep "^ test:integration " | wc -l | grep 1 157 | 158 | - name: Run Nextcloud 159 | # Only run if phpunit integration config file exists 160 | if: steps.check_integration.outcome == 'success' 161 | run: php -S localhost:8080 & 162 | 163 | - name: PHPUnit integration 164 | # Only run if phpunit integration config file exists 165 | if: steps.check_integration.outcome == 'success' 166 | working-directory: apps/${{ env.APP_NAME }} 167 | run: composer run test:integration 168 | 169 | - name: Print logs 170 | if: always() 171 | run: | 172 | cat data/nextcloud.log 173 | 174 | - name: Skipped 175 | # Fail the action when neither unit nor integration tests ran 176 | if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure' 177 | run: | 178 | echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts' 179 | exit 1 180 | 181 | summary: 182 | permissions: 183 | contents: none 184 | runs-on: ubuntu-latest 185 | needs: [changes, phpunit-oci] 186 | 187 | if: always() 188 | 189 | name: phpunit-oci-summary 190 | 191 | steps: 192 | - name: Summary status 193 | run: if ${{ needs.changes.outputs.src != 'false' && needs.phpunit-oci.result != 'success' }}; then exit 1; fi 194 | -------------------------------------------------------------------------------- /.github/workflows/phpunit-pgsql.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | name: PHPUnit PostgreSQL 7 | 8 | on: pull_request 9 | 10 | permissions: 11 | contents: read 12 | 13 | concurrency: 14 | group: phpunit-pgsql-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | matrix: 19 | runs-on: ubuntu-latest 20 | outputs: 21 | php-version: ${{ steps.versions.outputs.php-available-list }} 22 | server-max: ${{ steps.versions.outputs.branches-max-list }} 23 | steps: 24 | - name: Checkout app 25 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 26 | 27 | - name: Get version matrix 28 | id: versions 29 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 30 | 31 | changes: 32 | runs-on: ubuntu-latest 33 | 34 | outputs: 35 | src: ${{ steps.changes.outputs.src}} 36 | 37 | steps: 38 | - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 39 | id: changes 40 | continue-on-error: true 41 | with: 42 | filters: | 43 | src: 44 | - '.github/workflows/**' 45 | - 'appinfo/**' 46 | - 'lib/**' 47 | - 'templates/**' 48 | - 'tests/**' 49 | - 'vendor/**' 50 | - 'vendor-bin/**' 51 | - '.php-cs-fixer.dist.php' 52 | - 'composer.json' 53 | - 'composer.lock' 54 | 55 | phpunit-pgsql: 56 | runs-on: ubuntu-latest 57 | 58 | needs: [changes, matrix] 59 | if: needs.changes.outputs.src != 'false' 60 | 61 | strategy: 62 | matrix: 63 | php-versions: ${{ fromJson(needs.matrix.outputs.php-version) }} 64 | server-versions: ${{ fromJson(needs.matrix.outputs.server-max) }} 65 | 66 | name: PostgreSQL PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} 67 | 68 | services: 69 | postgres: 70 | image: ghcr.io/nextcloud/continuous-integration-postgres-14:latest 71 | ports: 72 | - 4444:5432/tcp 73 | env: 74 | POSTGRES_USER: root 75 | POSTGRES_PASSWORD: rootpassword 76 | POSTGRES_DB: nextcloud 77 | options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5 78 | 79 | steps: 80 | - name: Set app env 81 | run: | 82 | # Split and keep last 83 | echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV 84 | 85 | - name: Checkout server 86 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 87 | with: 88 | submodules: true 89 | repository: nextcloud/server 90 | ref: ${{ matrix.server-versions }} 91 | 92 | - name: Checkout app 93 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 94 | with: 95 | path: apps/${{ env.APP_NAME }} 96 | 97 | - name: Set up php ${{ matrix.php-versions }} 98 | uses: shivammathur/setup-php@a4e22b60bbb9c1021113f2860347b0759f66fe5d # v2 99 | with: 100 | php-version: ${{ matrix.php-versions }} 101 | # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation 102 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql, inotify 103 | coverage: none 104 | ini-file: development 105 | env: 106 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 107 | 108 | - name: Check composer file existence 109 | id: check_composer 110 | uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 111 | with: 112 | files: apps/${{ env.APP_NAME }}/composer.json 113 | 114 | - name: Set up dependencies 115 | # Only run if phpunit config file exists 116 | if: steps.check_composer.outputs.files_exists == 'true' 117 | working-directory: apps/${{ env.APP_NAME }} 118 | run: composer i 119 | 120 | - name: Set up Nextcloud 121 | env: 122 | DB_PORT: 4444 123 | run: | 124 | mkdir data 125 | ./occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin 126 | ./occ app:enable --force ${{ env.APP_NAME }} 127 | 128 | - name: Check PHPUnit script is defined 129 | id: check_phpunit 130 | continue-on-error: true 131 | working-directory: apps/${{ env.APP_NAME }} 132 | run: | 133 | composer run --list | grep "^ test:unit " | wc -l | grep 1 134 | 135 | - name: PHPUnit 136 | # Only run if phpunit config file exists 137 | if: steps.check_phpunit.outcome == 'success' 138 | working-directory: apps/${{ env.APP_NAME }} 139 | run: composer run test:unit 140 | 141 | - name: Check PHPUnit integration script is defined 142 | id: check_integration 143 | continue-on-error: true 144 | working-directory: apps/${{ env.APP_NAME }} 145 | run: | 146 | composer run --list | grep "^ test:integration " | wc -l | grep 1 147 | 148 | - name: Run Nextcloud 149 | # Only run if phpunit integration config file exists 150 | if: steps.check_integration.outcome == 'success' 151 | run: php -S localhost:8080 & 152 | 153 | - name: PHPUnit integration 154 | # Only run if phpunit integration config file exists 155 | if: steps.check_integration.outcome == 'success' 156 | working-directory: apps/${{ env.APP_NAME }} 157 | run: composer run test:integration 158 | 159 | - name: Print logs 160 | if: always() 161 | run: | 162 | cat data/nextcloud.log 163 | 164 | - name: Skipped 165 | # Fail the action when neither unit nor integration tests ran 166 | if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure' 167 | run: | 168 | echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts' 169 | exit 1 170 | 171 | summary: 172 | permissions: 173 | contents: none 174 | runs-on: ubuntu-latest 175 | needs: [changes, phpunit-pgsql] 176 | 177 | if: always() 178 | 179 | name: phpunit-pgsql-summary 180 | 181 | steps: 182 | - name: Summary status 183 | run: if ${{ needs.changes.outputs.src != 'false' && needs.phpunit-pgsql.result != 'success' }}; then exit 1; fi 184 | -------------------------------------------------------------------------------- /.github/workflows/phpunit-sqlite.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | name: PHPUnit SQLite 7 | 8 | on: pull_request 9 | 10 | permissions: 11 | contents: read 12 | 13 | concurrency: 14 | group: phpunit-sqlite-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | matrix: 19 | runs-on: ubuntu-latest 20 | outputs: 21 | php-version: ${{ steps.versions.outputs.php-available-list }} 22 | server-max: ${{ steps.versions.outputs.branches-max-list }} 23 | steps: 24 | - name: Checkout app 25 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 26 | 27 | - name: Get version matrix 28 | id: versions 29 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 30 | 31 | changes: 32 | runs-on: ubuntu-latest 33 | 34 | outputs: 35 | src: ${{ steps.changes.outputs.src}} 36 | 37 | steps: 38 | - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 39 | id: changes 40 | continue-on-error: true 41 | with: 42 | filters: | 43 | src: 44 | - '.github/workflows/**' 45 | - 'appinfo/**' 46 | - 'lib/**' 47 | - 'templates/**' 48 | - 'tests/**' 49 | - 'vendor/**' 50 | - 'vendor-bin/**' 51 | - '.php-cs-fixer.dist.php' 52 | - 'composer.json' 53 | - 'composer.lock' 54 | 55 | phpunit-sqlite: 56 | runs-on: ubuntu-latest 57 | 58 | needs: [changes, matrix] 59 | if: needs.changes.outputs.src != 'false' 60 | 61 | strategy: 62 | matrix: 63 | php-versions: ${{ fromJson(needs.matrix.outputs.php-version) }} 64 | server-versions: ${{ fromJson(needs.matrix.outputs.server-max) }} 65 | 66 | name: SQLite PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} 67 | 68 | steps: 69 | - name: Set app env 70 | run: | 71 | # Split and keep last 72 | echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV 73 | 74 | - name: Checkout server 75 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 76 | with: 77 | submodules: true 78 | repository: nextcloud/server 79 | ref: ${{ matrix.server-versions }} 80 | 81 | - name: Checkout app 82 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 83 | with: 84 | path: apps/${{ env.APP_NAME }} 85 | 86 | - name: Set up php ${{ matrix.php-versions }} 87 | uses: shivammathur/setup-php@a4e22b60bbb9c1021113f2860347b0759f66fe5d # v2 88 | with: 89 | php-version: ${{ matrix.php-versions }} 90 | # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation 91 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite, inotify 92 | coverage: none 93 | ini-file: development 94 | env: 95 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 96 | 97 | - name: Check composer file existence 98 | id: check_composer 99 | uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 100 | with: 101 | files: apps/${{ env.APP_NAME }}/composer.json 102 | 103 | - name: Set up dependencies 104 | # Only run if phpunit config file exists 105 | if: steps.check_composer.outputs.files_exists == 'true' 106 | working-directory: apps/${{ env.APP_NAME }} 107 | run: composer i 108 | 109 | - name: Set up Nextcloud 110 | env: 111 | DB_PORT: 4444 112 | run: | 113 | mkdir data 114 | ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin 115 | ./occ app:enable --force ${{ env.APP_NAME }} 116 | 117 | - name: Check PHPUnit script is defined 118 | id: check_phpunit 119 | continue-on-error: true 120 | working-directory: apps/${{ env.APP_NAME }} 121 | run: | 122 | composer run --list | grep "^ test:unit " | wc -l | grep 1 123 | 124 | - name: PHPUnit 125 | # Only run if phpunit config file exists 126 | if: steps.check_phpunit.outcome == 'success' 127 | working-directory: apps/${{ env.APP_NAME }} 128 | run: composer run test:unit 129 | 130 | - name: Check PHPUnit integration script is defined 131 | id: check_integration 132 | continue-on-error: true 133 | working-directory: apps/${{ env.APP_NAME }} 134 | run: | 135 | composer run --list | grep "^ test:integration " | wc -l | grep 1 136 | 137 | - name: Run Nextcloud 138 | # Only run if phpunit integration config file exists 139 | if: steps.check_integration.outcome == 'success' 140 | run: php -S localhost:8080 & 141 | 142 | - name: PHPUnit integration 143 | # Only run if phpunit integration config file exists 144 | if: steps.check_integration.outcome == 'success' 145 | working-directory: apps/${{ env.APP_NAME }} 146 | run: composer run test:integration 147 | 148 | - name: Print logs 149 | if: always() 150 | run: | 151 | cat data/nextcloud.log 152 | 153 | - name: Skipped 154 | # Fail the action when neither unit nor integration tests ran 155 | if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure' 156 | run: | 157 | echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts' 158 | exit 1 159 | 160 | summary: 161 | permissions: 162 | contents: none 163 | runs-on: ubuntu-latest 164 | needs: [changes, phpunit-sqlite] 165 | 166 | if: always() 167 | 168 | name: phpunit-sqlite-summary 169 | 170 | steps: 171 | - name: Summary status 172 | run: if ${{ needs.changes.outputs.src != 'false' && needs.phpunit-sqlite.result != 'success' }}; then exit 1; fi 173 | -------------------------------------------------------------------------------- /.github/workflows/psalm-matrix.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | name: Static analysis 7 | 8 | on: pull_request 9 | 10 | concurrency: 11 | group: psalm-${{ github.head_ref || github.run_id }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | matrix: 16 | runs-on: ubuntu-latest 17 | outputs: 18 | ocp-matrix: ${{ steps.versions.outputs.ocp-matrix }} 19 | steps: 20 | - name: Checkout app 21 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 22 | - name: Get version matrix 23 | id: versions 24 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 25 | 26 | static-analysis: 27 | runs-on: ubuntu-latest 28 | needs: matrix 29 | strategy: 30 | # do not stop on another job's failure 31 | fail-fast: false 32 | matrix: ${{ fromJson(needs.matrix.outputs.ocp-matrix) }} 33 | 34 | name: static-psalm-analysis ${{ matrix.ocp-version }} 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 38 | 39 | - name: Set up php${{ matrix.php-versions }} 40 | uses: shivammathur/setup-php@a4e22b60bbb9c1021113f2860347b0759f66fe5d # v2 41 | with: 42 | php-version: ${{ matrix.php-versions }} 43 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite, inotify 44 | coverage: none 45 | ini-file: development 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | 49 | - name: Install dependencies 50 | run: composer i 51 | 52 | - name: Install dependencies 53 | run: composer require --dev nextcloud/ocp:${{ matrix.ocp-version }} --ignore-platform-reqs --with-dependencies 54 | 55 | - name: Run coding standards check 56 | run: composer run psalm 57 | 58 | summary: 59 | runs-on: ubuntu-latest 60 | needs: static-analysis 61 | 62 | if: always() 63 | 64 | name: static-psalm-analysis-summary 65 | 66 | steps: 67 | - name: Summary status 68 | run: if ${{ needs.static-analysis.result != 'success' }}; then exit 1; fi 69 | -------------------------------------------------------------------------------- /.github/workflows/update-nextcloud-ocp.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | name: Update nextcloud/ocp 7 | 8 | on: 9 | workflow_dispatch: 10 | schedule: 11 | - cron: "5 2 * * 0" 12 | 13 | jobs: 14 | update-nextcloud-ocp: 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | branches: ['main', 'master', 'stable29', 'stable28', 'stable27'] 21 | 22 | name: update-nextcloud-ocp-${{ matrix.branches }} 23 | 24 | steps: 25 | - id: checkout 26 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 27 | with: 28 | ref: ${{ matrix.branches }} 29 | submodules: true 30 | continue-on-error: true 31 | 32 | - name: Set up php8.2 33 | if: steps.checkout.outcome == 'success' 34 | uses: shivammathur/setup-php@a4e22b60bbb9c1021113f2860347b0759f66fe5d # v2 35 | with: 36 | php-version: 8.2 37 | # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation 38 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite 39 | coverage: none 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | 43 | - name: Read codeowners 44 | if: steps.checkout.outcome == 'success' 45 | id: codeowners 46 | run: | 47 | grep '/appinfo/info.xml' .github/CODEOWNERS | cut -f 2- -d ' ' | xargs | awk '{ print "codeowners="$0 }' >> $GITHUB_OUTPUT 48 | continue-on-error: true 49 | 50 | - name: Composer install 51 | if: steps.checkout.outcome == 'success' 52 | run: composer install 53 | 54 | - name: Composer update nextcloud/ocp 55 | id: update_branch 56 | if: ${{ steps.checkout.outcome == 'success' && matrix.branches != 'main' }} 57 | run: composer require --dev nextcloud/ocp:dev-${{ matrix.branches }} 58 | 59 | - name: Raise on issue on failure 60 | uses: dacbd/create-issue-action@cdb57ab6ff8862aa09fee2be6ba77a59581921c2 # v2.0.0 61 | if: ${{ steps.checkout.outcome == 'success' && failure() && steps.update_branch.conclusion == 'failure' }} 62 | with: 63 | token: ${{ secrets.GITHUB_TOKEN }} 64 | title: Failed to update nextcloud/ocp package on branch ${{ matrix.branches }} 65 | body: Please check the output of the GitHub action and manually resolve the issues
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
${{ steps.codeowners.outputs.codeowners }} 66 | 67 | - name: Composer update nextcloud/ocp 68 | id: update_main 69 | if: ${{ steps.checkout.outcome == 'success' && matrix.branches == 'main' }} 70 | run: composer require --dev nextcloud/ocp:dev-master 71 | 72 | - name: Raise on issue on failure 73 | uses: dacbd/create-issue-action@cdb57ab6ff8862aa09fee2be6ba77a59581921c2 # v2.0.0 74 | if: ${{ steps.checkout.outcome == 'success' && failure() && steps.update_main.conclusion == 'failure' }} 75 | with: 76 | token: ${{ secrets.GITHUB_TOKEN }} 77 | title: Failed to update nextcloud/ocp package on branch ${{ matrix.branches }} 78 | body: Please check the output of the GitHub action and manually resolve the issues
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
${{ steps.codeowners.outputs.codeowners }} 79 | 80 | - name: Reset checkout 3rdparty 81 | if: steps.checkout.outcome == 'success' 82 | run: | 83 | git clean -f 3rdparty 84 | git checkout 3rdparty 85 | continue-on-error: true 86 | 87 | - name: Reset checkout vendor 88 | if: steps.checkout.outcome == 'success' 89 | run: | 90 | git clean -f vendor 91 | git checkout vendor 92 | continue-on-error: true 93 | 94 | - name: Reset checkout vendor-bin 95 | if: steps.checkout.outcome == 'success' 96 | run: | 97 | git clean -f vendor-bin 98 | git checkout vendor-bin 99 | continue-on-error: true 100 | 101 | - name: Create Pull Request 102 | if: steps.checkout.outcome == 'success' 103 | uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc # v6.0.1 104 | with: 105 | token: ${{ secrets.COMMAND_BOT_PAT }} 106 | commit-message: "chore(dev-deps): Bump nextcloud/ocp package" 107 | committer: GitHub 108 | author: nextcloud-command 109 | signoff: true 110 | branch: automated/noid/${{ matrix.branches }}-update-nextcloud-ocp 111 | title: "[${{ matrix.branches }}] Update nextcloud/ocp dependency" 112 | body: | 113 | Auto-generated update of [nextcloud/ocp](https://github.com/nextcloud-deps/ocp/) dependency 114 | labels: | 115 | dependencies 116 | 3. to review 117 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | vendor 3 | node_modules 4 | *.cache 5 | -------------------------------------------------------------------------------- /.nextcloudignore: -------------------------------------------------------------------------------- 1 | .drone 2 | .git 3 | .github 4 | .gitignore 5 | .scrutinizer.yml 6 | .travis.yml 7 | .tx 8 | .env 9 | krankerl.toml 10 | screenshots 11 | .nextcloudignore 12 | target 13 | tests 14 | composer.* 15 | .php_cs.dist 16 | psalm*.xml 17 | Dockerfile 18 | node_modules 19 | Makefile 20 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | getFinder() 15 | ->ignoreVCSIgnored(true) 16 | ->notPath('build') 17 | ->notPath('tests/stub.phpstub') 18 | ->notPath('l10n') 19 | ->notPath('src') 20 | ->notPath('vendor') 21 | ->in(__DIR__); 22 | return $config; 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.1.9 4 | - Compatible with Nextcloud 20 and 21 5 | 6 | ## v0.1.8 7 | - Compatible with Nextcloud 19 8 | 9 | ## v0.1.7 10 | - Compatible with Nextcloud 18 11 | 12 | ## v0.1.6 13 | - Improved handling of interrupted `stream_select` - @tmolitor-stud-tu 14 | 15 | ## v0.1.5 16 | - Compatible with Nextcloud 17 17 | 18 | ## v0.1.4 19 | - Support Nextcloud 16 20 | 21 | ## v0.1.3 22 | - Prevent breaking when files_external is not enabled 23 | - Compatible with Nextcloud 15 24 | 25 | ## v0.1.2 26 | - Fix issue where stream select generates large amount of error logs 27 | - Remove NC11 compatibility 28 | 29 | 30 | 31 | ## v0.1.1 32 | - Mark as compatible with NC13 and NC14 33 | 34 | ## v0.1.0 35 | Initial version with support for Nextcloud 11 and 12 36 | 37 | 38 | --- 39 | 40 | Generated by [changelog](https://github.com/gluons/changelog). 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # files_inotify 2 | 3 | [![PHPUnit](https://github.com/icewind1991/files_inotify/actions/workflows/phpunit.yml/badge.svg)](https://github.com/icewind1991/files_inotify/actions) 4 | 5 | Adds support detecting changes in local external storages with occ files_external:notify 6 | 7 | ## Requirements 8 | 9 | This app uses the php inotify extensions which is required to be installed before this app can be enabled. 10 | The php inotify extension can be installed from your distribution's package or [pecl](https://pecl.php.net/package/inotify). 11 | 12 | ## Usage 13 | 14 | To detect changes you need to run the `files_external:notify` command for the configured local external storage. 15 | 16 | Note that this command runs continuously and should be started in the background by an init system or other task manager for best usage. 17 | 18 | Find the id of the external storage that should be checked 19 | 20 | ``` 21 | occ files_external:list 22 | 23 | +----------+-------------+------------+-----------------------+------------------------+------------+------------------+-------------------+ 24 | | Mount ID | Mount Point | Storage | Authentication Type | Configuration | Options | Applicable Users | Applicable Groups | 25 | +----------+-------------+------------+-----------------------+------------------------+------------+------------------+-------------------+ 26 | | 5 | /test | Local | None | datadir: "....." | | | | 27 | +----------+-------------+------------+-----------------------+------------------------+------------+------------------+-------------------+ 28 | 29 | ``` 30 | 31 | Run the filesystem watch 32 | 33 | ``` 34 | occ files_external:notify -v 5 35 | ``` 36 | 37 | ## Scalability notes 38 | 39 | Due to the nature of `inotify` the memory requirements of listening for changes 40 | scales linearly with the number of folders in the storage. 41 | 42 | Additionally it's required to configure `fs.inotify.max_user_watches` on the server 43 | to be higher than the total number of folders being watched. 44 | -------------------------------------------------------------------------------- /appinfo/info.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | files_inotify 5 | INotify file watcher 6 | Adds support detecting changes in local external storages 7 | 12 | 13 | 0.2.3 14 | agpl 15 | Robin Appelman 16 | Files_INotify 17 | 18 | 19 | 20 | 21 | files 22 | 23 | https://github.com/icewind1991/files_inotify 24 | https://github.com/icewind1991/files_inotify/issues 25 | 26 | https://github.com/icewind1991/files_inotify.git 27 | 28 | 29 | 30 | inotify 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "project", 3 | "require-dev": { 4 | "nextcloud/ocp": "dev-master" 5 | }, 6 | "require": { 7 | "bamarni/composer-bin-plugin": "^1.8" 8 | }, 9 | "license": "AGPLv3", 10 | "scripts": { 11 | "post-install-cmd": [ 12 | "@composer bin all install --ansi" 13 | ], 14 | "post-update-cmd": [ 15 | "@composer bin all update --ansi" 16 | ], 17 | "lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -not -path './tests/integration/vendor/*' -print0 | xargs -0 -n1 php -l", 18 | "cs:check": "php-cs-fixer fix --dry-run --diff", 19 | "cs:fix": "php-cs-fixer fix", 20 | "test:unit": "phpunit -c tests/phpunit.xml", 21 | "psalm": "psalm --threads=1", 22 | "psalm:update-baseline": "psalm --threads=1 --update-baseline", 23 | "psalm:clear": "psalm --clear-cache && psalm --clear-global-cache", 24 | "psalm:fix": "psalm --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType" 25 | }, 26 | "config": { 27 | "allow-plugins": { 28 | "bamarni/composer-bin-plugin": true 29 | }, 30 | "platform": { 31 | "php": "8.1" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "79347efa5966aec068b8ece538e4c21b", 8 | "packages": [ 9 | { 10 | "name": "bamarni/composer-bin-plugin", 11 | "version": "1.8.2", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/bamarni/composer-bin-plugin.git", 15 | "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", 20 | "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "composer-plugin-api": "^2.0", 25 | "php": "^7.2.5 || ^8.0" 26 | }, 27 | "require-dev": { 28 | "composer/composer": "^2.0", 29 | "ext-json": "*", 30 | "phpstan/extension-installer": "^1.1", 31 | "phpstan/phpstan": "^1.8", 32 | "phpstan/phpstan-phpunit": "^1.1", 33 | "phpunit/phpunit": "^8.5 || ^9.5", 34 | "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", 35 | "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", 36 | "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" 37 | }, 38 | "type": "composer-plugin", 39 | "extra": { 40 | "class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin" 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "Bamarni\\Composer\\Bin\\": "src" 45 | } 46 | }, 47 | "notification-url": "https://packagist.org/downloads/", 48 | "license": [ 49 | "MIT" 50 | ], 51 | "description": "No conflicts for your bin dependencies", 52 | "keywords": [ 53 | "composer", 54 | "conflict", 55 | "dependency", 56 | "executable", 57 | "isolation", 58 | "tool" 59 | ], 60 | "support": { 61 | "issues": "https://github.com/bamarni/composer-bin-plugin/issues", 62 | "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2" 63 | }, 64 | "time": "2022-10-31T08:38:03+00:00" 65 | } 66 | ], 67 | "packages-dev": [ 68 | { 69 | "name": "nextcloud/ocp", 70 | "version": "dev-master", 71 | "source": { 72 | "type": "git", 73 | "url": "https://github.com/nextcloud-deps/ocp.git", 74 | "reference": "dc5473e3895d859535de45eab15d2095cb8eafe4" 75 | }, 76 | "dist": { 77 | "type": "zip", 78 | "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/dc5473e3895d859535de45eab15d2095cb8eafe4", 79 | "reference": "dc5473e3895d859535de45eab15d2095cb8eafe4", 80 | "shasum": "" 81 | }, 82 | "require": { 83 | "php": "~8.1 || ~8.2 || ~8.3 || ~8.4", 84 | "psr/clock": "^1.0", 85 | "psr/container": "^2.0.2", 86 | "psr/event-dispatcher": "^1.0", 87 | "psr/log": "^3.0.2" 88 | }, 89 | "default-branch": true, 90 | "type": "library", 91 | "extra": { 92 | "branch-alias": { 93 | "dev-master": "32.0.0-dev" 94 | } 95 | }, 96 | "notification-url": "https://packagist.org/downloads/", 97 | "license": [ 98 | "AGPL-3.0-or-later" 99 | ], 100 | "authors": [ 101 | { 102 | "name": "Christoph Wurst", 103 | "email": "christoph@winzerhof-wurst.at" 104 | }, 105 | { 106 | "name": "Joas Schilling", 107 | "email": "coding@schilljs.com" 108 | } 109 | ], 110 | "description": "Composer package containing Nextcloud's public OCP API and the unstable NCU API", 111 | "support": { 112 | "issues": "https://github.com/nextcloud-deps/ocp/issues", 113 | "source": "https://github.com/nextcloud-deps/ocp/tree/master" 114 | }, 115 | "time": "2025-03-11T00:46:19+00:00" 116 | }, 117 | { 118 | "name": "psr/clock", 119 | "version": "1.0.0", 120 | "source": { 121 | "type": "git", 122 | "url": "https://github.com/php-fig/clock.git", 123 | "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" 124 | }, 125 | "dist": { 126 | "type": "zip", 127 | "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", 128 | "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", 129 | "shasum": "" 130 | }, 131 | "require": { 132 | "php": "^7.0 || ^8.0" 133 | }, 134 | "type": "library", 135 | "autoload": { 136 | "psr-4": { 137 | "Psr\\Clock\\": "src/" 138 | } 139 | }, 140 | "notification-url": "https://packagist.org/downloads/", 141 | "license": [ 142 | "MIT" 143 | ], 144 | "authors": [ 145 | { 146 | "name": "PHP-FIG", 147 | "homepage": "https://www.php-fig.org/" 148 | } 149 | ], 150 | "description": "Common interface for reading the clock.", 151 | "homepage": "https://github.com/php-fig/clock", 152 | "keywords": [ 153 | "clock", 154 | "now", 155 | "psr", 156 | "psr-20", 157 | "time" 158 | ], 159 | "support": { 160 | "issues": "https://github.com/php-fig/clock/issues", 161 | "source": "https://github.com/php-fig/clock/tree/1.0.0" 162 | }, 163 | "time": "2022-11-25T14:36:26+00:00" 164 | }, 165 | { 166 | "name": "psr/container", 167 | "version": "2.0.2", 168 | "source": { 169 | "type": "git", 170 | "url": "https://github.com/php-fig/container.git", 171 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" 172 | }, 173 | "dist": { 174 | "type": "zip", 175 | "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", 176 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", 177 | "shasum": "" 178 | }, 179 | "require": { 180 | "php": ">=7.4.0" 181 | }, 182 | "type": "library", 183 | "extra": { 184 | "branch-alias": { 185 | "dev-master": "2.0.x-dev" 186 | } 187 | }, 188 | "autoload": { 189 | "psr-4": { 190 | "Psr\\Container\\": "src/" 191 | } 192 | }, 193 | "notification-url": "https://packagist.org/downloads/", 194 | "license": [ 195 | "MIT" 196 | ], 197 | "authors": [ 198 | { 199 | "name": "PHP-FIG", 200 | "homepage": "https://www.php-fig.org/" 201 | } 202 | ], 203 | "description": "Common Container Interface (PHP FIG PSR-11)", 204 | "homepage": "https://github.com/php-fig/container", 205 | "keywords": [ 206 | "PSR-11", 207 | "container", 208 | "container-interface", 209 | "container-interop", 210 | "psr" 211 | ], 212 | "support": { 213 | "issues": "https://github.com/php-fig/container/issues", 214 | "source": "https://github.com/php-fig/container/tree/2.0.2" 215 | }, 216 | "time": "2021-11-05T16:47:00+00:00" 217 | }, 218 | { 219 | "name": "psr/event-dispatcher", 220 | "version": "1.0.0", 221 | "source": { 222 | "type": "git", 223 | "url": "https://github.com/php-fig/event-dispatcher.git", 224 | "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" 225 | }, 226 | "dist": { 227 | "type": "zip", 228 | "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", 229 | "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", 230 | "shasum": "" 231 | }, 232 | "require": { 233 | "php": ">=7.2.0" 234 | }, 235 | "type": "library", 236 | "extra": { 237 | "branch-alias": { 238 | "dev-master": "1.0.x-dev" 239 | } 240 | }, 241 | "autoload": { 242 | "psr-4": { 243 | "Psr\\EventDispatcher\\": "src/" 244 | } 245 | }, 246 | "notification-url": "https://packagist.org/downloads/", 247 | "license": [ 248 | "MIT" 249 | ], 250 | "authors": [ 251 | { 252 | "name": "PHP-FIG", 253 | "homepage": "http://www.php-fig.org/" 254 | } 255 | ], 256 | "description": "Standard interfaces for event handling.", 257 | "keywords": [ 258 | "events", 259 | "psr", 260 | "psr-14" 261 | ], 262 | "support": { 263 | "issues": "https://github.com/php-fig/event-dispatcher/issues", 264 | "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" 265 | }, 266 | "time": "2019-01-08T18:20:26+00:00" 267 | }, 268 | { 269 | "name": "psr/log", 270 | "version": "3.0.2", 271 | "source": { 272 | "type": "git", 273 | "url": "https://github.com/php-fig/log.git", 274 | "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" 275 | }, 276 | "dist": { 277 | "type": "zip", 278 | "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", 279 | "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", 280 | "shasum": "" 281 | }, 282 | "require": { 283 | "php": ">=8.0.0" 284 | }, 285 | "type": "library", 286 | "extra": { 287 | "branch-alias": { 288 | "dev-master": "3.x-dev" 289 | } 290 | }, 291 | "autoload": { 292 | "psr-4": { 293 | "Psr\\Log\\": "src" 294 | } 295 | }, 296 | "notification-url": "https://packagist.org/downloads/", 297 | "license": [ 298 | "MIT" 299 | ], 300 | "authors": [ 301 | { 302 | "name": "PHP-FIG", 303 | "homepage": "https://www.php-fig.org/" 304 | } 305 | ], 306 | "description": "Common interface for logging libraries", 307 | "homepage": "https://github.com/php-fig/log", 308 | "keywords": [ 309 | "log", 310 | "psr", 311 | "psr-3" 312 | ], 313 | "support": { 314 | "source": "https://github.com/php-fig/log/tree/3.0.2" 315 | }, 316 | "time": "2024-09-11T13:17:53+00:00" 317 | } 318 | ], 319 | "aliases": [], 320 | "minimum-stability": "stable", 321 | "stability-flags": { 322 | "nextcloud/ocp": 20 323 | }, 324 | "prefer-stable": false, 325 | "prefer-lowest": false, 326 | "platform": {}, 327 | "platform-dev": {}, 328 | "platform-overrides": { 329 | "php": "8.1" 330 | }, 331 | "plugin-api-version": "2.6.0" 332 | } 333 | -------------------------------------------------------------------------------- /krankerl.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | before_cmds = [ 3 | 4 | ] 5 | -------------------------------------------------------------------------------- /lib/AppInfo/Application.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * @license GNU AGPL version 3 or any later version 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public License 19 | * along with this program. If not, see . 20 | * 21 | */ 22 | 23 | namespace OCA\Files_INotify\AppInfo; 24 | 25 | use OCA\Files_External\Service\BackendService; 26 | use OCA\Files_INotify\INotifyBackendProvider; 27 | use OCP\AppFramework\App; 28 | use OCP\AppFramework\Bootstrap\IBootContext; 29 | use OCP\AppFramework\Bootstrap\IBootstrap; 30 | use OCP\AppFramework\Bootstrap\IRegistrationContext; 31 | use OCP\AppFramework\IAppContainer; 32 | use OCP\EventDispatcher\IEventDispatcher; 33 | 34 | class Application extends App implements IBootstrap { 35 | public function __construct(array $urlParams = []) { 36 | parent::__construct('files_inotify', $urlParams); 37 | } 38 | 39 | public function register(IRegistrationContext $context): void { 40 | } 41 | 42 | public function boot(IBootContext $context): void { 43 | $context->injectFn([$this, 'registerBackendDependents']); 44 | } 45 | 46 | public function registerBackendDependents(IAppContainer $appContainer, IEventDispatcher $dispatcher) { 47 | $dispatcher->addListener( 48 | 'OCA\\Files_External::loadAdditionalBackends', 49 | function () use ($appContainer) { 50 | if (\OC::$CLI && class_exists(BackendService::class)) { 51 | // we can't inject these 2, since they would cause hard errors if files_external is not enabled 52 | /** @var BackendService $backendService */ 53 | $backendService = $appContainer->get(BackendService::class); 54 | /** @var INotifyBackendProvider $backendProvider */ 55 | $backendProvider = $appContainer->get(INotifyBackendProvider::class); 56 | $backendService->registerBackendProvider($backendProvider); 57 | } 58 | } 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/INotifyBackendProvider.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * @license GNU AGPL version 3 or any later version 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | * 22 | */ 23 | 24 | namespace OCA\Files_INotify; 25 | 26 | use OCA\Files_External\Lib\Config\IBackendProvider; 27 | use OCA\Files_INotify\Storage\INotifyBackend; 28 | 29 | class INotifyBackendProvider implements IBackendProvider { 30 | /** @var INotifyBackend */ 31 | private $notifyBackend; 32 | 33 | public function __construct(INotifyBackend $notifyBackend) { 34 | $this->notifyBackend = $notifyBackend; 35 | } 36 | 37 | public function getBackends() { 38 | return [ 39 | $this->notifyBackend 40 | ]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/LoadBackendListener.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * @license GNU AGPL version 3 or any later version 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | * 22 | */ 23 | 24 | namespace OCA\FilesInotify\lib; 25 | 26 | use OCA\Files_External\Service\BackendService; 27 | use OCA\Files_INotify\INotifyBackendProvider; 28 | use OCP\EventDispatcher\Event; 29 | use OCP\EventDispatcher\IEventListener; 30 | 31 | /** 32 | * @template-implements IEventListener 33 | */ 34 | class LoadBackendListener implements IEventListener { 35 | private BackendService $backendService; 36 | private INotifyBackendProvider $backendProvider; 37 | 38 | public function __construct(BackendService $backendService, INotifyBackendProvider $backendProvider) { 39 | $this->backendService = $backendService; 40 | $this->backendProvider = $backendProvider; 41 | } 42 | 43 | public function handle(Event $event): void { 44 | if (\OC::$CLI) { 45 | $this->backendService->registerBackendProvider($this->backendProvider); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Storage/INotifyBackend.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * @license GNU AGPL version 3 or any later version 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public License 19 | * along with this program. If not, see . 20 | * 21 | */ 22 | 23 | namespace OCA\Files_INotify\Storage; 24 | 25 | use OCA\Files_External\Lib\Auth\NullMechanism; 26 | use OCA\Files_External\Lib\Backend\Local; 27 | use OCP\IL10N; 28 | 29 | class INotifyBackend extends Local { 30 | public function __construct(IL10N $l, NullMechanism $legacyAuth) { 31 | parent::__construct($l, $legacyAuth); 32 | $this->setStorageClass(INotifyWrapper::class); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/Storage/INotifyWrapper.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * @license GNU AGPL version 3 or any later version 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public License 19 | * along with this program. If not, see . 20 | * 21 | */ 22 | 23 | namespace OCA\Files_INotify\Storage; 24 | 25 | use OC\Files\Storage\Local; 26 | use OCP\Files\Notify\IChange; 27 | use OCP\Files\Notify\IRenameChange; 28 | use OCP\Files\Storage\INotifyStorage; 29 | 30 | class INotifyWrapper extends Local implements INotifyStorage { 31 | public function listen($path, callable $callback) { 32 | $this->notify($path)->listen(function (IChange $change) use ($callback) { 33 | if ($change instanceof IRenameChange) { 34 | return $callback($change->getType(), $change->getPath(), $change->getTargetPath()); 35 | } else { 36 | return $callback($change->getType(), $change->getPath()); 37 | } 38 | }); 39 | } 40 | 41 | public function notify($path) { 42 | return new NotifyHandler($this->datadir); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Storage/NotifyHandler.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * @license GNU AGPL version 3 or any later version 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public License 19 | * along with this program. If not, see . 20 | * 21 | */ 22 | 23 | namespace OCA\Files_INotify\Storage; 24 | 25 | use OC\Files\Notify\Change; 26 | use OC\Files\Notify\RenameChange; 27 | use OCP\Files\Notify\IChange; 28 | use OCP\Files\Notify\INotifyHandler; 29 | 30 | class NotifyHandler implements INotifyHandler { 31 | /** @var resource|null */ 32 | private $fd; 33 | 34 | /** @var string */ 35 | private $basePath; 36 | 37 | /** @var string[] */ 38 | private $pathMap = []; 39 | 40 | /** @var string[][] */ 41 | private $moveMap = []; 42 | 43 | /** 44 | * @param string $basePath 45 | */ 46 | public function __construct($basePath) { 47 | $this->fd = inotify_init(); 48 | 49 | $this->basePath = rtrim($basePath, '/'); 50 | $this->register(); 51 | } 52 | 53 | private function getDirectoryIterator(string $path): \Iterator { 54 | return new \RecursiveIteratorIterator( 55 | new \RecursiveDirectoryIterator($path, 56 | \FilesystemIterator::CURRENT_AS_PATHNAME + \FilesystemIterator::SKIP_DOTS), 57 | \RecursiveIteratorIterator::SELF_FIRST, \RecursiveIteratorIterator::CATCH_GET_CHILD); 58 | } 59 | 60 | private function register(): void { 61 | $iterator = $this->getDirectoryIterator($this->basePath); 62 | 63 | $this->watchPath($this->basePath); 64 | foreach ($iterator as $path) { 65 | if (is_dir($path)) { 66 | $this->watchPath($path); 67 | } 68 | } 69 | } 70 | 71 | private function watchPath(string $path): void { 72 | if ($this->fd === null) { 73 | return; 74 | } 75 | $descriptor = inotify_add_watch($this->fd, $path, 76 | \IN_MODIFY + \IN_CREATE + \IN_MOVED_FROM + \IN_MOVED_TO + \IN_DELETE); 77 | $this->pathMap[$descriptor] = $path; 78 | } 79 | 80 | /** 81 | * @return IChange[] 82 | */ 83 | public function getChanges(): array { 84 | if ($this->fd === null) { 85 | return []; 86 | } 87 | stream_set_blocking($this->fd, false); 88 | return $this->deduplicateEvents($this->readEvents()); 89 | } 90 | 91 | /** 92 | * @return IChange[] 93 | */ 94 | private function readEvents(): array { 95 | if ($this->fd === null) { 96 | return []; 97 | } 98 | $events = inotify_read($this->fd); 99 | $parsedEvents = array_map([$this, 'parseEvent'], $events); 100 | return $this->deduplicateEvents(call_user_func_array('array_merge', $parsedEvents)); 101 | } 102 | 103 | /** 104 | * @param IChange[] $events 105 | * @return IChange[] 106 | */ 107 | private function deduplicateEvents(array $events): array { 108 | /** @var null|IChange $lastEvent */ 109 | $lastEvent = null; 110 | $filteredEvents = []; 111 | 112 | foreach ($events as $event) { 113 | if ($lastEvent === null || ($event->getPath() !== $lastEvent->getPath() && $event->getType() == $event->getType())) { 114 | $filteredEvents[] = $event; 115 | } 116 | $lastEvent = $event; 117 | } 118 | 119 | return $filteredEvents; 120 | } 121 | 122 | /** 123 | * @param array $event 124 | * @return IChange[] 125 | * @throws \Exception 126 | */ 127 | private function parseEvent(array $event): array { 128 | if (!isset($this->pathMap[$event['wd']])) { 129 | throw new \Exception('Invalid inotify event'); 130 | } 131 | $path = $this->pathMap[$event['wd']] . '/' . $event['name']; 132 | 133 | $mask = $event['mask']; 134 | $cookie = $event['cookie']; 135 | if (($mask & \IN_MOVED_TO) || ($mask & \IN_MOVED_FROM)) { 136 | if (!isset($this->moveMap[$cookie])) { 137 | $this->moveMap[$cookie] = []; 138 | } 139 | } 140 | if ($mask & \IN_MOVED_FROM) { 141 | if (isset($this->moveMap[$cookie]['to'])) { 142 | $targetPath = $this->moveMap[$event['cookie']]['to']; 143 | unset($this->moveMap[$cookie]); 144 | return [ 145 | new RenameChange(IChange::RENAMED, $this->getRelativePath($path), 146 | $this->getRelativePath($targetPath)), 147 | ]; 148 | } else { 149 | $this->moveMap[$event['cookie']]['from'] = $path; 150 | return []; 151 | } 152 | } 153 | 154 | if ($mask & \IN_MOVED_TO) { 155 | if (isset($this->moveMap[$cookie]['from'])) { 156 | $fromPath = $this->moveMap[$event['cookie']]['from']; 157 | unset($this->moveMap[$cookie]); 158 | return [ 159 | new RenameChange(IChange::RENAMED, $this->getRelativePath($fromPath), 160 | $this->getRelativePath($path)), 161 | ]; 162 | } else { 163 | $this->moveMap[$event['cookie']]['to'] = $path; 164 | return []; 165 | } 166 | } 167 | 168 | if ($mask & \IN_MODIFY) { 169 | return [new Change(IChange::MODIFIED, $this->getRelativePath($path))]; 170 | } 171 | if ($mask & \IN_CREATE) { 172 | if (is_dir($path . '/')) { 173 | $events = $this->createChildEvents($path); 174 | $this->watchPath($path); 175 | } else { 176 | $events = []; 177 | } 178 | array_unshift($events, new Change(IChange::ADDED, $this->getRelativePath($path))); 179 | return $events; 180 | } 181 | if ($mask & \IN_DELETE) { 182 | return [new Change(IChange::REMOVED, $this->getRelativePath($path))]; 183 | } 184 | return []; 185 | } 186 | 187 | /** 188 | * create "create changes" for files inside a newly detected directory 189 | * 190 | * this is needed since a file can be added to a directory before we have the time to add a watch 191 | * 192 | * @param $path 193 | * @return IChange[] 194 | */ 195 | private function createChildEvents(string $path): array { 196 | $changes = []; 197 | foreach ($this->getDirectoryIterator($path) as $file) { 198 | $changes[] = new Change(IChange::ADDED, $this->getRelativePath($file)); 199 | } 200 | return $changes; 201 | } 202 | 203 | /** 204 | * @param $path 205 | * @return string 206 | */ 207 | private function getRelativePath(string $path): string { 208 | return substr($path, strlen($this->basePath) + 1) ?: ''; 209 | } 210 | 211 | public function listen(callable $callback): void { 212 | if ($this->fd === null) { 213 | return; 214 | } 215 | stream_set_blocking($this->fd, true); 216 | if (function_exists('pcntl_signal')) { 217 | pcntl_signal(SIGTERM, [$this, 'stop']); 218 | pcntl_signal(SIGINT, [$this, 'stop']); 219 | } 220 | $active = true; 221 | // loop while $this->fd is valid 222 | while ($active && is_resource($this->fd)) { 223 | $read = [$this->fd]; 224 | $write = null; 225 | $except = null; 226 | // suppress errors and warnings (use return value instead) 227 | $changed = @stream_select($read, $write, $except, 60); 228 | // php docs say: On error FALSE is returned and a warning raised 229 | // php docs say: (this can happen if the system call is interrupted by an incoming signal). 230 | // handle those signals if possible 231 | if ($changed === false && function_exists('pcntl_signal_dispatch')) { 232 | pcntl_signal_dispatch(); 233 | } 234 | 235 | // we only added one stream, so $changed > 0 means it is readable now 236 | if ($changed) { 237 | $events = $this->readEvents(); 238 | foreach ($events as $event) { 239 | if ($callback($event) === false) { 240 | $active = false; // stop this loop 241 | } 242 | } 243 | } 244 | } 245 | } 246 | 247 | public function stop(): void { 248 | if ($this->fd === null) { 249 | return; 250 | } 251 | $handle = $this->fd; 252 | $this->fd = null; 253 | fclose($handle); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/Storage/NotifyHandlerTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * @license GNU AGPL version 3 or any later version 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public License 19 | * along with this program. If not, see . 20 | * 21 | */ 22 | 23 | namespace OCA\Files_INotify\Tests\Storage; 24 | 25 | use OC\Files\Notify\Change; 26 | use OC\Files\Notify\RenameChange; 27 | use OCA\Files_INotify\Storage\NotifyHandler; 28 | use OCP\Files\Notify\IChange; 29 | use Test\TestCase; 30 | 31 | class NotifyHandlerTest extends TestCase { 32 | /** @var NotifyHandler */ 33 | private $handler; 34 | /** @var string */ 35 | private $basePath; 36 | 37 | protected function setUp(): void { 38 | parent::setUp(); 39 | 40 | $this->basePath = \OC::$server->getTempManager()->getTemporaryFolder(); 41 | $this->handler = new NotifyHandler($this->basePath); 42 | } 43 | 44 | public function testBasicNotify() { 45 | file_put_contents($this->basePath . 'foo.txt', 'foo'); 46 | usleep(100 * 1000); 47 | 48 | $changes = $this->handler->getChanges(); 49 | $this->assertEquals([new Change(IChange::ADDED, 'foo.txt')], $changes); 50 | 51 | file_put_contents($this->basePath . 'foo.txt', 'bar'); 52 | usleep(100 * 1000); 53 | 54 | $changes = $this->handler->getChanges(); 55 | $this->assertEquals([new Change(IChange::MODIFIED, 'foo.txt')], $changes); 56 | 57 | rename($this->basePath . 'foo.txt', $this->basePath . 'bar.txt'); 58 | usleep(100 * 1000); 59 | 60 | $changes = $this->handler->getChanges(); 61 | $this->assertEquals([new RenameChange(IChange::RENAMED, 'foo.txt', 'bar.txt')], $changes); 62 | 63 | unlink($this->basePath . 'bar.txt'); 64 | usleep(100 * 1000); 65 | 66 | $changes = $this->handler->getChanges(); 67 | $this->assertEquals([new Change(IChange::REMOVED, 'bar.txt')], $changes); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | addPsr4('Test\\', OC::$SERVERROOT . '/tests/lib/', true); 8 | \OC::$composerAutoloader->addPsr4('Tests\\', OC::$SERVERROOT . '/tests/', true); 9 | 10 | OC_App::loadApp('files_inotify'); 11 | 12 | OC_Hook::clear(); 13 | -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | . 9 | 10 | 11 | 12 | ../ 13 | 14 | ../tests 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/stub.phpstub: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * @license GNU AGPL version 3 or any later version 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | * 22 | */ 23 | 24 | namespace { 25 | class OC { 26 | static $CLI = false; 27 | } 28 | } 29 | 30 | namespace OC\Files\Notify { 31 | 32 | use OCP\Files\Notify\IChange; 33 | use OCP\Files\Notify\IRenameChange; 34 | 35 | class Change implements IChange { 36 | public function __construct(int $type, string $path) { 37 | } 38 | 39 | public function getType() { 40 | } 41 | 42 | public function getPath() { 43 | } 44 | 45 | } 46 | 47 | class RenameChange implements IRenameChange, IChange { 48 | public function __construct(int $type, string $source, string $target) { 49 | } 50 | 51 | public function getType() { 52 | } 53 | 54 | public function getPath() { 55 | } 56 | 57 | public function getTargetPath() { 58 | } 59 | } 60 | } 61 | 62 | namespace OCA\Files_External\Lib\Backend { 63 | class Backend { 64 | /** 65 | * @param string $class 66 | * @return $this 67 | */ 68 | public function setStorageClass($class) { 69 | 70 | } 71 | } 72 | 73 | class Local extends Backend { 74 | 75 | public function __construct($l10n, $auth) { 76 | } 77 | } 78 | } 79 | 80 | namespace OCA\Files_External\Lib\Auth { 81 | class NullMechanism { 82 | } 83 | } 84 | 85 | namespace OCA\Files_External\Lib\Config { 86 | use OCA\Files_External\Lib\Backend\Backend; 87 | 88 | interface IBackendProvider { 89 | /** 90 | * @return Backend[] 91 | */ 92 | public function getBackends(); 93 | } 94 | } 95 | 96 | namespace OCA\Files_External\Service { 97 | 98 | use OCA\Files_External\Lib\Config\IBackendProvider; 99 | 100 | class BackendService { 101 | public function registerBackendProvider(IBackendProvider $provider) { 102 | 103 | } 104 | } 105 | } 106 | 107 | namespace OC\Files\Storage { 108 | class Local { 109 | public $datadir; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /vendor-bin/cs-fixer/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "nextcloud/coding-standard": "^1.3.1" 4 | }, 5 | "config": { 6 | "platform": { 7 | "php": "8.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /vendor-bin/cs-fixer/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "b80be9f75e57093d11f0a5065039b255", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "kubawerlos/php-cs-fixer-custom-fixers", 12 | "version": "v3.23.0", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", 16 | "reference": "b3210c6e546bdfc95664297a8971ae3b6b1f4a5a" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/b3210c6e546bdfc95664297a8971ae3b6b1f4a5a", 21 | "reference": "b3210c6e546bdfc95664297a8971ae3b6b1f4a5a", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "ext-filter": "*", 26 | "ext-tokenizer": "*", 27 | "friendsofphp/php-cs-fixer": "^3.61.1", 28 | "php": "^7.4 || ^8.0" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "^9.6.4 || ^10.5.29" 32 | }, 33 | "type": "library", 34 | "autoload": { 35 | "psr-4": { 36 | "PhpCsFixerCustomFixers\\": "src" 37 | } 38 | }, 39 | "notification-url": "https://packagist.org/downloads/", 40 | "license": [ 41 | "MIT" 42 | ], 43 | "authors": [ 44 | { 45 | "name": "Kuba Werłos", 46 | "email": "werlos@gmail.com" 47 | } 48 | ], 49 | "description": "A set of custom fixers for PHP CS Fixer", 50 | "support": { 51 | "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", 52 | "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.23.0" 53 | }, 54 | "time": "2025-02-15T09:15:56+00:00" 55 | }, 56 | { 57 | "name": "nextcloud/coding-standard", 58 | "version": "v1.3.2", 59 | "source": { 60 | "type": "git", 61 | "url": "https://github.com/nextcloud/coding-standard.git", 62 | "reference": "9c719c4747fa26efc12f2e8b21c14a9a75c6ba6d" 63 | }, 64 | "dist": { 65 | "type": "zip", 66 | "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/9c719c4747fa26efc12f2e8b21c14a9a75c6ba6d", 67 | "reference": "9c719c4747fa26efc12f2e8b21c14a9a75c6ba6d", 68 | "shasum": "" 69 | }, 70 | "require": { 71 | "kubawerlos/php-cs-fixer-custom-fixers": "^3.22", 72 | "php": "^7.3|^8.0", 73 | "php-cs-fixer/shim": "^3.17" 74 | }, 75 | "type": "library", 76 | "autoload": { 77 | "psr-4": { 78 | "Nextcloud\\CodingStandard\\": "src" 79 | } 80 | }, 81 | "notification-url": "https://packagist.org/downloads/", 82 | "license": [ 83 | "MIT" 84 | ], 85 | "authors": [ 86 | { 87 | "name": "Christoph Wurst", 88 | "email": "christoph@winzerhof-wurst.at" 89 | } 90 | ], 91 | "description": "Nextcloud coding standards for the php cs fixer", 92 | "support": { 93 | "issues": "https://github.com/nextcloud/coding-standard/issues", 94 | "source": "https://github.com/nextcloud/coding-standard/tree/v1.3.2" 95 | }, 96 | "time": "2024-10-14T16:49:05+00:00" 97 | }, 98 | { 99 | "name": "php-cs-fixer/shim", 100 | "version": "v3.71.0", 101 | "source": { 102 | "type": "git", 103 | "url": "https://github.com/PHP-CS-Fixer/shim.git", 104 | "reference": "bbc2a38cc7b89727def47025dd8e03e1e3e46d82" 105 | }, 106 | "dist": { 107 | "type": "zip", 108 | "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/bbc2a38cc7b89727def47025dd8e03e1e3e46d82", 109 | "reference": "bbc2a38cc7b89727def47025dd8e03e1e3e46d82", 110 | "shasum": "" 111 | }, 112 | "require": { 113 | "ext-json": "*", 114 | "ext-tokenizer": "*", 115 | "php": "^7.4 || ^8.0" 116 | }, 117 | "replace": { 118 | "friendsofphp/php-cs-fixer": "self.version" 119 | }, 120 | "suggest": { 121 | "ext-dom": "For handling output formats in XML", 122 | "ext-mbstring": "For handling non-UTF8 characters." 123 | }, 124 | "bin": [ 125 | "php-cs-fixer", 126 | "php-cs-fixer.phar" 127 | ], 128 | "type": "application", 129 | "notification-url": "https://packagist.org/downloads/", 130 | "license": [ 131 | "MIT" 132 | ], 133 | "authors": [ 134 | { 135 | "name": "Fabien Potencier", 136 | "email": "fabien@symfony.com" 137 | }, 138 | { 139 | "name": "Dariusz Rumiński", 140 | "email": "dariusz.ruminski@gmail.com" 141 | } 142 | ], 143 | "description": "A tool to automatically fix PHP code style", 144 | "support": { 145 | "issues": "https://github.com/PHP-CS-Fixer/shim/issues", 146 | "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.71.0" 147 | }, 148 | "time": "2025-03-07T23:07:16+00:00" 149 | } 150 | ], 151 | "aliases": [], 152 | "minimum-stability": "stable", 153 | "stability-flags": {}, 154 | "prefer-stable": false, 155 | "prefer-lowest": false, 156 | "platform": {}, 157 | "platform-dev": {}, 158 | "platform-overrides": { 159 | "php": "8.1" 160 | }, 161 | "plugin-api-version": "2.6.0" 162 | } 163 | -------------------------------------------------------------------------------- /vendor-bin/phpunit/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "phpunit/phpunit": "^9" 4 | }, 5 | "config": { 6 | "platform": { 7 | "php": "8.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /vendor-bin/phpunit/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "d31767cd369c343e7e8721f89b76a7f4", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "doctrine/instantiator", 12 | "version": "2.0.0", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/doctrine/instantiator.git", 16 | "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", 21 | "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": "^8.1" 26 | }, 27 | "require-dev": { 28 | "doctrine/coding-standard": "^11", 29 | "ext-pdo": "*", 30 | "ext-phar": "*", 31 | "phpbench/phpbench": "^1.2", 32 | "phpstan/phpstan": "^1.9.4", 33 | "phpstan/phpstan-phpunit": "^1.3", 34 | "phpunit/phpunit": "^9.5.27", 35 | "vimeo/psalm": "^5.4" 36 | }, 37 | "type": "library", 38 | "autoload": { 39 | "psr-4": { 40 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 41 | } 42 | }, 43 | "notification-url": "https://packagist.org/downloads/", 44 | "license": [ 45 | "MIT" 46 | ], 47 | "authors": [ 48 | { 49 | "name": "Marco Pivetta", 50 | "email": "ocramius@gmail.com", 51 | "homepage": "https://ocramius.github.io/" 52 | } 53 | ], 54 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 55 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 56 | "keywords": [ 57 | "constructor", 58 | "instantiate" 59 | ], 60 | "support": { 61 | "issues": "https://github.com/doctrine/instantiator/issues", 62 | "source": "https://github.com/doctrine/instantiator/tree/2.0.0" 63 | }, 64 | "funding": [ 65 | { 66 | "url": "https://www.doctrine-project.org/sponsorship.html", 67 | "type": "custom" 68 | }, 69 | { 70 | "url": "https://www.patreon.com/phpdoctrine", 71 | "type": "patreon" 72 | }, 73 | { 74 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", 75 | "type": "tidelift" 76 | } 77 | ], 78 | "time": "2022-12-30T00:23:10+00:00" 79 | }, 80 | { 81 | "name": "myclabs/deep-copy", 82 | "version": "1.13.0", 83 | "source": { 84 | "type": "git", 85 | "url": "https://github.com/myclabs/DeepCopy.git", 86 | "reference": "024473a478be9df5fdaca2c793f2232fe788e414" 87 | }, 88 | "dist": { 89 | "type": "zip", 90 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", 91 | "reference": "024473a478be9df5fdaca2c793f2232fe788e414", 92 | "shasum": "" 93 | }, 94 | "require": { 95 | "php": "^7.1 || ^8.0" 96 | }, 97 | "conflict": { 98 | "doctrine/collections": "<1.6.8", 99 | "doctrine/common": "<2.13.3 || >=3 <3.2.2" 100 | }, 101 | "require-dev": { 102 | "doctrine/collections": "^1.6.8", 103 | "doctrine/common": "^2.13.3 || ^3.2.2", 104 | "phpspec/prophecy": "^1.10", 105 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 106 | }, 107 | "type": "library", 108 | "autoload": { 109 | "files": [ 110 | "src/DeepCopy/deep_copy.php" 111 | ], 112 | "psr-4": { 113 | "DeepCopy\\": "src/DeepCopy/" 114 | } 115 | }, 116 | "notification-url": "https://packagist.org/downloads/", 117 | "license": [ 118 | "MIT" 119 | ], 120 | "description": "Create deep copies (clones) of your objects", 121 | "keywords": [ 122 | "clone", 123 | "copy", 124 | "duplicate", 125 | "object", 126 | "object graph" 127 | ], 128 | "support": { 129 | "issues": "https://github.com/myclabs/DeepCopy/issues", 130 | "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" 131 | }, 132 | "funding": [ 133 | { 134 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 135 | "type": "tidelift" 136 | } 137 | ], 138 | "time": "2025-02-12T12:17:51+00:00" 139 | }, 140 | { 141 | "name": "nikic/php-parser", 142 | "version": "v5.4.0", 143 | "source": { 144 | "type": "git", 145 | "url": "https://github.com/nikic/PHP-Parser.git", 146 | "reference": "447a020a1f875a434d62f2a401f53b82a396e494" 147 | }, 148 | "dist": { 149 | "type": "zip", 150 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", 151 | "reference": "447a020a1f875a434d62f2a401f53b82a396e494", 152 | "shasum": "" 153 | }, 154 | "require": { 155 | "ext-ctype": "*", 156 | "ext-json": "*", 157 | "ext-tokenizer": "*", 158 | "php": ">=7.4" 159 | }, 160 | "require-dev": { 161 | "ircmaxell/php-yacc": "^0.0.7", 162 | "phpunit/phpunit": "^9.0" 163 | }, 164 | "bin": [ 165 | "bin/php-parse" 166 | ], 167 | "type": "library", 168 | "extra": { 169 | "branch-alias": { 170 | "dev-master": "5.0-dev" 171 | } 172 | }, 173 | "autoload": { 174 | "psr-4": { 175 | "PhpParser\\": "lib/PhpParser" 176 | } 177 | }, 178 | "notification-url": "https://packagist.org/downloads/", 179 | "license": [ 180 | "BSD-3-Clause" 181 | ], 182 | "authors": [ 183 | { 184 | "name": "Nikita Popov" 185 | } 186 | ], 187 | "description": "A PHP parser written in PHP", 188 | "keywords": [ 189 | "parser", 190 | "php" 191 | ], 192 | "support": { 193 | "issues": "https://github.com/nikic/PHP-Parser/issues", 194 | "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" 195 | }, 196 | "time": "2024-12-30T11:07:19+00:00" 197 | }, 198 | { 199 | "name": "phar-io/manifest", 200 | "version": "2.0.4", 201 | "source": { 202 | "type": "git", 203 | "url": "https://github.com/phar-io/manifest.git", 204 | "reference": "54750ef60c58e43759730615a392c31c80e23176" 205 | }, 206 | "dist": { 207 | "type": "zip", 208 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", 209 | "reference": "54750ef60c58e43759730615a392c31c80e23176", 210 | "shasum": "" 211 | }, 212 | "require": { 213 | "ext-dom": "*", 214 | "ext-libxml": "*", 215 | "ext-phar": "*", 216 | "ext-xmlwriter": "*", 217 | "phar-io/version": "^3.0.1", 218 | "php": "^7.2 || ^8.0" 219 | }, 220 | "type": "library", 221 | "extra": { 222 | "branch-alias": { 223 | "dev-master": "2.0.x-dev" 224 | } 225 | }, 226 | "autoload": { 227 | "classmap": [ 228 | "src/" 229 | ] 230 | }, 231 | "notification-url": "https://packagist.org/downloads/", 232 | "license": [ 233 | "BSD-3-Clause" 234 | ], 235 | "authors": [ 236 | { 237 | "name": "Arne Blankerts", 238 | "email": "arne@blankerts.de", 239 | "role": "Developer" 240 | }, 241 | { 242 | "name": "Sebastian Heuer", 243 | "email": "sebastian@phpeople.de", 244 | "role": "Developer" 245 | }, 246 | { 247 | "name": "Sebastian Bergmann", 248 | "email": "sebastian@phpunit.de", 249 | "role": "Developer" 250 | } 251 | ], 252 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 253 | "support": { 254 | "issues": "https://github.com/phar-io/manifest/issues", 255 | "source": "https://github.com/phar-io/manifest/tree/2.0.4" 256 | }, 257 | "funding": [ 258 | { 259 | "url": "https://github.com/theseer", 260 | "type": "github" 261 | } 262 | ], 263 | "time": "2024-03-03T12:33:53+00:00" 264 | }, 265 | { 266 | "name": "phar-io/version", 267 | "version": "3.2.1", 268 | "source": { 269 | "type": "git", 270 | "url": "https://github.com/phar-io/version.git", 271 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 272 | }, 273 | "dist": { 274 | "type": "zip", 275 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 276 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 277 | "shasum": "" 278 | }, 279 | "require": { 280 | "php": "^7.2 || ^8.0" 281 | }, 282 | "type": "library", 283 | "autoload": { 284 | "classmap": [ 285 | "src/" 286 | ] 287 | }, 288 | "notification-url": "https://packagist.org/downloads/", 289 | "license": [ 290 | "BSD-3-Clause" 291 | ], 292 | "authors": [ 293 | { 294 | "name": "Arne Blankerts", 295 | "email": "arne@blankerts.de", 296 | "role": "Developer" 297 | }, 298 | { 299 | "name": "Sebastian Heuer", 300 | "email": "sebastian@phpeople.de", 301 | "role": "Developer" 302 | }, 303 | { 304 | "name": "Sebastian Bergmann", 305 | "email": "sebastian@phpunit.de", 306 | "role": "Developer" 307 | } 308 | ], 309 | "description": "Library for handling version information and constraints", 310 | "support": { 311 | "issues": "https://github.com/phar-io/version/issues", 312 | "source": "https://github.com/phar-io/version/tree/3.2.1" 313 | }, 314 | "time": "2022-02-21T01:04:05+00:00" 315 | }, 316 | { 317 | "name": "phpunit/php-code-coverage", 318 | "version": "9.2.32", 319 | "source": { 320 | "type": "git", 321 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 322 | "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" 323 | }, 324 | "dist": { 325 | "type": "zip", 326 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", 327 | "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", 328 | "shasum": "" 329 | }, 330 | "require": { 331 | "ext-dom": "*", 332 | "ext-libxml": "*", 333 | "ext-xmlwriter": "*", 334 | "nikic/php-parser": "^4.19.1 || ^5.1.0", 335 | "php": ">=7.3", 336 | "phpunit/php-file-iterator": "^3.0.6", 337 | "phpunit/php-text-template": "^2.0.4", 338 | "sebastian/code-unit-reverse-lookup": "^2.0.3", 339 | "sebastian/complexity": "^2.0.3", 340 | "sebastian/environment": "^5.1.5", 341 | "sebastian/lines-of-code": "^1.0.4", 342 | "sebastian/version": "^3.0.2", 343 | "theseer/tokenizer": "^1.2.3" 344 | }, 345 | "require-dev": { 346 | "phpunit/phpunit": "^9.6" 347 | }, 348 | "suggest": { 349 | "ext-pcov": "PHP extension that provides line coverage", 350 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 351 | }, 352 | "type": "library", 353 | "extra": { 354 | "branch-alias": { 355 | "dev-main": "9.2.x-dev" 356 | } 357 | }, 358 | "autoload": { 359 | "classmap": [ 360 | "src/" 361 | ] 362 | }, 363 | "notification-url": "https://packagist.org/downloads/", 364 | "license": [ 365 | "BSD-3-Clause" 366 | ], 367 | "authors": [ 368 | { 369 | "name": "Sebastian Bergmann", 370 | "email": "sebastian@phpunit.de", 371 | "role": "lead" 372 | } 373 | ], 374 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 375 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 376 | "keywords": [ 377 | "coverage", 378 | "testing", 379 | "xunit" 380 | ], 381 | "support": { 382 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 383 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", 384 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" 385 | }, 386 | "funding": [ 387 | { 388 | "url": "https://github.com/sebastianbergmann", 389 | "type": "github" 390 | } 391 | ], 392 | "time": "2024-08-22T04:23:01+00:00" 393 | }, 394 | { 395 | "name": "phpunit/php-file-iterator", 396 | "version": "3.0.6", 397 | "source": { 398 | "type": "git", 399 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 400 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" 401 | }, 402 | "dist": { 403 | "type": "zip", 404 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 405 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 406 | "shasum": "" 407 | }, 408 | "require": { 409 | "php": ">=7.3" 410 | }, 411 | "require-dev": { 412 | "phpunit/phpunit": "^9.3" 413 | }, 414 | "type": "library", 415 | "extra": { 416 | "branch-alias": { 417 | "dev-master": "3.0-dev" 418 | } 419 | }, 420 | "autoload": { 421 | "classmap": [ 422 | "src/" 423 | ] 424 | }, 425 | "notification-url": "https://packagist.org/downloads/", 426 | "license": [ 427 | "BSD-3-Clause" 428 | ], 429 | "authors": [ 430 | { 431 | "name": "Sebastian Bergmann", 432 | "email": "sebastian@phpunit.de", 433 | "role": "lead" 434 | } 435 | ], 436 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 437 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 438 | "keywords": [ 439 | "filesystem", 440 | "iterator" 441 | ], 442 | "support": { 443 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 444 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" 445 | }, 446 | "funding": [ 447 | { 448 | "url": "https://github.com/sebastianbergmann", 449 | "type": "github" 450 | } 451 | ], 452 | "time": "2021-12-02T12:48:52+00:00" 453 | }, 454 | { 455 | "name": "phpunit/php-invoker", 456 | "version": "3.1.1", 457 | "source": { 458 | "type": "git", 459 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 460 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" 461 | }, 462 | "dist": { 463 | "type": "zip", 464 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 465 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 466 | "shasum": "" 467 | }, 468 | "require": { 469 | "php": ">=7.3" 470 | }, 471 | "require-dev": { 472 | "ext-pcntl": "*", 473 | "phpunit/phpunit": "^9.3" 474 | }, 475 | "suggest": { 476 | "ext-pcntl": "*" 477 | }, 478 | "type": "library", 479 | "extra": { 480 | "branch-alias": { 481 | "dev-master": "3.1-dev" 482 | } 483 | }, 484 | "autoload": { 485 | "classmap": [ 486 | "src/" 487 | ] 488 | }, 489 | "notification-url": "https://packagist.org/downloads/", 490 | "license": [ 491 | "BSD-3-Clause" 492 | ], 493 | "authors": [ 494 | { 495 | "name": "Sebastian Bergmann", 496 | "email": "sebastian@phpunit.de", 497 | "role": "lead" 498 | } 499 | ], 500 | "description": "Invoke callables with a timeout", 501 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 502 | "keywords": [ 503 | "process" 504 | ], 505 | "support": { 506 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 507 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" 508 | }, 509 | "funding": [ 510 | { 511 | "url": "https://github.com/sebastianbergmann", 512 | "type": "github" 513 | } 514 | ], 515 | "time": "2020-09-28T05:58:55+00:00" 516 | }, 517 | { 518 | "name": "phpunit/php-text-template", 519 | "version": "2.0.4", 520 | "source": { 521 | "type": "git", 522 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 523 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" 524 | }, 525 | "dist": { 526 | "type": "zip", 527 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 528 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 529 | "shasum": "" 530 | }, 531 | "require": { 532 | "php": ">=7.3" 533 | }, 534 | "require-dev": { 535 | "phpunit/phpunit": "^9.3" 536 | }, 537 | "type": "library", 538 | "extra": { 539 | "branch-alias": { 540 | "dev-master": "2.0-dev" 541 | } 542 | }, 543 | "autoload": { 544 | "classmap": [ 545 | "src/" 546 | ] 547 | }, 548 | "notification-url": "https://packagist.org/downloads/", 549 | "license": [ 550 | "BSD-3-Clause" 551 | ], 552 | "authors": [ 553 | { 554 | "name": "Sebastian Bergmann", 555 | "email": "sebastian@phpunit.de", 556 | "role": "lead" 557 | } 558 | ], 559 | "description": "Simple template engine.", 560 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 561 | "keywords": [ 562 | "template" 563 | ], 564 | "support": { 565 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 566 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" 567 | }, 568 | "funding": [ 569 | { 570 | "url": "https://github.com/sebastianbergmann", 571 | "type": "github" 572 | } 573 | ], 574 | "time": "2020-10-26T05:33:50+00:00" 575 | }, 576 | { 577 | "name": "phpunit/php-timer", 578 | "version": "5.0.3", 579 | "source": { 580 | "type": "git", 581 | "url": "https://github.com/sebastianbergmann/php-timer.git", 582 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" 583 | }, 584 | "dist": { 585 | "type": "zip", 586 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 587 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 588 | "shasum": "" 589 | }, 590 | "require": { 591 | "php": ">=7.3" 592 | }, 593 | "require-dev": { 594 | "phpunit/phpunit": "^9.3" 595 | }, 596 | "type": "library", 597 | "extra": { 598 | "branch-alias": { 599 | "dev-master": "5.0-dev" 600 | } 601 | }, 602 | "autoload": { 603 | "classmap": [ 604 | "src/" 605 | ] 606 | }, 607 | "notification-url": "https://packagist.org/downloads/", 608 | "license": [ 609 | "BSD-3-Clause" 610 | ], 611 | "authors": [ 612 | { 613 | "name": "Sebastian Bergmann", 614 | "email": "sebastian@phpunit.de", 615 | "role": "lead" 616 | } 617 | ], 618 | "description": "Utility class for timing", 619 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 620 | "keywords": [ 621 | "timer" 622 | ], 623 | "support": { 624 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 625 | "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" 626 | }, 627 | "funding": [ 628 | { 629 | "url": "https://github.com/sebastianbergmann", 630 | "type": "github" 631 | } 632 | ], 633 | "time": "2020-10-26T13:16:10+00:00" 634 | }, 635 | { 636 | "name": "phpunit/phpunit", 637 | "version": "9.6.22", 638 | "source": { 639 | "type": "git", 640 | "url": "https://github.com/sebastianbergmann/phpunit.git", 641 | "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" 642 | }, 643 | "dist": { 644 | "type": "zip", 645 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", 646 | "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", 647 | "shasum": "" 648 | }, 649 | "require": { 650 | "doctrine/instantiator": "^1.5.0 || ^2", 651 | "ext-dom": "*", 652 | "ext-json": "*", 653 | "ext-libxml": "*", 654 | "ext-mbstring": "*", 655 | "ext-xml": "*", 656 | "ext-xmlwriter": "*", 657 | "myclabs/deep-copy": "^1.12.1", 658 | "phar-io/manifest": "^2.0.4", 659 | "phar-io/version": "^3.2.1", 660 | "php": ">=7.3", 661 | "phpunit/php-code-coverage": "^9.2.32", 662 | "phpunit/php-file-iterator": "^3.0.6", 663 | "phpunit/php-invoker": "^3.1.1", 664 | "phpunit/php-text-template": "^2.0.4", 665 | "phpunit/php-timer": "^5.0.3", 666 | "sebastian/cli-parser": "^1.0.2", 667 | "sebastian/code-unit": "^1.0.8", 668 | "sebastian/comparator": "^4.0.8", 669 | "sebastian/diff": "^4.0.6", 670 | "sebastian/environment": "^5.1.5", 671 | "sebastian/exporter": "^4.0.6", 672 | "sebastian/global-state": "^5.0.7", 673 | "sebastian/object-enumerator": "^4.0.4", 674 | "sebastian/resource-operations": "^3.0.4", 675 | "sebastian/type": "^3.2.1", 676 | "sebastian/version": "^3.0.2" 677 | }, 678 | "suggest": { 679 | "ext-soap": "To be able to generate mocks based on WSDL files", 680 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 681 | }, 682 | "bin": [ 683 | "phpunit" 684 | ], 685 | "type": "library", 686 | "extra": { 687 | "branch-alias": { 688 | "dev-master": "9.6-dev" 689 | } 690 | }, 691 | "autoload": { 692 | "files": [ 693 | "src/Framework/Assert/Functions.php" 694 | ], 695 | "classmap": [ 696 | "src/" 697 | ] 698 | }, 699 | "notification-url": "https://packagist.org/downloads/", 700 | "license": [ 701 | "BSD-3-Clause" 702 | ], 703 | "authors": [ 704 | { 705 | "name": "Sebastian Bergmann", 706 | "email": "sebastian@phpunit.de", 707 | "role": "lead" 708 | } 709 | ], 710 | "description": "The PHP Unit Testing framework.", 711 | "homepage": "https://phpunit.de/", 712 | "keywords": [ 713 | "phpunit", 714 | "testing", 715 | "xunit" 716 | ], 717 | "support": { 718 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 719 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy", 720 | "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" 721 | }, 722 | "funding": [ 723 | { 724 | "url": "https://phpunit.de/sponsors.html", 725 | "type": "custom" 726 | }, 727 | { 728 | "url": "https://github.com/sebastianbergmann", 729 | "type": "github" 730 | }, 731 | { 732 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 733 | "type": "tidelift" 734 | } 735 | ], 736 | "time": "2024-12-05T13:48:26+00:00" 737 | }, 738 | { 739 | "name": "sebastian/cli-parser", 740 | "version": "1.0.2", 741 | "source": { 742 | "type": "git", 743 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 744 | "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" 745 | }, 746 | "dist": { 747 | "type": "zip", 748 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", 749 | "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", 750 | "shasum": "" 751 | }, 752 | "require": { 753 | "php": ">=7.3" 754 | }, 755 | "require-dev": { 756 | "phpunit/phpunit": "^9.3" 757 | }, 758 | "type": "library", 759 | "extra": { 760 | "branch-alias": { 761 | "dev-master": "1.0-dev" 762 | } 763 | }, 764 | "autoload": { 765 | "classmap": [ 766 | "src/" 767 | ] 768 | }, 769 | "notification-url": "https://packagist.org/downloads/", 770 | "license": [ 771 | "BSD-3-Clause" 772 | ], 773 | "authors": [ 774 | { 775 | "name": "Sebastian Bergmann", 776 | "email": "sebastian@phpunit.de", 777 | "role": "lead" 778 | } 779 | ], 780 | "description": "Library for parsing CLI options", 781 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 782 | "support": { 783 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 784 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" 785 | }, 786 | "funding": [ 787 | { 788 | "url": "https://github.com/sebastianbergmann", 789 | "type": "github" 790 | } 791 | ], 792 | "time": "2024-03-02T06:27:43+00:00" 793 | }, 794 | { 795 | "name": "sebastian/code-unit", 796 | "version": "1.0.8", 797 | "source": { 798 | "type": "git", 799 | "url": "https://github.com/sebastianbergmann/code-unit.git", 800 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" 801 | }, 802 | "dist": { 803 | "type": "zip", 804 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", 805 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", 806 | "shasum": "" 807 | }, 808 | "require": { 809 | "php": ">=7.3" 810 | }, 811 | "require-dev": { 812 | "phpunit/phpunit": "^9.3" 813 | }, 814 | "type": "library", 815 | "extra": { 816 | "branch-alias": { 817 | "dev-master": "1.0-dev" 818 | } 819 | }, 820 | "autoload": { 821 | "classmap": [ 822 | "src/" 823 | ] 824 | }, 825 | "notification-url": "https://packagist.org/downloads/", 826 | "license": [ 827 | "BSD-3-Clause" 828 | ], 829 | "authors": [ 830 | { 831 | "name": "Sebastian Bergmann", 832 | "email": "sebastian@phpunit.de", 833 | "role": "lead" 834 | } 835 | ], 836 | "description": "Collection of value objects that represent the PHP code units", 837 | "homepage": "https://github.com/sebastianbergmann/code-unit", 838 | "support": { 839 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 840 | "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" 841 | }, 842 | "funding": [ 843 | { 844 | "url": "https://github.com/sebastianbergmann", 845 | "type": "github" 846 | } 847 | ], 848 | "time": "2020-10-26T13:08:54+00:00" 849 | }, 850 | { 851 | "name": "sebastian/code-unit-reverse-lookup", 852 | "version": "2.0.3", 853 | "source": { 854 | "type": "git", 855 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 856 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" 857 | }, 858 | "dist": { 859 | "type": "zip", 860 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 861 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 862 | "shasum": "" 863 | }, 864 | "require": { 865 | "php": ">=7.3" 866 | }, 867 | "require-dev": { 868 | "phpunit/phpunit": "^9.3" 869 | }, 870 | "type": "library", 871 | "extra": { 872 | "branch-alias": { 873 | "dev-master": "2.0-dev" 874 | } 875 | }, 876 | "autoload": { 877 | "classmap": [ 878 | "src/" 879 | ] 880 | }, 881 | "notification-url": "https://packagist.org/downloads/", 882 | "license": [ 883 | "BSD-3-Clause" 884 | ], 885 | "authors": [ 886 | { 887 | "name": "Sebastian Bergmann", 888 | "email": "sebastian@phpunit.de" 889 | } 890 | ], 891 | "description": "Looks up which function or method a line of code belongs to", 892 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 893 | "support": { 894 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 895 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" 896 | }, 897 | "funding": [ 898 | { 899 | "url": "https://github.com/sebastianbergmann", 900 | "type": "github" 901 | } 902 | ], 903 | "time": "2020-09-28T05:30:19+00:00" 904 | }, 905 | { 906 | "name": "sebastian/comparator", 907 | "version": "4.0.8", 908 | "source": { 909 | "type": "git", 910 | "url": "https://github.com/sebastianbergmann/comparator.git", 911 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a" 912 | }, 913 | "dist": { 914 | "type": "zip", 915 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", 916 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a", 917 | "shasum": "" 918 | }, 919 | "require": { 920 | "php": ">=7.3", 921 | "sebastian/diff": "^4.0", 922 | "sebastian/exporter": "^4.0" 923 | }, 924 | "require-dev": { 925 | "phpunit/phpunit": "^9.3" 926 | }, 927 | "type": "library", 928 | "extra": { 929 | "branch-alias": { 930 | "dev-master": "4.0-dev" 931 | } 932 | }, 933 | "autoload": { 934 | "classmap": [ 935 | "src/" 936 | ] 937 | }, 938 | "notification-url": "https://packagist.org/downloads/", 939 | "license": [ 940 | "BSD-3-Clause" 941 | ], 942 | "authors": [ 943 | { 944 | "name": "Sebastian Bergmann", 945 | "email": "sebastian@phpunit.de" 946 | }, 947 | { 948 | "name": "Jeff Welch", 949 | "email": "whatthejeff@gmail.com" 950 | }, 951 | { 952 | "name": "Volker Dusch", 953 | "email": "github@wallbash.com" 954 | }, 955 | { 956 | "name": "Bernhard Schussek", 957 | "email": "bschussek@2bepublished.at" 958 | } 959 | ], 960 | "description": "Provides the functionality to compare PHP values for equality", 961 | "homepage": "https://github.com/sebastianbergmann/comparator", 962 | "keywords": [ 963 | "comparator", 964 | "compare", 965 | "equality" 966 | ], 967 | "support": { 968 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 969 | "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" 970 | }, 971 | "funding": [ 972 | { 973 | "url": "https://github.com/sebastianbergmann", 974 | "type": "github" 975 | } 976 | ], 977 | "time": "2022-09-14T12:41:17+00:00" 978 | }, 979 | { 980 | "name": "sebastian/complexity", 981 | "version": "2.0.3", 982 | "source": { 983 | "type": "git", 984 | "url": "https://github.com/sebastianbergmann/complexity.git", 985 | "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" 986 | }, 987 | "dist": { 988 | "type": "zip", 989 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", 990 | "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", 991 | "shasum": "" 992 | }, 993 | "require": { 994 | "nikic/php-parser": "^4.18 || ^5.0", 995 | "php": ">=7.3" 996 | }, 997 | "require-dev": { 998 | "phpunit/phpunit": "^9.3" 999 | }, 1000 | "type": "library", 1001 | "extra": { 1002 | "branch-alias": { 1003 | "dev-master": "2.0-dev" 1004 | } 1005 | }, 1006 | "autoload": { 1007 | "classmap": [ 1008 | "src/" 1009 | ] 1010 | }, 1011 | "notification-url": "https://packagist.org/downloads/", 1012 | "license": [ 1013 | "BSD-3-Clause" 1014 | ], 1015 | "authors": [ 1016 | { 1017 | "name": "Sebastian Bergmann", 1018 | "email": "sebastian@phpunit.de", 1019 | "role": "lead" 1020 | } 1021 | ], 1022 | "description": "Library for calculating the complexity of PHP code units", 1023 | "homepage": "https://github.com/sebastianbergmann/complexity", 1024 | "support": { 1025 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1026 | "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" 1027 | }, 1028 | "funding": [ 1029 | { 1030 | "url": "https://github.com/sebastianbergmann", 1031 | "type": "github" 1032 | } 1033 | ], 1034 | "time": "2023-12-22T06:19:30+00:00" 1035 | }, 1036 | { 1037 | "name": "sebastian/diff", 1038 | "version": "4.0.6", 1039 | "source": { 1040 | "type": "git", 1041 | "url": "https://github.com/sebastianbergmann/diff.git", 1042 | "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" 1043 | }, 1044 | "dist": { 1045 | "type": "zip", 1046 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", 1047 | "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", 1048 | "shasum": "" 1049 | }, 1050 | "require": { 1051 | "php": ">=7.3" 1052 | }, 1053 | "require-dev": { 1054 | "phpunit/phpunit": "^9.3", 1055 | "symfony/process": "^4.2 || ^5" 1056 | }, 1057 | "type": "library", 1058 | "extra": { 1059 | "branch-alias": { 1060 | "dev-master": "4.0-dev" 1061 | } 1062 | }, 1063 | "autoload": { 1064 | "classmap": [ 1065 | "src/" 1066 | ] 1067 | }, 1068 | "notification-url": "https://packagist.org/downloads/", 1069 | "license": [ 1070 | "BSD-3-Clause" 1071 | ], 1072 | "authors": [ 1073 | { 1074 | "name": "Sebastian Bergmann", 1075 | "email": "sebastian@phpunit.de" 1076 | }, 1077 | { 1078 | "name": "Kore Nordmann", 1079 | "email": "mail@kore-nordmann.de" 1080 | } 1081 | ], 1082 | "description": "Diff implementation", 1083 | "homepage": "https://github.com/sebastianbergmann/diff", 1084 | "keywords": [ 1085 | "diff", 1086 | "udiff", 1087 | "unidiff", 1088 | "unified diff" 1089 | ], 1090 | "support": { 1091 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1092 | "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" 1093 | }, 1094 | "funding": [ 1095 | { 1096 | "url": "https://github.com/sebastianbergmann", 1097 | "type": "github" 1098 | } 1099 | ], 1100 | "time": "2024-03-02T06:30:58+00:00" 1101 | }, 1102 | { 1103 | "name": "sebastian/environment", 1104 | "version": "5.1.5", 1105 | "source": { 1106 | "type": "git", 1107 | "url": "https://github.com/sebastianbergmann/environment.git", 1108 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" 1109 | }, 1110 | "dist": { 1111 | "type": "zip", 1112 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", 1113 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", 1114 | "shasum": "" 1115 | }, 1116 | "require": { 1117 | "php": ">=7.3" 1118 | }, 1119 | "require-dev": { 1120 | "phpunit/phpunit": "^9.3" 1121 | }, 1122 | "suggest": { 1123 | "ext-posix": "*" 1124 | }, 1125 | "type": "library", 1126 | "extra": { 1127 | "branch-alias": { 1128 | "dev-master": "5.1-dev" 1129 | } 1130 | }, 1131 | "autoload": { 1132 | "classmap": [ 1133 | "src/" 1134 | ] 1135 | }, 1136 | "notification-url": "https://packagist.org/downloads/", 1137 | "license": [ 1138 | "BSD-3-Clause" 1139 | ], 1140 | "authors": [ 1141 | { 1142 | "name": "Sebastian Bergmann", 1143 | "email": "sebastian@phpunit.de" 1144 | } 1145 | ], 1146 | "description": "Provides functionality to handle HHVM/PHP environments", 1147 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1148 | "keywords": [ 1149 | "Xdebug", 1150 | "environment", 1151 | "hhvm" 1152 | ], 1153 | "support": { 1154 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1155 | "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" 1156 | }, 1157 | "funding": [ 1158 | { 1159 | "url": "https://github.com/sebastianbergmann", 1160 | "type": "github" 1161 | } 1162 | ], 1163 | "time": "2023-02-03T06:03:51+00:00" 1164 | }, 1165 | { 1166 | "name": "sebastian/exporter", 1167 | "version": "4.0.6", 1168 | "source": { 1169 | "type": "git", 1170 | "url": "https://github.com/sebastianbergmann/exporter.git", 1171 | "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" 1172 | }, 1173 | "dist": { 1174 | "type": "zip", 1175 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", 1176 | "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", 1177 | "shasum": "" 1178 | }, 1179 | "require": { 1180 | "php": ">=7.3", 1181 | "sebastian/recursion-context": "^4.0" 1182 | }, 1183 | "require-dev": { 1184 | "ext-mbstring": "*", 1185 | "phpunit/phpunit": "^9.3" 1186 | }, 1187 | "type": "library", 1188 | "extra": { 1189 | "branch-alias": { 1190 | "dev-master": "4.0-dev" 1191 | } 1192 | }, 1193 | "autoload": { 1194 | "classmap": [ 1195 | "src/" 1196 | ] 1197 | }, 1198 | "notification-url": "https://packagist.org/downloads/", 1199 | "license": [ 1200 | "BSD-3-Clause" 1201 | ], 1202 | "authors": [ 1203 | { 1204 | "name": "Sebastian Bergmann", 1205 | "email": "sebastian@phpunit.de" 1206 | }, 1207 | { 1208 | "name": "Jeff Welch", 1209 | "email": "whatthejeff@gmail.com" 1210 | }, 1211 | { 1212 | "name": "Volker Dusch", 1213 | "email": "github@wallbash.com" 1214 | }, 1215 | { 1216 | "name": "Adam Harvey", 1217 | "email": "aharvey@php.net" 1218 | }, 1219 | { 1220 | "name": "Bernhard Schussek", 1221 | "email": "bschussek@gmail.com" 1222 | } 1223 | ], 1224 | "description": "Provides the functionality to export PHP variables for visualization", 1225 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1226 | "keywords": [ 1227 | "export", 1228 | "exporter" 1229 | ], 1230 | "support": { 1231 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1232 | "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" 1233 | }, 1234 | "funding": [ 1235 | { 1236 | "url": "https://github.com/sebastianbergmann", 1237 | "type": "github" 1238 | } 1239 | ], 1240 | "time": "2024-03-02T06:33:00+00:00" 1241 | }, 1242 | { 1243 | "name": "sebastian/global-state", 1244 | "version": "5.0.7", 1245 | "source": { 1246 | "type": "git", 1247 | "url": "https://github.com/sebastianbergmann/global-state.git", 1248 | "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" 1249 | }, 1250 | "dist": { 1251 | "type": "zip", 1252 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", 1253 | "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", 1254 | "shasum": "" 1255 | }, 1256 | "require": { 1257 | "php": ">=7.3", 1258 | "sebastian/object-reflector": "^2.0", 1259 | "sebastian/recursion-context": "^4.0" 1260 | }, 1261 | "require-dev": { 1262 | "ext-dom": "*", 1263 | "phpunit/phpunit": "^9.3" 1264 | }, 1265 | "suggest": { 1266 | "ext-uopz": "*" 1267 | }, 1268 | "type": "library", 1269 | "extra": { 1270 | "branch-alias": { 1271 | "dev-master": "5.0-dev" 1272 | } 1273 | }, 1274 | "autoload": { 1275 | "classmap": [ 1276 | "src/" 1277 | ] 1278 | }, 1279 | "notification-url": "https://packagist.org/downloads/", 1280 | "license": [ 1281 | "BSD-3-Clause" 1282 | ], 1283 | "authors": [ 1284 | { 1285 | "name": "Sebastian Bergmann", 1286 | "email": "sebastian@phpunit.de" 1287 | } 1288 | ], 1289 | "description": "Snapshotting of global state", 1290 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1291 | "keywords": [ 1292 | "global state" 1293 | ], 1294 | "support": { 1295 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1296 | "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" 1297 | }, 1298 | "funding": [ 1299 | { 1300 | "url": "https://github.com/sebastianbergmann", 1301 | "type": "github" 1302 | } 1303 | ], 1304 | "time": "2024-03-02T06:35:11+00:00" 1305 | }, 1306 | { 1307 | "name": "sebastian/lines-of-code", 1308 | "version": "1.0.4", 1309 | "source": { 1310 | "type": "git", 1311 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1312 | "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" 1313 | }, 1314 | "dist": { 1315 | "type": "zip", 1316 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", 1317 | "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", 1318 | "shasum": "" 1319 | }, 1320 | "require": { 1321 | "nikic/php-parser": "^4.18 || ^5.0", 1322 | "php": ">=7.3" 1323 | }, 1324 | "require-dev": { 1325 | "phpunit/phpunit": "^9.3" 1326 | }, 1327 | "type": "library", 1328 | "extra": { 1329 | "branch-alias": { 1330 | "dev-master": "1.0-dev" 1331 | } 1332 | }, 1333 | "autoload": { 1334 | "classmap": [ 1335 | "src/" 1336 | ] 1337 | }, 1338 | "notification-url": "https://packagist.org/downloads/", 1339 | "license": [ 1340 | "BSD-3-Clause" 1341 | ], 1342 | "authors": [ 1343 | { 1344 | "name": "Sebastian Bergmann", 1345 | "email": "sebastian@phpunit.de", 1346 | "role": "lead" 1347 | } 1348 | ], 1349 | "description": "Library for counting the lines of code in PHP source code", 1350 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1351 | "support": { 1352 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1353 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" 1354 | }, 1355 | "funding": [ 1356 | { 1357 | "url": "https://github.com/sebastianbergmann", 1358 | "type": "github" 1359 | } 1360 | ], 1361 | "time": "2023-12-22T06:20:34+00:00" 1362 | }, 1363 | { 1364 | "name": "sebastian/object-enumerator", 1365 | "version": "4.0.4", 1366 | "source": { 1367 | "type": "git", 1368 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1369 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" 1370 | }, 1371 | "dist": { 1372 | "type": "zip", 1373 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", 1374 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", 1375 | "shasum": "" 1376 | }, 1377 | "require": { 1378 | "php": ">=7.3", 1379 | "sebastian/object-reflector": "^2.0", 1380 | "sebastian/recursion-context": "^4.0" 1381 | }, 1382 | "require-dev": { 1383 | "phpunit/phpunit": "^9.3" 1384 | }, 1385 | "type": "library", 1386 | "extra": { 1387 | "branch-alias": { 1388 | "dev-master": "4.0-dev" 1389 | } 1390 | }, 1391 | "autoload": { 1392 | "classmap": [ 1393 | "src/" 1394 | ] 1395 | }, 1396 | "notification-url": "https://packagist.org/downloads/", 1397 | "license": [ 1398 | "BSD-3-Clause" 1399 | ], 1400 | "authors": [ 1401 | { 1402 | "name": "Sebastian Bergmann", 1403 | "email": "sebastian@phpunit.de" 1404 | } 1405 | ], 1406 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1407 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1408 | "support": { 1409 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1410 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" 1411 | }, 1412 | "funding": [ 1413 | { 1414 | "url": "https://github.com/sebastianbergmann", 1415 | "type": "github" 1416 | } 1417 | ], 1418 | "time": "2020-10-26T13:12:34+00:00" 1419 | }, 1420 | { 1421 | "name": "sebastian/object-reflector", 1422 | "version": "2.0.4", 1423 | "source": { 1424 | "type": "git", 1425 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1426 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" 1427 | }, 1428 | "dist": { 1429 | "type": "zip", 1430 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1431 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1432 | "shasum": "" 1433 | }, 1434 | "require": { 1435 | "php": ">=7.3" 1436 | }, 1437 | "require-dev": { 1438 | "phpunit/phpunit": "^9.3" 1439 | }, 1440 | "type": "library", 1441 | "extra": { 1442 | "branch-alias": { 1443 | "dev-master": "2.0-dev" 1444 | } 1445 | }, 1446 | "autoload": { 1447 | "classmap": [ 1448 | "src/" 1449 | ] 1450 | }, 1451 | "notification-url": "https://packagist.org/downloads/", 1452 | "license": [ 1453 | "BSD-3-Clause" 1454 | ], 1455 | "authors": [ 1456 | { 1457 | "name": "Sebastian Bergmann", 1458 | "email": "sebastian@phpunit.de" 1459 | } 1460 | ], 1461 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1462 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1463 | "support": { 1464 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1465 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" 1466 | }, 1467 | "funding": [ 1468 | { 1469 | "url": "https://github.com/sebastianbergmann", 1470 | "type": "github" 1471 | } 1472 | ], 1473 | "time": "2020-10-26T13:14:26+00:00" 1474 | }, 1475 | { 1476 | "name": "sebastian/recursion-context", 1477 | "version": "4.0.5", 1478 | "source": { 1479 | "type": "git", 1480 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1481 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" 1482 | }, 1483 | "dist": { 1484 | "type": "zip", 1485 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", 1486 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", 1487 | "shasum": "" 1488 | }, 1489 | "require": { 1490 | "php": ">=7.3" 1491 | }, 1492 | "require-dev": { 1493 | "phpunit/phpunit": "^9.3" 1494 | }, 1495 | "type": "library", 1496 | "extra": { 1497 | "branch-alias": { 1498 | "dev-master": "4.0-dev" 1499 | } 1500 | }, 1501 | "autoload": { 1502 | "classmap": [ 1503 | "src/" 1504 | ] 1505 | }, 1506 | "notification-url": "https://packagist.org/downloads/", 1507 | "license": [ 1508 | "BSD-3-Clause" 1509 | ], 1510 | "authors": [ 1511 | { 1512 | "name": "Sebastian Bergmann", 1513 | "email": "sebastian@phpunit.de" 1514 | }, 1515 | { 1516 | "name": "Jeff Welch", 1517 | "email": "whatthejeff@gmail.com" 1518 | }, 1519 | { 1520 | "name": "Adam Harvey", 1521 | "email": "aharvey@php.net" 1522 | } 1523 | ], 1524 | "description": "Provides functionality to recursively process PHP variables", 1525 | "homepage": "https://github.com/sebastianbergmann/recursion-context", 1526 | "support": { 1527 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1528 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" 1529 | }, 1530 | "funding": [ 1531 | { 1532 | "url": "https://github.com/sebastianbergmann", 1533 | "type": "github" 1534 | } 1535 | ], 1536 | "time": "2023-02-03T06:07:39+00:00" 1537 | }, 1538 | { 1539 | "name": "sebastian/resource-operations", 1540 | "version": "3.0.4", 1541 | "source": { 1542 | "type": "git", 1543 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1544 | "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" 1545 | }, 1546 | "dist": { 1547 | "type": "zip", 1548 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", 1549 | "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", 1550 | "shasum": "" 1551 | }, 1552 | "require": { 1553 | "php": ">=7.3" 1554 | }, 1555 | "require-dev": { 1556 | "phpunit/phpunit": "^9.0" 1557 | }, 1558 | "type": "library", 1559 | "extra": { 1560 | "branch-alias": { 1561 | "dev-main": "3.0-dev" 1562 | } 1563 | }, 1564 | "autoload": { 1565 | "classmap": [ 1566 | "src/" 1567 | ] 1568 | }, 1569 | "notification-url": "https://packagist.org/downloads/", 1570 | "license": [ 1571 | "BSD-3-Clause" 1572 | ], 1573 | "authors": [ 1574 | { 1575 | "name": "Sebastian Bergmann", 1576 | "email": "sebastian@phpunit.de" 1577 | } 1578 | ], 1579 | "description": "Provides a list of PHP built-in functions that operate on resources", 1580 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1581 | "support": { 1582 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" 1583 | }, 1584 | "funding": [ 1585 | { 1586 | "url": "https://github.com/sebastianbergmann", 1587 | "type": "github" 1588 | } 1589 | ], 1590 | "time": "2024-03-14T16:00:52+00:00" 1591 | }, 1592 | { 1593 | "name": "sebastian/type", 1594 | "version": "3.2.1", 1595 | "source": { 1596 | "type": "git", 1597 | "url": "https://github.com/sebastianbergmann/type.git", 1598 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" 1599 | }, 1600 | "dist": { 1601 | "type": "zip", 1602 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", 1603 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", 1604 | "shasum": "" 1605 | }, 1606 | "require": { 1607 | "php": ">=7.3" 1608 | }, 1609 | "require-dev": { 1610 | "phpunit/phpunit": "^9.5" 1611 | }, 1612 | "type": "library", 1613 | "extra": { 1614 | "branch-alias": { 1615 | "dev-master": "3.2-dev" 1616 | } 1617 | }, 1618 | "autoload": { 1619 | "classmap": [ 1620 | "src/" 1621 | ] 1622 | }, 1623 | "notification-url": "https://packagist.org/downloads/", 1624 | "license": [ 1625 | "BSD-3-Clause" 1626 | ], 1627 | "authors": [ 1628 | { 1629 | "name": "Sebastian Bergmann", 1630 | "email": "sebastian@phpunit.de", 1631 | "role": "lead" 1632 | } 1633 | ], 1634 | "description": "Collection of value objects that represent the types of the PHP type system", 1635 | "homepage": "https://github.com/sebastianbergmann/type", 1636 | "support": { 1637 | "issues": "https://github.com/sebastianbergmann/type/issues", 1638 | "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" 1639 | }, 1640 | "funding": [ 1641 | { 1642 | "url": "https://github.com/sebastianbergmann", 1643 | "type": "github" 1644 | } 1645 | ], 1646 | "time": "2023-02-03T06:13:03+00:00" 1647 | }, 1648 | { 1649 | "name": "sebastian/version", 1650 | "version": "3.0.2", 1651 | "source": { 1652 | "type": "git", 1653 | "url": "https://github.com/sebastianbergmann/version.git", 1654 | "reference": "c6c1022351a901512170118436c764e473f6de8c" 1655 | }, 1656 | "dist": { 1657 | "type": "zip", 1658 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", 1659 | "reference": "c6c1022351a901512170118436c764e473f6de8c", 1660 | "shasum": "" 1661 | }, 1662 | "require": { 1663 | "php": ">=7.3" 1664 | }, 1665 | "type": "library", 1666 | "extra": { 1667 | "branch-alias": { 1668 | "dev-master": "3.0-dev" 1669 | } 1670 | }, 1671 | "autoload": { 1672 | "classmap": [ 1673 | "src/" 1674 | ] 1675 | }, 1676 | "notification-url": "https://packagist.org/downloads/", 1677 | "license": [ 1678 | "BSD-3-Clause" 1679 | ], 1680 | "authors": [ 1681 | { 1682 | "name": "Sebastian Bergmann", 1683 | "email": "sebastian@phpunit.de", 1684 | "role": "lead" 1685 | } 1686 | ], 1687 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1688 | "homepage": "https://github.com/sebastianbergmann/version", 1689 | "support": { 1690 | "issues": "https://github.com/sebastianbergmann/version/issues", 1691 | "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" 1692 | }, 1693 | "funding": [ 1694 | { 1695 | "url": "https://github.com/sebastianbergmann", 1696 | "type": "github" 1697 | } 1698 | ], 1699 | "time": "2020-09-28T06:39:44+00:00" 1700 | }, 1701 | { 1702 | "name": "theseer/tokenizer", 1703 | "version": "1.2.3", 1704 | "source": { 1705 | "type": "git", 1706 | "url": "https://github.com/theseer/tokenizer.git", 1707 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" 1708 | }, 1709 | "dist": { 1710 | "type": "zip", 1711 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1712 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1713 | "shasum": "" 1714 | }, 1715 | "require": { 1716 | "ext-dom": "*", 1717 | "ext-tokenizer": "*", 1718 | "ext-xmlwriter": "*", 1719 | "php": "^7.2 || ^8.0" 1720 | }, 1721 | "type": "library", 1722 | "autoload": { 1723 | "classmap": [ 1724 | "src/" 1725 | ] 1726 | }, 1727 | "notification-url": "https://packagist.org/downloads/", 1728 | "license": [ 1729 | "BSD-3-Clause" 1730 | ], 1731 | "authors": [ 1732 | { 1733 | "name": "Arne Blankerts", 1734 | "email": "arne@blankerts.de", 1735 | "role": "Developer" 1736 | } 1737 | ], 1738 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1739 | "support": { 1740 | "issues": "https://github.com/theseer/tokenizer/issues", 1741 | "source": "https://github.com/theseer/tokenizer/tree/1.2.3" 1742 | }, 1743 | "funding": [ 1744 | { 1745 | "url": "https://github.com/theseer", 1746 | "type": "github" 1747 | } 1748 | ], 1749 | "time": "2024-03-03T12:36:25+00:00" 1750 | } 1751 | ], 1752 | "aliases": [], 1753 | "minimum-stability": "stable", 1754 | "stability-flags": {}, 1755 | "prefer-stable": false, 1756 | "prefer-lowest": false, 1757 | "platform": {}, 1758 | "platform-dev": {}, 1759 | "platform-overrides": { 1760 | "php": "8.1" 1761 | }, 1762 | "plugin-api-version": "2.6.0" 1763 | } 1764 | -------------------------------------------------------------------------------- /vendor-bin/psalm/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "vimeo/psalm": "^6" 4 | }, 5 | "config": { 6 | "platform": { 7 | "php": "8.1.17" 8 | } 9 | } 10 | } 11 | --------------------------------------------------------------------------------