diff --git a/.env.dist b/.env.dist index e80ae27f..dd0e0373 100644 --- a/.env.dist +++ b/.env.dist @@ -1,4 +1,5 @@ -APP_ENV=develope +#docker configuration +ENV=dev USERMAP_UID=1000 USERMAP_GID=984 MYSQL_USER=dev @@ -6,7 +7,6 @@ MYSQL_PASSWORD=dev MYSQL_ROOT_PASSWORD=root MYSQL_DATABASE=db MYSQL_PUBLIC_PORT=3306 -MYSQL_TESTING_PORT=3307 PHP_VERSION=8.3 MARIADB_VERSION=latest APACHE_VERSION=2.4-alpine diff --git a/.github/actions/setup-php-composer/action.yml b/.github/actions/setup-php-composer/action.yml deleted file mode 100644 index aae13386..00000000 --- a/.github/actions/setup-php-composer/action.yml +++ /dev/null @@ -1,30 +0,0 @@ -# .github/actions/setup-php-composer/action.yml -name: 'Setup PHP and Composer' -description: 'Common setup for PHP jobs' -inputs: - php-version: - description: 'PHP Version' - default: '8.4' - install-flags: - description: 'Composer install flags' - default: '--prefer-dist --no-progress' - -runs: - using: "composite" - steps: - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ inputs.php-version }} - extensions: pdo, mbstring # hier Standard-Extensions ergänzen - - - name: Cache Composer packages - uses: actions/cache@v5 - with: - path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-php- - - - name: Install dependencies - shell: bash - run: composer install ${{ inputs.install-flags }} diff --git a/.github/workflows/codestyle-and-unittest.yml b/.github/workflows/codestyle-and-unittest.yml index 7ab5f60a..b64b4dfa 100644 --- a/.github/workflows/codestyle-and-unittest.yml +++ b/.github/workflows/codestyle-and-unittest.yml @@ -4,107 +4,147 @@ on: branches: - '*' - '!master' - - '!develop' paths: - "**.php" - workflow_dispatch: + pull_request: jobs: - # 1. Job: Statische Analyse (Lint, Stan, CodeSniffer) - # Diese laufen zusammen, da sie keine Datenbank benötigen. - static-analysis: - name: PHP Quality Checks - runs-on: ubuntu-latest + phplint: + name: PHP Lint + runs-on: ubuntu-20.04 + steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@v4.1.1 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- - - name: Setup PHP and Composer - uses: ./.github/actions/setup-php-composer + - name: Install dependencies + run: composer install --prefer-dist --no-progress - name: Run PHP Linter run: composer run-script phplint + phpstan: + name: PHP Stan + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4.1.1 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + - name: Run PHP Stan run: composer run-script phpstan + phpcs: + name: PHP CodeSniffer + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4.1.1 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + - name: Run PHP Codesniffer run: composer run-script phpcs - - name: Notify Failure - if: failure() - uses: rjstone/discord-webhook-notify@v1.0.4 - with: - severity: error - details: Static Analysis (Lint/Stan/CS) failed. - webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} - - # 2. Job: Tests (Unit & Functional) - # Dieser Job benötigt die MariaDB Service-Container - tests: - name: PHP Tests - needs: static-analysis - runs-on: ubuntu-latest - services: - database-testing: - image: mariadb:latest - env: - MYSQL_ALLOW_EMPTY_PASSWORD: false - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: db - MYSQL_USER: dev - MYSQL_PASSWORD: dev - ports: - - 3306:3306 - options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3 - env: - DB_DATABASE: db - DB_USERNAME: dev - DB_PASSWORD: dev - DB_HOST: 127.0.0.1 - DB_PORT: 3306 - APP_ENV: action + phpunit: + name: PHP Unit Test + needs: + - phplint + - phpstan + - phpcs + runs-on: ubuntu-20.04 + steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@v4.1.1 - - name: Setup PHP and Composer - uses: ./.github/actions/setup-php-composer + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress - name: Run PHP Unit Test run: composer run-script unittest - - name: Run PHP Functional Test - run: composer run-script functionaltest + phpfunctional: + name: PHP Functional Test + needs: + - phplint + - phpstan + - phpcs + runs-on: ubuntu-20.04 - - name: Notify Failure - if: failure() - uses: rjstone/discord-webhook-notify@v1.0.4 - with: - severity: error - details: Unit or Functional Tests failed. - webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} - - # 3. Job: Abschluss-Benachrichtigung - final-status: - name: Final Status Message - needs: [static-analysis, tests] - if: always() - runs-on: ubuntu-latest steps: - - name: Check Info Success - if: ${{ needs.static-analysis.result == 'success' && needs.tests.result == 'success' }} - uses: rjstone/discord-webhook-notify@v1.0.4 + - uses: actions/checkout@v4.1.1 + + - name: Install PHP with extensions. + uses: shivammathur/setup-php@v2 with: - severity: info - details: Checks successfully executed on API. - webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} + php-version: 8.3 + extensions: pdo, pdo_sqlite + ini-values: date.timezone='UTC' - - name: Check Info Failure - if: ${{ needs.static-analysis.result != 'success' || needs.tests.result != 'success' }} - uses: rjstone/discord-webhook-notify@v1.0.4 + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 with: - severity: error - details: >- - ${{ format('❌ Incorrect checks during the API check. [View Logs](https://github.com/{0}/actions/runs/{1})', - github.repository, - github.run_id) }} - webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run PHP Unit Test + run: composer run-script functionaltest diff --git a/.github/workflows/deploy_build.yml b/.github/workflows/deploy_build.yml deleted file mode 100644 index 1bb88d73..00000000 --- a/.github/workflows/deploy_build.yml +++ /dev/null @@ -1,121 +0,0 @@ -name: Deployment on Build -on: - push: - branches: [ "master" ] - workflow_dispatch: - -jobs: - # 1. Statische Analyse (Zusammengefasst, da sie keine DB brauchen) - static-analysis: - name: PHP Quality Checks - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6.0.2 - - - name: Setup Environment - uses: ./.github/actions/setup-php-composer - - - name: Validate Composer - run: composer validate --strict - - - name: Run Checks - run: | - composer run-script phplint - composer run-script phpstan - composer run-script phpcs - - - name: Notify Failure - if: failure() - uses: rjstone/discord-webhook-notify@v1.0.4 - with: - severity: error - details: Static Analysis (Lint/Stan/CS) failed. - webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} - - # 2. Tests (Unit & Functional getrennt, da Functional DB braucht) - tests: - name: PHP Tests - needs: static-analysis - runs-on: ubuntu-latest - services: - mariadb: - image: mariadb:latest - env: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: db - MYSQL_USER: dev - MYSQL_PASSWORD: dev - ports: ["3306:3306"] - options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3 - env: - DB_DATABASE: db - DB_USERNAME: dev - DB_PASSWORD: dev - DB_HOST: 127.0.0.1 - APP_ENV: action - steps: - - uses: actions/checkout@v6.0.2 - - name: Setup Environment - uses: ./.github/actions/setup-php-composer - - - name: Run Unit Tests - run: composer run-script unittest - - - name: Run Functional Tests - run: composer run-script functionaltest - - - name: Notify Failure - if: failure() - uses: rjstone/discord-webhook-notify@v1.0.4 - with: - severity: error - details: PHP Tests failed. - webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} - - # 3. Deployment - deploy: - name: Deploy to Dev - needs: tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6.0.2 - - - name: Setup Environment (No Dev) - uses: ./.github/actions/setup-php-composer - with: - install-flags: '--prefer-dist --no-progress --no-dev' - - - name: Build OpenAPI - run: composer run-script openapi - - - name: Deploy via rsync - uses: burnett01/rsync-deployments@8.0.3 - with: - switches: -avz --no-o --no-g --no-perms --no-t --omit-dir-times --delete --exclude-from='.rsync-exclude' - remote_path: ${{ secrets.DEPLOY_PATH_API_BUILD }} - remote_host: ${{ secrets.DEPLOY_HOST }} - remote_port: ${{ secrets.DEPLOY_PORT }} - remote_user: ${{ secrets.DEPLOY_USER }} - remote_key: ${{ secrets.DEPLOY_KEY }} - remote_key_pass: ${{ secrets.DEPLOY_KEY_PASS }} - - - name: Clear Cache - uses: appleboy/ssh-action@v1.2.2 - with: - host: ${{ secrets.DEPLOY_HOST }} - username: ${{ secrets.DEPLOY_USER }} - key: ${{ secrets.DEPLOY_KEY }} - passphrase: ${{ secrets.DEPLOY_KEY_PASS }} - script: rm -rf ${{ secrets.DEPLOY_PATH_API_BUILD }}/data/cache/* - - - name: Deployment Notification - if: always() - uses: rjstone/discord-webhook-notify@v1.0.4 - with: - severity: ${{ job.status == 'success' && 'info' || 'error' }} - details: >- - ${{ job.status == 'success' - && '[🚀 Successful deployment to build!](https://build.ownhackathon.de/api/docs)' - || format('[❌ Deployment failed! View Logs](https://github.com/{0}/actions/runs/{1})', github.repository, github.run_id) - }} - webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} diff --git a/.github/workflows/deploy_dev.yml b/.github/workflows/deploy_dev.yml deleted file mode 100644 index 13b9c356..00000000 --- a/.github/workflows/deploy_dev.yml +++ /dev/null @@ -1,121 +0,0 @@ -name: Deployment on Development -on: - push: - branches: [ "develop" ] - workflow_dispatch: - -jobs: - # 1. Statische Analyse (Zusammengefasst, da sie keine DB brauchen) - static-analysis: - name: PHP Quality Checks - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6.0.2 - - - name: Setup Environment - uses: ./.github/actions/setup-php-composer - - - name: Validate Composer - run: composer validate --strict - - - name: Run Checks - run: | - composer run-script phplint - composer run-script phpstan - composer run-script phpcs - - - name: Notify Failure - if: failure() - uses: rjstone/discord-webhook-notify@v1.0.4 - with: - severity: error - details: Static Analysis (Lint/Stan/CS) failed. - webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} - - # 2. Tests (Unit & Functional getrennt, da Functional DB braucht) - tests: - name: PHP Tests - needs: static-analysis - runs-on: ubuntu-latest - services: - mariadb: - image: mariadb:latest - env: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: db - MYSQL_USER: dev - MYSQL_PASSWORD: dev - ports: ["3306:3306"] - options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3 - env: - DB_DATABASE: db - DB_USERNAME: dev - DB_PASSWORD: dev - DB_HOST: 127.0.0.1 - APP_ENV: action - steps: - - uses: actions/checkout@v6.0.2 - - name: Setup Environment - uses: ./.github/actions/setup-php-composer - - - name: Run Unit Tests - run: composer run-script unittest - - - name: Run Functional Tests - run: composer run-script functionaltest - - - name: Notify Failure - if: failure() - uses: rjstone/discord-webhook-notify@v1.0.4 - with: - severity: error - details: PHP Tests failed. - webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} - - # 3. Deployment - deploy: - name: Deploy to Dev - needs: tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6.0.2 - - - name: Setup Environment (No Dev) - uses: ./.github/actions/setup-php-composer - with: - install-flags: '--prefer-dist --no-progress --no-dev' - - - name: Build OpenAPI - run: composer run-script openapi - - - name: Deploy via rsync - uses: burnett01/rsync-deployments@8.0.3 - with: - switches: -avz --no-o --no-g --no-perms --no-t --omit-dir-times --delete --exclude-from='.rsync-exclude' - remote_path: ${{ secrets.DEPLOY_PATH_API_DEV }} - remote_host: ${{ secrets.DEPLOY_HOST }} - remote_port: ${{ secrets.DEPLOY_PORT }} - remote_user: ${{ secrets.DEPLOY_USER }} - remote_key: ${{ secrets.DEPLOY_KEY }} - remote_key_pass: ${{ secrets.DEPLOY_KEY_PASS }} - - - name: Clear Cache - uses: appleboy/ssh-action@v1.2.2 - with: - host: ${{ secrets.DEPLOY_HOST }} - username: ${{ secrets.DEPLOY_USER }} - key: ${{ secrets.DEPLOY_KEY }} - passphrase: ${{ secrets.DEPLOY_KEY_PASS }} - script: rm -rf ${{ secrets.DEPLOY_PATH_API_DEV }}/data/cache/* - - - name: Deployment Notification - if: always() - uses: rjstone/discord-webhook-notify@v1.0.4 - with: - severity: ${{ job.status == 'success' && 'info' || 'error' }} - details: >- - ${{ job.status == 'success' - && '[🚀 Successful deployment to dev!](https://dev.ownhackathon.de/api/docs)' - || format('[❌ Deployment failed! View Logs](https://github.com/{0}/actions/runs/{1})', github.repository, github.run_id) - }} - webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} diff --git a/.github/workflows/deployment_build.yml b/.github/workflows/deployment_build.yml new file mode 100644 index 00000000..d7edda4f --- /dev/null +++ b/.github/workflows/deployment_build.yml @@ -0,0 +1,234 @@ +name: Development Deployment +on: + push: + branches: [ "master" ] + workflow_dispatch: + +jobs: + + phplint: + name: PHP Lint + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4.1.1 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run PHP Linter + run: composer run-script phplint + + - name: Test Info + uses: rjstone/discord-webhook-notify@v1 + with: + severity: error + details: Code-Style error. + webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} + if: failure() + + phpstan: + name: PHP Stan + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4.1.1 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run PHP Stan + run: composer run-script phpstan + + - name: Test Info + uses: rjstone/discord-webhook-notify@v1 + with: + severity: error + details: Code-Style error. + webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} + if: failure() + + phpcs: + name: PHP CodeSniffer + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4.1.1 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run PHP Codesniffer + run: composer run-script phpcs + + - name: Test Info + uses: rjstone/discord-webhook-notify@v1 + with: + severity: error + details: Code-Style error. + webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} + if: failure() + + phpunit: + name: PHP Unit Test + needs: + - phplint + - phpstan + - phpcs + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4.1.1 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run PHP Unit Test + run: composer run-script unittest + + - name: Test Info + uses: rjstone/discord-webhook-notify@v1 + with: + severity: error + details: Test error. + webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} + if: failure() + + phpfunctional: + name: PHP Functional Test + needs: + - phplint + - phpstan + - phpcs + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4.1.1 + + - name: Install PHP with extensions. + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + extensions: pdo, pdo_sqlite + ini-values: date.timezone='UTC' + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run PHP Unit Test + run: composer run-script functionaltest + + - name: Test Info + uses: rjstone/discord-webhook-notify@v1 + with: + severity: error + details: Test error. + webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} + if: failure() + + deployment_of_hackathon_to_build: + name: Deploy Build + needs: + - phpcs + - phplint + - phpstan + - phpunit + - phpfunctional + runs-on: ubuntu-20.04 + steps: + - name: Get latest code + uses: actions/checkout@v4.1.1 + + - name: Install Composer Dependencies + run: composer install --prefer-dist --no-progress --no-dev + + - name: Run create openapi Data + run: composer run-script openapi + + - name: rsync deployments + uses: burnett01/rsync-deployments@7.0.1 + with: + switches: -avz --no-o --no-g --no-perms --no-t --omit-dir-times --delete --exclude-from='.rsync-exclude' + path: / + remote_path: ${{ secrets.DEPLOY_PATH_API_BUILD }} + remote_host: ${{ secrets.DEPLOY_HOST }} + remote_port: ${{ secrets.DEPLOY_PORT }} + remote_user: ${{ secrets.DEPLOY_USER }} + remote_key: ${{ secrets.DEPLOY_KEY }} + remote_key_pass: ${{ secrets.DEPLOY_KEY_PASS }} + + - name: Test Info Success + uses: rjstone/discord-webhook-notify@v1 + with: + severity: info + details: Successful deployment on build https://build.hackathon.exdrals.de + webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} + if: success() + + - name: Test Info Failure + uses: rjstone/discord-webhook-notify@v1 + with: + severity: error + details: Failure deployment on build. + webhookUrl: ${{ secrets.WEBHOOK_DISCORD_URL }} + if: failure() diff --git a/.github/workflows/deployment_production.yml b/.github/workflows/deployment_production.yml new file mode 100644 index 00000000..68c01dfa --- /dev/null +++ b/.github/workflows/deployment_production.yml @@ -0,0 +1,51 @@ +name: Production Deployment + +on: + release: + types: [ published ] + +jobs: + deployment_of_hackathon_to_production: + env: + VUE_APP_API_BASE_URL: 'https://hackathon.exdrals.de' + runs-on: ubuntu-20.04 + steps: + - name: Get latest code + uses: actions/checkout@v2 + + - name: Install Composer Dependencies + run: composer install --prefer-dist --no-progress --no-dev + + - name: Run create openapi Data + run: composer run-script openapi + + - name: FTP Deploy to Production Server (Staging) + uses: SamKirkland/FTP-Deploy-Action@4.3.2 + with: + server: ${{ secrets.PROD_HOST }} + username: ${{ secrets.PROD_USER }} + password: ${{ secrets.PROD_PASSWORD }} + exclude: | + **/.git* + **/*.dist + **/*.dist/** + **/.git*/** + **/bin/** + **/tests/** + **/config/autoload/** + **/config/migrations/** + **/node_modules/** + **/client/** + **/database/** + **/scripts/** + **/tests/** + **/docker/** + **/public/assets/** + **/public/index.html + **/*.md + **/*.xml + **/*.neon + **/*.json + **/*.yml + **/*.lock + **/LICENSE diff --git a/.gitignore b/.gitignore index e3d8f26f..e3dcfbf5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ /tests/behat.yml /tests/.phpunit.result.cache /vendor/ -/public/assets/* *.cache *.phar .ddev @@ -10,11 +9,4 @@ /database/structure/update*.sql /public/api/doc/swagger.json coverage.xml -/.phpcs-cache -/.phpunit.result.cache -/clover.xml -/coveralls-upload.json -/phpunit.xml -/public/test.php -/tmp -.env +/.env diff --git a/.laminas-ci/pre-run.sh b/.laminas-ci/pre-run.sh deleted file mode 100755 index 8b8528d8..00000000 --- a/.laminas-ci/pre-run.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -# Due to the fact that we are disabling plugins when installing/updating/downgrading composer dependencies -# we have to manually enable the coding standard here. -composer enable-codestandard diff --git a/.phplint.yml b/.phplint.yml index 2264a4f4..c4f8ebb9 100644 --- a/.phplint.yml +++ b/.phplint.yml @@ -1,4 +1,4 @@ -path: ./ +path: ./src jobs: 10 cache: .phplint.cache extensions: diff --git a/.rsync-exclude b/.rsync-exclude index 97e349aa..08a0a9c2 100644 --- a/.rsync-exclude +++ b/.rsync-exclude @@ -1,33 +1,27 @@ -/.github -/.laminas-ci -/bin/ -/config/.gitignore -/config/*.dist -/config/*.tpl -/config/autoload/.gitignore -/config/autoload/*.dist -/config/autoload/*.local.* -/config/autoload/*.testing.* -/config/autoload/*.develope.* -/config/autoload/*.action.* -/config/autoload/migrations.* -/database/ -/data/.gitignore -/data/log/** -/docker/ -/docs/ -/public/.* -/public/*.php -/public/*.html -/public/assets/ +/.git +/*.dist +/bin /tests/ -/.* -/*.json -/*.lock -/*.yml -/*.yaml +/config/autoload/*.dist +/config/autoload/*.develope.php +/config/autoload/*.local.php +/config/autoload/*.testing.php +/config/*.dist +/node_modules +/client +/database +/data/log/* +/scripts +/docker +/public/assets +/public/index.html +/*.md /*.xml /*.neon -/*.md +/composer.* +/*.yml /LICENSE -/README +/.github +/docs +.env* +.rsync-exclude diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..1fe5a2f2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,37 @@ + + +# Changelog for the Hackathon evaluation Project + + + + + + + + + + + + + + + +## Unreleased + +### Added +- add Changelog #74 @BibaltiK +- add Issues Templates #77 @BibaltiK + + + + + + + + + + diff --git a/README.md b/README.md index 384ea0da..0e8b24fc 100644 --- a/README.md +++ b/README.md @@ -1,103 +1,34 @@ -# ownHackathon -Evaluation Project for Hackathons +# (Black) Hackathon +Evaluation project for the Hackathon Events on the Discord server from BlackScorp -## Setup: Executable Test Environment +## Steps for an executable test environment -Follow these steps to set up the project on your local machine. +1. Install `git` and `docker` on your maschine +2. Run `git clone git@github.com:ownHackathon/hackathon-api.git` +3. Optional: Check the configurations in `config/autoload`. In case of changes, copy te file and remove the `.dist` file extension and adjust + the configuration file. +4. Copy `.env.dist` and rename it `.env` and set correct your userid and groupid. You can find them out in the terminal via commands `id -u && id -g` +5. Run `docker-compose up -d` +6. Run `docker-compose exec php composer install` +7. Run `docker-compose exec php composer run doctrine migrations:sync-metadata-storage` +8. Run `docker-compose exec php composer run doctrine migrations:migrate` +9. Run `docker-compose exec php composer run openapi` -### 1. Prerequisites -Ensure you have `git` and `docker` (including the Docker Compose plugin) installed. +Done. You can now open http://localhost/api/doc/ Thanks and have fun. -### 2. Clone the Repository -```bash -git clone git@github.com:ownHackathon/hackathon-api.git -cd hackathon-api -``` +See docker-compose.yml for existing services -### 3. Environment Configuration -Copy the environment template and adjust it: -```bash -cp .env.dist .env -``` -Open the `.env` file and set your `USERMAP_UID` and `USERMAP_GID`. This ensures correct file permissions within the container. You can find your IDs by running: -```bash -id -u && id -g -``` +# unsupportet Script -### 4. Application Configuration (Optional) -Check the configuration files in `config/autoload/`. If changes are needed, copy the desired `.dist` file, remove the extension, and adjust the settings. +You will find a script called `hackathon` under `/bin`. This offers possibilities to control the project -### 5. Quick Setup (Recommended) -We provide a management script to automate the entire process (infrastructure start, dependency installation, database migrations, and documentation generation). +- `./bin/hackathon setup` => start the docker container, run composer install and seed Database Data +- `./bin/hackathon start` => start the docker container +- `./bin/hackathon restart` => restart the docker container +- `./bin/hackathon stop` => stop the docker container +- `./bin/hackathon reset` => clean up Database +- `./bin/hackathon reset vendor` => clean up vendor folder +- `./bin/hackathon reset all` => clean up system completely +- `./bin/hackathon composer` => run composer with own param e.g. `./bin/hackathon composer install` +- `./bin/hackathon php` => run commands in php container e.g. `./bin/hackathon php php -v` -**Once finished, the script automatically displays a table with all service URLs, ports, and database credentials.** - -> [!CAUTION] -> **NOTE:** This script is provided **as-is and unsupported**. Use it at your own risk. - -```bash -# Make the script executable -chmod +x bin/hackathon - -# Run the automated setup -./bin/hackathon setup -``` - -*Alternatively, you can run the commands manually:* -
-Show manual steps - -```bash -docker-compose up -d -docker-compose exec php composer install -docker-compose exec php composer run doctrine migrations:sync-metadata-storage -docker-compose exec php composer run doctrine migrations:migrate -docker-compose exec php composer run openapi -``` -
- ---- - -## Unsupported Management CLI via `./bin/hackathon` - -The management script consolidates all essential developer commands into a single tool. - -### Usage -Run the script from the project root directory: -```bash -./bin/hackathon [COMMAND] -``` - -#### Infrastructure Commands -* **`start`**: Starts the containers. **Note:** It checks for initialization (vendor/ and volumes) and prevents start if the project isn't set up. -* **`stop`** / **`down`**: Pauses containers or stops/removes containers and networks. -* **`setup`**: Complete initial setup (Start, Install, Migrate, OpenAPI, Info). -* **`services`**: Lists all available service names used in this project. -* **`logs [svc]`**: Tails logs for all or a specific service (e.g., `./bin/hackathon logs php`). -* **`info`**: Displays connectivity info (URLs, Ports, and DB Credentials) for running services. -* **`openapi`**: Regenerates the API documentation. - -#### Cleanup & Reset -* **`clean {docker|app|all}`**: - * `docker`: Removes containers, volumes, and **all** project images. - * `app`: Removes `vendor/` and all cache files (`.phplint`, `.phpunit`, etc.). - * `all`: Performs both docker and app cleanup. -* **`reset {database|vendor|all}`**: - * `database`: Wipes database volumes and re-runs migrations. - * `vendor`: Wipes and reinstalls the `vendor/` folder (Database remains untouched). - * `all`: Wipes database, vendor, and all caches, followed by a fresh `setup`. - -#### Development & Utility Commands -* **`composer [...]`**: Run Composer commands in the PHP container. -* **`php [...]`**: Run PHP commands in the PHP container. -* **`test [...]`**: Shortcut to run PHPUnit tests (passes arguments to PHPUnit). -* **`bash`**: Direct interactive shell access to the PHP container. -* **`mysql`**: Direct access to the MariaDB database console. -* **`indocker [service] [command]`**: Access any specific container (`php`, `apache`, `database`, `database-testing`, `mailhog`). - ---- - -### 💡 Pro-Tip -You can create an alias in your `.bashrc` or `.zshrc` to work even faster: -`alias h='./bin/hackathon'` -> Then simply use `h setup`, `h info`, `h bash` or `h test`. -``` diff --git a/bin/hackathon b/bin/hackathon index 523b6ccf..93c6f4cc 100755 --- a/bin/hackathon +++ b/bin/hackathon @@ -1,320 +1,99 @@ -#!/usr/bin/env bash +#!/usr/bin/bash +export APP_ENV=${APP_ENV:-develope} -# --- Configuration --- -CONTAINER_PHP="php" -DOCKER_COMPOSE="docker compose" - -# Get project name (directory name, lowercase) -PROJECT_NAME=$(basename "$PWD" | tr '[:upper:]' '[:lower:]') - -# Colors -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' # No Color - -# Load environment variables from .env if present -if [ -f .env ]; then - export $(grep -v '^#' .env | xargs) -fi - -# --- Helper Functions --- - -log() { echo -e "${GREEN}[INFO]${NC} $1"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } -error() { echo -e "${RED}[ERROR]${NC} $1"; } - -# Check if a specific container is running -is_running() { - [ "$(docker inspect -f '{{.State.Running}}' "$1" 2>/dev/null)" == "true" ] +function composer_install() { + docker-compose exec php composer install } -# List all valid service names defined in this project -list_services() { - log "Available services in this project:" - echo "-----------------------------------------------------------" - echo -e " ${GREEN}php${NC} - PHP Application Container" - echo -e " ${GREEN}apache${NC} - Web Server (Apache)" - echo -e " ${GREEN}database${NC} - MariaDB Main Instance" - echo -e " ${GREEN}database-testing${NC} - MariaDB Testing Instance" - echo -e " ${GREEN}mailhog${NC} - Mail Testing Service" - echo "-----------------------------------------------------------" +function setup_database() { + docker-compose exec --env APP_ENV php composer run doctrine migrations:sync-metadata-storage + docker-compose exec --env APP_ENV php composer run doctrine migrations:migrate --no-interaction } -# Robust check for initialization -check_initialization() { - local initialized=true - - # 1. Check for vendor directory - if [ ! -d "vendor" ]; then - warn "Dependency check failed: directory 'vendor/' is missing." - initialized=false - fi - - # 2. Check for database volumes - local volume_exists=$(docker volume ls -q --filter "label=com.docker.compose.project" --filter "label=com.docker.compose.volume=db") - - if [ -z "$volume_exists" ]; then - warn "Infrastructure check failed: Database volume 'db' not found." - initialized=false - fi - - if [ "$initialized" = false ]; then - error "Project is not fully initialized." - echo -e "Please run ${GREEN}./bin/hackathon setup${NC} to initialize the environment." - exit 1 - fi -} - -show_info() { - echo "" - log "Service Connectivity Information:" - echo "-----------------------------------------------------------" - if is_running "hackathon-apache"; then - echo -e "Web Interface: http://localhost:${HTTP_PORT:-80}" - echo -e "API Documentation: http://localhost:${HTTP_PORT:-80}/api/docs/" - fi - if is_running "hackathon-mailhog"; then - echo -e "Mailhog Web-UI: http://localhost:${MAILHOG_WEBUI_PORT:-8025}" - echo -e "Mailhog SMTP Port: localhost:${MAILHOG_SMTP_PORT:-1025}" - fi - if is_running "hackathon-mariadb"; then - echo -e "Database (App): localhost:${MYSQL_PUBLIC_PORT:-3306}" - echo -e " User: ${MYSQL_USER:-dev} | Pass: ${MYSQL_PASSWORD:-dev}" - fi - if is_running "hackathon-mariadb-testing"; then - echo -e "Database (Test): localhost:${MYSQL_TESTING_PORT:-3307}" - echo -e " User: ${MYSQL_USER:-dev} | Pass: ${MYSQL_PASSWORD:-dev}" - fi - echo "-----------------------------------------------------------" - echo "" -} - -up() { - log "Starting containers..." - $DOCKER_COMPOSE up -d +function docker_start() { + docker-compose up -d } -down() { - log "Stopping containers and removing networks..." - $DOCKER_COMPOSE down +function docker_down() { + docker-compose down } -cleanup_docker() { - log "Starting full Docker cleanup (containers, volumes, images)..." - $DOCKER_COMPOSE down -v --rmi all --remove-orphans - log "All Docker resources have been removed." +function docker_database_cleanup() { + docker volume rm hackathon-api_db } -cleanup_app() { - log "Cleaning application files and caches (preserving .gitkeep)..." - - # 1. Remove entire directories - local dirs=(".phplint.cache" ".phpunit.cache" ".phpunit.functional.cache" "vendor") - for dir in "${dirs[@]}"; do - if [ -d "$dir" ]; then - rm -rf "$dir" && log "-> $dir/ removed" - fi - done - - # 2. Clear contents of directories, but EXCLUDE .gitkeep - local content_dirs=("data/cache" "data/log") - for c_dir in "${content_dirs[@]}"; do - if [ -d "$c_dir" ]; then - # find: mindepth 1 (only contents), ! -name (not .gitkeep), -delete (remove) - find "$c_dir" -mindepth 1 ! -name ".gitkeep" -delete && log "-> contents of $c_dir/ cleared (except .gitkeep)" - fi - done - - # 3. Remove specific files - if [ -f ".phpcs-cache" ]; then - rm ".phpcs-cache" && log "-> .phpcs-cache removed" - fi +function docker_vendor_cleanup() { + rm -rf "./vendor" } -install_deps() { - if [ ! -d "./vendor" ] || [ "$1" == "--force" ]; then - log "Installing PHP dependencies..." - $DOCKER_COMPOSE exec $CONTAINER_PHP composer install - fi +function docker_cleanup() { + docker image rm -f ghcr.io/ownhackathon/hackathon-api-php:latest mariadb:latest mailhog/mailhog httpd:alpine + docker_database_cleanup + docker_vendor_cleanup } -migrate_db() { - log "Waiting for database readiness..." - $DOCKER_COMPOSE exec $CONTAINER_PHP php -r " - \$start = time(); - while (true) { - try { - new PDO('mysql:host=database;port=3306', 'root', '${MYSQL_ROOT_PASSWORD:-root}'); - break; - } catch (Exception \$e) { - if (time() - \$start > 30) { echo 'Timeout!'; exit(1); } - echo '.'; sleep(1); - } - }" - echo "" - log "Running migrations..." - $DOCKER_COMPOSE exec $CONTAINER_PHP composer run doctrine migrations:sync-metadata-storage -- --no-interaction - $DOCKER_COMPOSE exec $CONTAINER_PHP composer run doctrine migrations:migrate -- --no-interaction -} - -generate_openapi() { - log "Generating OpenAPI documentation..." - $DOCKER_COMPOSE exec $CONTAINER_PHP composer run openapi -} - -build_stack() { - up - install_deps "$1" - migrate_db - generate_openapi - show_info -} - -# --- Command Handler --- - case $1 in "start") - check_initialization - up - show_info + docker_start ;; - "stop") - log "Pausing containers..." - $DOCKER_COMPOSE stop + + "restart") + docker-compose restart ;; - "down") - down + + "stop") + docker_down ;; + "setup") - build_stack - ;; - "openapi") - generate_openapi - ;; - "info") - show_info - ;; - "services") - list_services - ;; - "logs") - if [ -z "$2" ]; then - $DOCKER_COMPOSE logs -f - else - case $2 in - php|apache|database|database-testing|mailhog) $DOCKER_COMPOSE logs -f "$2" ;; - *) list_services; echo -e "Usage: $0 logs {${GREEN}svc${NC}}"; exit 1 ;; - esac - fi - ;; - "test") - $DOCKER_COMPOSE exec $CONTAINER_PHP vendor/bin/phpunit "${@:2}" - ;; - "mysql") - log "Connecting to Database Console..." - $DOCKER_COMPOSE exec database mariadb -u "${MYSQL_USER:-dev}" -p"${MYSQL_PASSWORD:-dev}" "${MYSQL_DATABASE:-db}" - ;; - "bash") - log "Entering PHP container..." - $DOCKER_COMPOSE exec $CONTAINER_PHP sh -c "which bash > /dev/null && bash || sh" - ;; + docker_start + composer_install + setup_database + ;; + "clean") - case $2 in - "docker") cleanup_docker ;; - "app") cleanup_app ;; - "all") cleanup_docker; cleanup_app ;; - *) - echo -e "Usage: $0 clean {${GREEN}docker${NC}|${GREEN}app${NC}|${GREEN}all${NC}}" - echo "" - echo "Options:" - echo -e " ${GREEN}docker${NC} - Remove all project containers, networks, volumes and ALL images" - echo -e " ${GREEN}app${NC} - Remove vendor/ folder, all cache directories and clear logs (preserves .gitkeep)" - echo -e " ${GREEN}all${NC} - Full cleanup (Combines both docker and app cleanup)" - exit 1 - ;; - esac - ;; + docker_vendor_cleanup + ;; + "reset") + echo "turning down docker container" + docker_down + case $2 in - "database") - log "Resetting database..." - $DOCKER_COMPOSE down -v - build_stack - ;; - "vendor") - log "Resetting vendor folder..." - [ -d "vendor" ] && rm -rf "vendor" - up - install_deps "--force" - show_info - ;; "all") - log "Resetting entire application stack..." - $DOCKER_COMPOSE down -v - cleanup_app - build_stack "--force" - ;; + echo "cleanup system completely" + docker_cleanup + ;; + "vendor") + echo "cleanup vendor folder" + docker_vendor_cleanup + ;; *) - echo -e "Usage: $0 reset {${GREEN}database${NC}|${GREEN}vendor${NC}|${GREEN}all${NC}}" - echo "" - echo "Options:" - echo -e " ${GREEN}database${NC} - Wipe database volumes and re-run migrations (restarts containers)" - echo -e " ${GREEN}vendor${NC} - Wipe vendor/ folder and reinstall dependencies (DB stays untouched)" - echo -e " ${GREEN}all${NC} - Wipe database, vendor/ and ALL caches, then perform fresh setup" - exit 1 + echo "cleanup database" + docker_database_cleanup ;; esac - ;; + + echo "turning on docker container" + docker_start + + echo "check and install vendor" + composer_install + + echo "wait for services" + sleep 5 + + echo "create database storage information an run migrations" + setup_database + + echo "done" + ;; + "composer") - $DOCKER_COMPOSE exec $CONTAINER_PHP composer "${@:2}" + docker-compose run --rm --env APP_ENV php composer "${@:2}" ;; + "php") - $DOCKER_COMPOSE exec $CONTAINER_PHP php "${@:2}" - ;; - "indocker") - case $2 in - php|apache|database|database-testing|mailhog) - SERVICE=$2 - COMMAND=${@:3} - if [ -z "$COMMAND" ]; then - log "Entering $SERVICE container..." - $DOCKER_COMPOSE exec "$SERVICE" sh -c "which bash > /dev/null && bash || sh" - else - $DOCKER_COMPOSE exec "$SERVICE" $COMMAND - fi - ;; - *) - list_services - echo -e "Usage: $0 indocker {${GREEN}svc${NC}} [command]" - exit 1 - ;; - esac - ;; - *) - echo -e "Usage: $0 {${GREEN}start${NC}|${GREEN}stop${NC}|${GREEN}down${NC}|${GREEN}setup${NC}|${GREEN}reset${NC}|${GREEN}openapi${NC}|${GREEN}info${NC}|${GREEN}clean${NC}|${GREEN}composer${NC}|${GREEN}php${NC}|${GREEN}indocker${NC}}" - echo "" - echo "Infrastructure Commands:" - echo -e " ${GREEN}start${NC} - Start containers (checks for initialization first)" - echo -e " ${GREEN}stop${NC} - Pause containers (preserves state)" - echo -e " ${GREEN}down${NC} - Stop and remove containers and networks" - echo -e " ${GREEN}setup${NC} - Full initial installation (Start, Install, Migrate, OpenAPI)" - echo -e " ${GREEN}info${NC} - Show connectivity info (URLs, Ports, Credentials)" - echo -e " ${GREEN}services${NC} - List all available service names" - echo -e " ${GREEN}openapi${NC} - Regenerate the OpenAPI documentation" - echo "" - echo "Maintenance Commands:" - echo -e " ${GREEN}clean [...]${NC} - Delete resources (run without args for options)" - echo -e " ${GREEN}reset [...]${NC} - Restart with fresh data (run without args for options)" - echo "" - echo "Utility & Development:" - echo -e " ${GREEN}composer [...]${NC} - Execute Composer commands" - echo -e " ${GREEN}php [...]${NC} - Execute PHP scripts" - echo -e " ${GREEN}test [...]${NC} - Run PHPUnit tests" - echo -e " ${GREEN}logs [svc]${NC} - Tail logs for all or a specific service" - echo -e " ${GREEN}bash${NC} - Shortcut to enter PHP container" - echo -e " ${GREEN}mysql${NC} - Shortcut to enter Database CLI" - echo -e " ${GREEN}indocker [svc]${NC} - Access a specific container" - exit 1 + docker-compose run --rm --env APP_ENV php "${@:2}" ;; esac diff --git a/bin/migrations.php b/bin/migrations.php index cef980d9..bea2a60f 100755 --- a/bin/migrations.php +++ b/bin/migrations.php @@ -14,10 +14,10 @@ $env = getenv('APP_ENV') ?: ''; -$dbParams = (require realpath(__DIR__) . sprintf('/../config/autoload/database.%s.php', $env))['database']; +$dbParams = (require realpath(__DIR__) . sprintf('/../config/autoload/database.%s.php', getenv('APP_ENV') ?: 'global'))['database']; $dbParams['driver'] = 'pdo_' . $dbParams['driver']; -$config = (require realpath(__DIR__) . sprintf('/../config/autoload/migrations.%s.php', $env))['migrations']; +$config = (require realpath(__DIR__) . sprintf('/../config/autoload/migrations.%s.php', getenv('APP_ENV') ?: 'global'))['migrations']; $connection = DriverManager::getConnection($dbParams); @@ -26,14 +26,12 @@ $configuration->setCustomTemplate(__DIR__ . '/../config/migrations.template.tpl'); $configuration->addMigrationsDirectory('Migrations', $config['migrations_paths']['Migrations']); -if (array_key_exists('TestDataMigrations', $config['migrations_paths'])) { - $configuration->addMigrationsDirectory('TestDataMigrations', $config['migrations_paths']['TestDataMigrations']); -} $configuration->setAllOrNothing($config['all_or_nothing']); $configuration->setCheckDatabasePlatform($config['check_database_platform']); $configuration->setTransactional($config['transactional']); $configuration->setMigrationOrganization($config['organize_migrations']); + $storageConfiguration = new TableMetadataStorageConfiguration(); $storageConfiguration->setTableName($config['table_storage']['table_name']); $storageConfiguration->setVersionColumnName($config['table_storage']['version_column_name']); @@ -41,6 +39,7 @@ $storageConfiguration->setExecutedAtColumnName($config['table_storage']['executed_at_column_name']); $storageConfiguration->setExecutionTimeColumnName($config['table_storage']['execution_time_column_name']); + $configuration->setMetadataStorageConfiguration($storageConfiguration); $container = require_once __DIR__ . '/../config/container.php'; diff --git a/composer.json b/composer.json index 27380e7a..d8315009 100644 --- a/composer.json +++ b/composer.json @@ -1,120 +1,109 @@ { - "name": "ownhackathon/hackathon-api", - "description": "Evaluation project for self-created hackathon", - "type": "project", - "license": "BSD-3-Clause", - "keywords": [ - "Hackathon", - "OpenSource" - ], - "homepage": "https://github.com/ownHackathon", - "support": { - "docs": "https://github.com/ownHackathon", - "issues": "https://github.com/ownHackathon/hackathon-api/issues", - "source": "https://github.com/ownHackathon/hackathon-api" - }, - "config": { - "allow-plugins": { - "composer/package-versions-deprecated": true, - "dealerdirect/phpcodesniffer-composer-installer": true, - "laminas/laminas-component-installer": true + "name": "ownhackathon/hackathon-api", + "description": "Evaluation project for the Hackathon on the Discord server from BlackScorp", + "type": "project", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "BibaltiK", + "email": "bibaltik@exdrals.de" + } + ], + "keywords": [ + "BlackScorp", + "Hackathon", + "Discord" + ], + "homepage": "https://hackathon.exdrals.de", + "config": { + "sort-packages": true, + "allow-plugins": { + "composer/package-versions-deprecated": true, + "laminas/laminas-component-installer": true, + "dealerdirect/phpcodesniffer-composer-installer": true + } }, - "platform": { - "php": "8.4.16" + "scripts": { + "post-create-project-cmd": [ + "@development-enable" + ], + "development-disable": "laminas-development-mode disable", + "development-enable": "laminas-development-mode enable", + "development-status": "laminas-development-mode status", + "mezzio": "laminas --ansi", + "doctrine": "php ./bin/migrations.php", + "openapi": "php ./vendor/bin/openapi ./src -f json -o ./public/api/doc/swagger.json", + "phplint": "phplint -c .phplint.yml --no-cache", + "phpcs": "phpcs --standard=phpcs.xml --extensions=php --tab-width=4 -sp src tests", + "phpcbf": "phpcbf --standard=phpcs.xml --extensions=php --tab-width=4 -sp src tests", + "phpstan": "vendor/bin/phpstan analyse -c phpstan.neon", + "unittest": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --colors=always --configuration phpunit_unittest.xml", + "functionaltest": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --colors=always --configuration phpunit_functionaltest.xml", + "test": [ + "@unittest", + "@functionaltest" + ], + "check": [ + "@phplint", + "@phpcs", + "@phpstan", + "@unittest", + "@functionaltest" + ], + "clear-config-cache": "php bin/clear-config-cache.php", + "enable-codestandard": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run" }, - "sort-packages": true - }, - "extra": { - "laminas": { - "component-auto-installs": [ - "mezzio/mezzio", - "mezzio/mezzio-helpers", - "mezzio/mezzio-router", - "laminas/laminas-httphandlerrunner", - "mezzio/mezzio-fastroute" - ] - } - }, - "require": { - "php": "~8.4.0", - "ext-json": "*", - "ext-openssl": "*", - "ext-pdo": "*", - "envms/fluentpdo": "^2.2", - "firebase/php-jwt": "^6.0", - "laminas/laminas-config-aggregator": "^1.6", - "laminas/laminas-diactoros": "^3.8.0", - "laminas/laminas-inputfilter": "^2.30", - "laminas/laminas-servicemanager": "^3.4", - "laminas/laminas-stdlib": "^3.6", - "laminas/laminas-validator": "^2.64", - "laminas/laminas-component-installer": "^2.6 || ^3.0", - "laminas/laminas-development-mode": "^3.3.0", - "mezzio/mezzio-tooling": "^2.12", - "mezzio/mezzio": "^3", - "mezzio/mezzio-cors": "^1.13", - "mezzio/mezzio-fastroute": "^3.0.3", - "mezzio/mezzio-helpers": "^5.7", - "monolog/monolog": "^3.9.0", - "ramsey/uuid": "^4.7", - "symfony/mailer": "^8", - "zircote/swagger-php": "^6", - "jetbrains/phpstorm-attributes": "^1.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.2", - "doctrine/migrations": "^3.6", - "filp/whoops": "^2.7.1", - "helmich/phpunit-json-assert": "^3.5", - "helmich/phpunit-psr7-assert": "^4.4", - "overtrue/phplint": "^9.0", - "phpstan/phpstan": "^2.1.33", - "phpstan/phpstan-deprecation-rules": "^2.0.3", - "phpunit/php-code-coverage": "^10.0", - "phpunit/phpunit": "^10.0", - "roave/security-advisories": "dev-master", - "slevomat/coding-standard": "^8.15", - "squizlabs/php_codesniffer": "^4.0" - }, - "autoload": { - "psr-4": { - "App\\": "src/App/", - "Core\\": "src/Core/" - } - }, - "autoload-dev": { - "psr-4": { - "FunctionalTest\\": "tests/FunctionalTest/", - "UnitTest\\": "tests/UnitTest/" + "require": { + "php": "^8.3", + "ext-json": "*", + "ext-openssl": "*", + "ext-pdo": "*", + "composer/package-versions-deprecated": "^1.11.99.4", + "doctrine/migrations": "^3.5", + "envms/fluentpdo": "^2.2.0", + "firebase/php-jwt": "^6.0", + "jetbrains/phpstorm-attributes": "^1.0", + "laminas/laminas-component-installer": "^3.2.0", + "laminas/laminas-config-aggregator": "^1.6.0", + "laminas/laminas-development-mode": "^3.12", + "laminas/laminas-diactoros": "^2.8.0", + "laminas/laminas-hydrator": "^4.3.1", + "laminas/laminas-inputfilter": "^2.12", + "laminas/laminas-log": "^2.17", + "laminas/laminas-servicemanager": "^3.10", + "laminas/laminas-stdlib": "^3.6.0", + "laminas/laminas-validator": "^2.15", + "laminas/laminas-zendframework-bridge": "^1.4.0", + "mezzio/mezzio": "^3.6.0", + "mezzio/mezzio-fastroute": "^3.3.0", + "mezzio/mezzio-helpers": "^5.7.0", + "mezzio/mezzio-tooling": "^2.9", + "ramsey/uuid": "^4.3", + "symfony/mailer": "^6.1", + "zircote/swagger-php": "^4.6" + }, + "require-dev": { + "filp/whoops": "^2.14.4", + "helmich/phpunit-json-assert": "^3.5", + "overtrue/phplint": "^5.3", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1.2", + "phpunit/phpunit": "^10.0", + "roave/security-advisories": "dev-latest", + "slevomat/coding-standard": "^8.14", + "squizlabs/php_codesniffer": "^3.6", + "trinet/mezzio-test": "^1.1" + }, + "autoload": { + "psr-4": { + "App\\": "src/App/", + "Core\\": "src/Core/", + "Migrations\\": "database/migrations" + } + }, + "autoload-dev": { + "psr-4": { + "Test\\": "tests" + } } - }, - "scripts": { - "check": [ - "@phplint", - "@phpcs", - "@phpstan", - "@test" - ], - "clear-config-cache": "php bin/clear-config-cache.php", - "development-disable": "laminas-development-mode disable", - "development-enable": "laminas-development-mode enable", - "development-status": "laminas-development-mode status", - "doctrine": "php ./bin/migrations.php", - "enable-codestandard": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run", - "functionaltest": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --colors=always --display-phpunit-deprecations --configuration phpunit_functionaltest.xml", - "mezzio": "laminas --ansi", - "openapi": "php ./vendor/bin/openapi ./src -f json -o ./public/api/docs/swagger.json", - "phpcbf": "phpcbf --standard=phpcs.xml -p", - "phpcs": "phpcs --standard=phpcs.xml -s -p", - "phplint": "vendor/bin/phplint", - "phpstan": "vendor/bin/phpstan analyse -c phpstan.neon", - "post-create-project-cmd": [ - "@development-enable" - ], - "test": [ - "@unittest", - "@functionaltest" - ], - "unittest": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --colors=always --display-phpunit-deprecations --configuration phpunit_unittest.xml" - } } diff --git a/composer.lock b/composer.lock index 346de3ae..d2920520 100644 --- a/composer.lock +++ b/composer.lock @@ -4,29 +4,29 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1a6899dfb7acc2c2a218b4d63c4136ec", + "content-hash": "c26f79a035cdf55aaffbf44b2cad64ef", "packages": [ { "name": "brick/math", - "version": "0.14.1", + "version": "0.12.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f05858549e5f9d7bb45875a75583240a38a281d0" + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0", - "reference": "f05858549e5f9d7bb45875a75583240a38a281d0", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", "shasum": "" }, "require": { - "php": "^8.2" + "php": "^8.1" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpstan/phpstan": "2.1.22", - "phpunit/phpunit": "^11.5" + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" }, "type": "library", "autoload": { @@ -56,7 +56,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.1" + "source": "https://github.com/brick/math/tree/0.12.1" }, "funding": [ { @@ -64,30 +64,30 @@ "type": "github" } ], - "time": "2025-11-24T14:40:29+00:00" + "time": "2023-11-29T23:19:16+00:00" }, { "name": "brick/varexporter", - "version": "0.6.0", + "version": "0.5.0", "source": { "type": "git", "url": "https://github.com/brick/varexporter.git", - "reference": "af98bfc2b702a312abbcaff37656dbe419cec5bc" + "reference": "84b2a7a91f69aa5d079aec5a0a7256ebf2dceb6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/varexporter/zipball/af98bfc2b702a312abbcaff37656dbe419cec5bc", - "reference": "af98bfc2b702a312abbcaff37656dbe419cec5bc", + "url": "https://api.github.com/repos/brick/varexporter/zipball/84b2a7a91f69aa5d079aec5a0a7256ebf2dceb6b", + "reference": "84b2a7a91f69aa5d079aec5a0a7256ebf2dceb6b", "shasum": "" }, "require": { "nikic/php-parser": "^5.0", - "php": "^8.1" + "php": "^7.4 || ^8.0" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "6.8.4" + "phpunit/phpunit": "^9.3", + "psalm/phar": "5.21.1" }, "type": "library", "autoload": { @@ -105,7 +105,7 @@ ], "support": { "issues": "https://github.com/brick/varexporter/issues", - "source": "https://github.com/brick/varexporter/tree/0.6.0" + "source": "https://github.com/brick/varexporter/tree/0.5.0" }, "funding": [ { @@ -113,36 +113,44 @@ "type": "github" } ], - "time": "2025-02-20T17:42:39+00:00" + "time": "2024-05-10T17:15:19+00:00" }, { - "name": "doctrine/lexer", - "version": "3.0.1", + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", "source": { "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", "shasum": "" }, "require": { - "php": "^8.1" + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" }, "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^5.21" + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } }, - "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" + "PackageVersions\\": "src/PackageVersions" } }, "notification-url": "https://packagist.org/downloads/", @@ -151,82 +159,77 @@ ], "authors": [ { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" }, { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" } ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.1" + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" }, "funding": [ { - "url": "https://www.doctrine-project.org/sponsorship.html", + "url": "https://packagist.com", "type": "custom" }, { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" + "url": "https://github.com/composer", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2024-02-05T11:56:58+00:00" + "time": "2022-01-17T14:14:24+00:00" }, { - "name": "egulias/email-validator", + "name": "doctrine/dbal", "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/egulias/EmailValidator.git", - "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + "url": "https://github.com/doctrine/dbal.git", + "reference": "50fda19f80724b55ff770bb4ff352407008e63c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", - "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/50fda19f80724b55ff770bb4ff352407008e63c5", + "reference": "50fda19f80724b55ff770bb4ff352407008e63c5", "shasum": "" }, "require": { - "doctrine/lexer": "^2.0 || ^3.0", - "php": ">=8.1", - "symfony/polyfill-intl-idn": "^1.26" + "doctrine/deprecations": "^0.5.3|^1", + "php": "^8.1", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" }, "require-dev": { - "phpunit/phpunit": "^10.2", - "vimeo/psalm": "^5.12" + "doctrine/coding-standard": "12.0.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2023.2", + "phpstan/phpstan": "1.11.5", + "phpstan/phpstan-phpunit": "1.4.0", + "phpstan/phpstan-strict-rules": "^1.6", + "phpunit/phpunit": "10.5.22", + "psalm/plugin-phpunit": "0.19.0", + "slevomat/coding-standard": "8.13.1", + "squizlabs/php_codesniffer": "3.10.1", + "symfony/cache": "^6.3.8|^7.0", + "symfony/console": "^5.4|^6.3|^7.0", + "vimeo/psalm": "5.24.0" }, "suggest": { - "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + "symfony/console": "For helpful console commands such as SQL execution and import of files." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" - } - }, "autoload": { "psr-4": { - "Egulias\\EmailValidator\\": "src" + "Doctrine\\DBAL\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -235,117 +238,141 @@ ], "authors": [ { - "name": "Eduardo Gulias Davis" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" } ], - "description": "A library for validating emails against several RFCs", - "homepage": "https://github.com/egulias/EmailValidator", + "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", + "homepage": "https://www.doctrine-project.org/projects/dbal.html", "keywords": [ - "email", - "emailvalidation", - "emailvalidator", - "validation", - "validator" + "abstraction", + "database", + "db2", + "dbal", + "mariadb", + "mssql", + "mysql", + "oci8", + "oracle", + "pdo", + "pgsql", + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlite", + "sqlserver", + "sqlsrv" ], "support": { - "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/4.0.4" }, "funding": [ { - "url": "https://github.com/egulias", - "type": "github" + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", + "type": "tidelift" } ], - "time": "2025-03-06T22:45:56+00:00" + "time": "2024-06-19T11:57:23+00:00" }, { - "name": "envms/fluentpdo", - "version": "v2.2.4", + "name": "doctrine/deprecations", + "version": "1.1.3", "source": { "type": "git", - "url": "https://github.com/envms/fluentpdo.git", - "reference": "1985e0e8406a56140f387bc9bec786b419cbeccc" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/envms/fluentpdo/zipball/1985e0e8406a56140f387bc9bec786b419cbeccc", - "reference": "1985e0e8406a56140f387bc9bec786b419cbeccc", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", "shasum": "" }, "require": { - "ext-pdo": "*", - "php": ">=7.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "envms/fluent-test": "^1.0", - "phpunit/phpunit": "^8.0" + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, "type": "library", "autoload": { "psr-4": { - "Envms\\FluentPDO\\": "src/" + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0", - "GPL-2.0+" - ], - "authors": [ - { - "name": "envms", - "homepage": "https://env.ms" - } - ], - "description": "FluentPDO is a quick and light PHP library for rapid query building. It features a smart join builder, which automatically creates table joins.", - "homepage": "https://github.com/envms/fluentpdo", - "keywords": [ - "builder", - "database", - "db", - "dbal", - "fluent", - "mysql", - "oracle", - "pdo", - "query" + "MIT" ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", "support": { - "issues": "https://github.com/envms/fluentpdo/issues", - "source": "https://github.com/envms/fluentpdo/tree/v2.2.4" + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" }, - "time": "2022-01-27T21:49:44+00:00" + "time": "2024-01-30T19:34:25+00:00" }, { - "name": "fig/http-message-util", - "version": "1.1.5", + "name": "doctrine/event-manager", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message-util.git", - "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + "url": "https://github.com/doctrine/event-manager.git", + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", - "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0 || ^8.0" + "php": "^8.1" }, - "suggest": { - "psr/http-message": "The package containing the PSR-7 interfaces" + "conflict": { + "doctrine/common": "<2.9" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "^5.24" }, + "type": "library", "autoload": { "psr-4": { - "Fig\\Http\\Message\\": "src/" + "Doctrine\\Common\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -354,291 +381,683 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" } ], - "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "event", + "event dispatcher", + "event manager", + "event system", + "events" ], "support": { - "issues": "https://github.com/php-fig/http-message-util/issues", - "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/2.0.1" }, - "time": "2020-11-24T22:02:12+00:00" - }, - { - "name": "firebase/php-jwt", - "version": "v6.11.1", - "source": { + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "type": "tidelift" + } + ], + "time": "2024-05-22T20:47:39+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { "type": "git", - "url": "https://github.com/firebase/php-jwt.git", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "php": "^8.0" + "php": "^8.1" }, "require-dev": { - "guzzlehttp/guzzle": "^7.4", - "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "psr/cache": "^2.0||^3.0", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0" - }, - "suggest": { - "ext-sodium": "Support EdDSA (Ed25519) signatures", - "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" }, "type": "library", "autoload": { "psr-4": { - "Firebase\\JWT\\": "src" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Neuman Vong", - "email": "neuman+pear@twilio.com", - "role": "Developer" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" }, { - "name": "Anant Narayanan", - "email": "anant@php.net", - "role": "Developer" + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", - "homepage": "https://github.com/firebase/php-jwt", + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ - "jwt", + "annotations", + "docblock", + "lexer", + "parser", "php" ], "support": { - "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" - }, - "time": "2025-04-09T20:32:01+00:00" - }, - { - "name": "jetbrains/phpstorm-attributes", - "version": "1.2", - "source": { - "type": "git", - "url": "https://github.com/JetBrains/phpstorm-attributes.git", - "reference": "64de815a4509c29e00d5e3474087fd24c171afc2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/JetBrains/phpstorm-attributes/zipball/64de815a4509c29e00d5e3474087fd24c171afc2", - "reference": "64de815a4509c29e00d5e3474087fd24c171afc2", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "JetBrains\\PhpStorm\\": "src/" - } + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ + "funding": [ { - "name": "JetBrains", - "homepage": "https://www.jetbrains.com" + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" } ], - "description": "PhpStorm specific attributes", - "keywords": [ - "attributes", - "jetbrains", - "phpstorm" - ], - "support": { - "issues": "https://youtrack.jetbrains.com/newIssue?project=WI", - "source": "https://github.com/JetBrains/phpstorm-attributes/tree/1.2" - }, - "time": "2024-10-11T10:46:19+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { - "name": "laminas/laminas-cli", - "version": "1.13.0", + "name": "doctrine/migrations", + "version": "3.8.0", "source": { "type": "git", - "url": "https://github.com/laminas/laminas-cli.git", - "reference": "c84265f644c604f5a70bf5c8fcdb4b0e2aa115d5" + "url": "https://github.com/doctrine/migrations.git", + "reference": "535a70dcbd88b8c6ba945be050977457f4f4c06c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-cli/zipball/c84265f644c604f5a70bf5c8fcdb4b0e2aa115d5", - "reference": "c84265f644c604f5a70bf5c8fcdb4b0e2aa115d5", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/535a70dcbd88b8c6ba945be050977457f4f4c06c", + "reference": "535a70dcbd88b8c6ba945be050977457f4f4c06c", "shasum": "" }, "require": { - "composer-runtime-api": "^2.0.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", - "psr/container": "^1.0 || ^2.0", - "symfony/console": "^6.0 || ^7.0", - "symfony/event-dispatcher": "^6.0 || ^7.0", - "webmozart/assert": "^1.11" + "composer-runtime-api": "^2", + "doctrine/dbal": "^3.6 || ^4", + "doctrine/deprecations": "^0.5.3 || ^1", + "doctrine/event-manager": "^1.2 || ^2.0", + "php": "^8.1", + "psr/log": "^1.1.3 || ^2 || ^3", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^6.2 || ^7.0" }, "conflict": { - "amphp/amp": "<2.6.4" + "doctrine/orm": "<2.12 || >=4" }, "require-dev": { - "laminas/laminas-coding-standard": "^3.1.0", - "laminas/laminas-mvc": "^3.8.0", - "laminas/laminas-servicemanager": "^3.24.0", - "mikey179/vfsstream": "2.0.x-dev", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1" + "doctrine/coding-standard": "^12", + "doctrine/orm": "^2.13 || ^3", + "doctrine/persistence": "^2 || ^3", + "doctrine/sql-formatter": "^1.0", + "ext-pdo_sqlite": "*", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpstan/phpstan-strict-rules": "^1.4", + "phpstan/phpstan-symfony": "^1.3", + "phpunit/phpunit": "^10.3", + "symfony/cache": "^5.4 || ^6.0 || ^7.0", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "suggest": { + "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.", + "symfony/yaml": "Allows the use of yaml for migration configuration files." }, "bin": [ - "bin/laminas" + "bin/doctrine-migrations" ], "type": "library", "autoload": { "psr-4": { - "Laminas\\Cli\\": "src/" + "Doctrine\\Migrations\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Command-line interface for Laminas projects", + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Michael Simonson", + "email": "contact@mikesimonson.com" + } + ], + "description": "PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to use and a powerful tool.", + "homepage": "https://www.doctrine-project.org/projects/migrations.html", "keywords": [ - "cli", - "command", - "console", - "laminas" + "database", + "dbal", + "migrations" ], "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-cli/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/mezzio/laminas-cli/issues", - "rss": "https://github.com/mezzio/laminas-cli/releases.atom", - "source": "https://github.com/mezzio/laminas-cli" + "issues": "https://github.com/doctrine/migrations/issues", + "source": "https://github.com/doctrine/migrations/tree/3.8.0" }, "funding": [ { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fmigrations", + "type": "tidelift" } ], - "time": "2025-10-14T22:21:28+00:00" + "time": "2024-06-26T14:12:46+00:00" }, { - "name": "laminas/laminas-code", - "version": "4.17.0", + "name": "egulias/email-validator", + "version": "4.0.2", "source": { "type": "git", - "url": "https://github.com/laminas/laminas-code.git", - "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd" + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-code/zipball/40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd", - "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", "shasum": "" }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "doctrine/annotations": "^2.0.1", - "ext-phar": "*", - "laminas/laminas-coding-standard": "^3.0.0", - "laminas/laminas-stdlib": "^3.18.0", - "phpunit/phpunit": "^10.5.58", - "psalm/plugin-phpunit": "^0.19.0", - "vimeo/psalm": "^5.15.0" + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" }, "suggest": { - "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "laminas/laminas-stdlib": "Laminas\\Stdlib component" + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, "autoload": { "psr-4": { - "Laminas\\Code\\": "src/" + "Egulias\\EmailValidator\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", - "homepage": "https://laminas.dev", + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", "keywords": [ - "code", - "laminas", - "laminasframework" + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" ], "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-code/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-code/issues", - "rss": "https://github.com/laminas/laminas-code/releases.atom", - "source": "https://github.com/laminas/laminas-code" + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" }, "funding": [ { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" + "url": "https://github.com/egulias", + "type": "github" } ], - "time": "2025-11-01T09:38:14+00:00" + "time": "2023-10-06T06:47:41+00:00" }, { - "name": "laminas/laminas-component-installer", - "version": "3.7.0", + "name": "envms/fluentpdo", + "version": "v2.2.4", "source": { "type": "git", - "url": "https://github.com/laminas/laminas-component-installer.git", - "reference": "cd2baf076f8035edca93baef584835f2de1b9e0f" + "url": "https://github.com/envms/fluentpdo.git", + "reference": "1985e0e8406a56140f387bc9bec786b419cbeccc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-component-installer/zipball/cd2baf076f8035edca93baef584835f2de1b9e0f", - "reference": "cd2baf076f8035edca93baef584835f2de1b9e0f", + "url": "https://api.github.com/repos/envms/fluentpdo/zipball/1985e0e8406a56140f387bc9bec786b419cbeccc", + "reference": "1985e0e8406a56140f387bc9bec786b419cbeccc", "shasum": "" }, "require": { - "composer-plugin-api": "^2.6", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" - }, + "ext-pdo": "*", + "php": ">=7.1" + }, + "require-dev": { + "envms/fluent-test": "^1.0", + "phpunit/phpunit": "^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Envms\\FluentPDO\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0", + "GPL-2.0+" + ], + "authors": [ + { + "name": "envms", + "homepage": "https://env.ms" + } + ], + "description": "FluentPDO is a quick and light PHP library for rapid query building. It features a smart join builder, which automatically creates table joins.", + "homepage": "https://github.com/envms/fluentpdo", + "keywords": [ + "builder", + "database", + "db", + "dbal", + "fluent", + "mysql", + "oracle", + "pdo", + "query" + ], + "support": { + "issues": "https://github.com/envms/fluentpdo/issues", + "source": "https://github.com/envms/fluentpdo/tree/v2.2.4" + }, + "time": "2022-01-27T21:49:44+00:00" + }, + { + "name": "fig/http-message-util", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v6.10.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "500501c2ce893c824c801da135d02661199f60c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5", + "reference": "500501c2ce893c824c801da135d02661199f60c5", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.10.1" + }, + "time": "2024-05-18T18:05:11+00:00" + }, + { + "name": "jetbrains/phpstorm-attributes", + "version": "1.1", + "source": { + "type": "git", + "url": "https://github.com/JetBrains/phpstorm-attributes.git", + "reference": "22fb28d679deceedba8366dbae65cc8ebfc17e26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JetBrains/phpstorm-attributes/zipball/22fb28d679deceedba8366dbae65cc8ebfc17e26", + "reference": "22fb28d679deceedba8366dbae65cc8ebfc17e26", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "JetBrains\\PhpStorm\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "JetBrains", + "homepage": "https://www.jetbrains.com" + } + ], + "description": "PhpStorm specific attributes", + "keywords": [ + "attributes", + "jetbrains", + "phpstorm" + ], + "support": { + "issues": "https://youtrack.jetbrains.com/newIssue?project=WI", + "source": "https://github.com/JetBrains/phpstorm-attributes/tree/1.1" + }, + "time": "2023-09-01T08:50:25+00:00" + }, + { + "name": "laminas/laminas-cli", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-cli.git", + "reference": "cc59875b2a983b05a70abf4f9b3af739b1257f34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-cli/zipball/cc59875b2a983b05a70abf4f9b3af739b1257f34", + "reference": "cc59875b2a983b05a70abf4f9b3af739b1257f34", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "psr/container": "^1.0 || ^2.0", + "symfony/console": "^6.0 || ^7.0", + "symfony/event-dispatcher": "^6.0 || ^7.0", + "symfony/polyfill-php80": "^1.17", + "webmozart/assert": "^1.10" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-mvc": "^3.7.0", + "laminas/laminas-servicemanager": "^3.22.1", + "mikey179/vfsstream": "2.0.x-dev", + "phpunit/phpunit": "^10.5.5", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.18" + }, + "bin": [ + "bin/laminas" + ], + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Cli\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Command-line interface for Laminas projects", + "keywords": [ + "cli", + "command", + "console", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-cli/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/laminas-cli/issues", + "rss": "https://github.com/mezzio/laminas-cli/releases.atom", + "source": "https://github.com/mezzio/laminas-cli" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2024-01-02T15:08:03+00:00" + }, + { + "name": "laminas/laminas-code", + "version": "4.14.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-code.git", + "reference": "562e02b7d85cb9142b5116cc76c4c7c162a11a1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/562e02b7d85cb9142b5116cc76c4c7c162a11a1c", + "reference": "562e02b7d85cb9142b5116cc76c4c7c162a11a1c", + "shasum": "" + }, + "require": { + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0.1", + "ext-phar": "*", + "laminas/laminas-coding-standard": "^2.5.0", + "laminas/laminas-stdlib": "^3.17.0", + "phpunit/phpunit": "^10.3.3", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.15.0" + }, + "suggest": { + "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", + "laminas/laminas-stdlib": "Laminas\\Stdlib component" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Code\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", + "homepage": "https://laminas.dev", + "keywords": [ + "code", + "laminas", + "laminasframework" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-code/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-code/issues", + "rss": "https://github.com/laminas/laminas-code/releases.atom", + "source": "https://github.com/laminas/laminas-code" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2024-06-17T08:50:25+00:00" + }, + { + "name": "laminas/laminas-component-installer", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-component-installer.git", + "reference": "e4c15d50c5dcbe0207285659f083df70bb256bb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-component-installer/zipball/e4c15d50c5dcbe0207285659f083df70bb256bb6", + "reference": "e4c15d50c5dcbe0207285659f083df70bb256bb6", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + }, "conflict": { "zendframework/zend-component-installer": "*" }, "require-dev": { - "composer/composer": "^2.7.7", - "laminas/laminas-coding-standard": "~3.1.0", + "composer/composer": "^2.6.4", + "laminas/laminas-coding-standard": "~2.5.0", "mikey179/vfsstream": "^1.6.11", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1", + "phpunit/phpunit": "^10.4", + "psalm/plugin-phpunit": "^0.18.0", + "vimeo/psalm": "^5.15.0", "webmozart/assert": "^1.11.0" }, "type": "composer-plugin", @@ -676,26 +1095,26 @@ "type": "community_bridge" } ], - "time": "2025-10-16T19:54:21+00:00" + "time": "2023-11-21T15:32:55+00:00" }, { "name": "laminas/laminas-config-aggregator", - "version": "1.19.0", + "version": "1.15.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-config-aggregator.git", - "reference": "612343ce135c340fc667da3615e50d865a86b4d9" + "reference": "102e048734413a4499846571b156aeaa6c2aba56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-config-aggregator/zipball/612343ce135c340fc667da3615e50d865a86b4d9", - "reference": "612343ce135c340fc667da3615e50d865a86b4d9", + "url": "https://api.github.com/repos/laminas/laminas-config-aggregator/zipball/102e048734413a4499846571b156aeaa6c2aba56", + "reference": "102e048734413a4499846571b156aeaa6c2aba56", "shasum": "" }, "require": { - "brick/varexporter": "^0.5.0 || ^0.4.0 || ^0.6.0", + "brick/varexporter": "^0.5.0 || ^0.4.0", "laminas/laminas-stdlib": "^3.18.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "webimpress/safe-writer": "^2.2.0" }, "conflict": { @@ -703,11 +1122,11 @@ "zendframework/zend-config-aggregator": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "~3.1.0", - "laminas/laminas-config": "^3.10.1", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1" + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-config": "^3.9.0", + "phpunit/phpunit": "^10.5.11", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.22.2" }, "suggest": { "laminas/laminas-config": "Allows loading configuration from XML, INI, YAML, and JSON files", @@ -744,34 +1163,34 @@ "type": "community_bridge" } ], - "time": "2025-10-14T19:57:01+00:00" + "time": "2024-05-12T10:04:30+00:00" }, { "name": "laminas/laminas-development-mode", - "version": "3.15.0", + "version": "3.12.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-development-mode.git", - "reference": "87611d4d742dc314244dcbe4e173a2af11a7c0bc" + "reference": "cd2f9885deab41ef590924d53ad4041db490b923" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-development-mode/zipball/87611d4d742dc314244dcbe4e173a2af11a7c0bc", - "reference": "87611d4d742dc314244dcbe4e173a2af11a7c0bc", + "url": "https://api.github.com/repos/laminas/laminas-development-mode/zipball/cd2f9885deab41ef590924d53ad4041db490b923", + "reference": "cd2f9885deab41ef590924d53ad4041db490b923", "shasum": "" }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "conflict": { "zfcampus/zf-development-mode": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "~3.1.0", - "mikey179/vfsstream": "^1.6.12", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1" + "laminas/laminas-coding-standard": "~2.5.0", + "mikey179/vfsstream": "^1.6.11", + "phpunit/phpunit": "^10.4.2", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.15.0" }, "bin": [ "bin/laminas-development-mode" @@ -805,51 +1224,51 @@ "type": "community_bridge" } ], - "time": "2025-10-14T21:17:32+00:00" + "time": "2023-11-21T16:03:48+00:00" }, { "name": "laminas/laminas-diactoros", - "version": "3.8.0", + "version": "2.26.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "60c182916b2749480895601649563970f3f12ec4" + "reference": "6584d44eb8e477e89d453313b858daac6183cddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/60c182916b2749480895601649563970f3f12ec4", - "reference": "60c182916b2749480895601649563970f3f12ec4", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/6584d44eb8e477e89d453313b858daac6183cddc", + "reference": "6584d44eb8e477e89d453313b858daac6183cddc", "shasum": "" }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", - "psr/http-factory": "^1.1", - "psr/http-message": "^1.1 || ^2.0" + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1" }, "conflict": { - "amphp/amp": "<2.6.4" + "zendframework/zend-diactoros": "*" }, "provide": { - "psr/http-factory-implementation": "^1.0", - "psr/http-message-implementation": "^1.1 || ^2.0" + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" }, "require-dev": { "ext-curl": "*", "ext-dom": "*", "ext-gd": "*", "ext-libxml": "*", - "http-interop/http-factory-tests": "^2.2.0", - "laminas/laminas-coding-standard": "~3.1.0", - "php-http/psr7-integration-tests": "^1.4.0", - "phpunit/phpunit": "^10.5.36", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13" + "http-interop/http-factory-tests": "^0.9.0", + "laminas/laminas-coding-standard": "^2.5", + "php-http/psr7-integration-tests": "^1.2", + "phpunit/phpunit": "^9.5.28", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.6" }, "type": "library", "extra": { "laminas": { - "module": "Laminas\\Diactoros", - "config-provider": "Laminas\\Diactoros\\ConfigProvider" + "config-provider": "Laminas\\Diactoros\\ConfigProvider", + "module": "Laminas\\Diactoros" } }, "autoload": { @@ -858,9 +1277,18 @@ "src/functions/marshal_headers_from_sapi.php", "src/functions/marshal_method_from_sapi.php", "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/marshal_uri_from_sapi.php", "src/functions/normalize_server.php", "src/functions/normalize_uploaded_files.php", - "src/functions/parse_cookie_header.php" + "src/functions/parse_cookie_header.php", + "src/functions/create_uploaded_file.legacy.php", + "src/functions/marshal_headers_from_sapi.legacy.php", + "src/functions/marshal_method_from_sapi.legacy.php", + "src/functions/marshal_protocol_version_from_sapi.legacy.php", + "src/functions/marshal_uri_from_sapi.legacy.php", + "src/functions/normalize_server.legacy.php", + "src/functions/normalize_uploaded_files.legacy.php", + "src/functions/parse_cookie_header.legacy.php" ], "psr-4": { "Laminas\\Diactoros\\": "src/" @@ -893,36 +1321,37 @@ "type": "community_bridge" } ], - "time": "2025-10-12T15:31:36+00:00" + "time": "2023-10-29T16:17:44+00:00" }, { "name": "laminas/laminas-escaper", - "version": "2.18.0", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-escaper.git", - "reference": "06f211dfffff18d91844c1f55250d5d13c007e18" + "reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/06f211dfffff18d91844c1f55250d5d13c007e18", - "reference": "06f211dfffff18d91844c1f55250d5d13c007e18", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/af459883f4018d0f8a0c69c7a209daef3bf973ba", + "reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba", "shasum": "" }, "require": { "ext-ctype": "*", "ext-mbstring": "*", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "conflict": { "zendframework/zend-escaper": "*" }, "require-dev": { - "infection/infection": "^0.31.0", - "laminas/laminas-coding-standard": "~3.1.0", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1" + "infection/infection": "^0.27.0", + "laminas/laminas-coding-standard": "~2.5.0", + "maglnet/composer-require-checker": "^3.8.0", + "phpunit/phpunit": "^9.6.7", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.9" }, "type": "library", "autoload": { @@ -954,42 +1383,42 @@ "type": "community_bridge" } ], - "time": "2025-10-14T18:31:13+00:00" + "time": "2023-10-10T08:35:13+00:00" }, { "name": "laminas/laminas-filter", - "version": "2.42.0", + "version": "2.36.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-filter.git", - "reference": "985d27bd42daf51b415ce1ee889e0978cc1e59ed" + "reference": "307afc21ada0648e84cdcf9e14cd84bd43ee9d13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/985d27bd42daf51b415ce1ee889e0978cc1e59ed", - "reference": "985d27bd42daf51b415ce1ee889e0978cc1e59ed", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/307afc21ada0648e84cdcf9e14cd84bd43ee9d13", + "reference": "307afc21ada0648e84cdcf9e14cd84bd43ee9d13", "shasum": "" }, "require": { "ext-mbstring": "*", "laminas/laminas-servicemanager": "^3.21.0", - "laminas/laminas-stdlib": "^3.19.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "laminas/laminas-stdlib": "^3.13.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "conflict": { "laminas/laminas-validator": "<2.10.1", "zendframework/zend-filter": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "^3.1", - "laminas/laminas-crypt": "^3.12", - "laminas/laminas-i18n": "^2.30.0", - "laminas/laminas-uri": "^2.13", - "pear/archive_tar": "^1.6.0", - "phpunit/phpunit": "^10.5.58", + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-crypt": "^3.11", + "laminas/laminas-i18n": "^2.26.0", + "laminas/laminas-uri": "^2.11", + "pear/archive_tar": "^1.5.0", + "phpunit/phpunit": "^10.5.20", "psalm/plugin-phpunit": "^0.19.0", "psr/http-factory": "^1.1.0", - "vimeo/psalm": "^5.26.1" + "vimeo/psalm": "^5.24.0" }, "suggest": { "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", @@ -1033,34 +1462,34 @@ "type": "community_bridge" } ], - "time": "2025-10-13T15:44:52+00:00" + "time": "2024-06-13T10:31:36+00:00" }, { "name": "laminas/laminas-httphandlerrunner", - "version": "2.13.0", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-httphandlerrunner.git", - "reference": "181eaeeb838ad3d80fbbcfb0657a46bc212bbd4e" + "reference": "35a0ba92e940a2f9533754f5a56187fa321f7693" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/181eaeeb838ad3d80fbbcfb0657a46bc212bbd4e", - "reference": "181eaeeb838ad3d80fbbcfb0657a46bc212bbd4e", + "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/35a0ba92e940a2f9533754f5a56187fa321f7693", + "reference": "35a0ba92e940a2f9533754f5a56187fa321f7693", "shasum": "" }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "psr/http-message": "^1.0 || ^2.0", "psr/http-message-implementation": "^1.0 || ^2.0", "psr/http-server-handler": "^1.0" }, "require-dev": { - "laminas/laminas-coding-standard": "~3.1.0", - "laminas/laminas-diactoros": "^3.6.0", - "phpunit/phpunit": "^10.5.46", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.10.3" + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-diactoros": "^3.3.0", + "phpunit/phpunit": "^10.5.5", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.18" }, "type": "library", "extra": { @@ -1100,40 +1529,117 @@ "type": "community_bridge" } ], - "time": "2025-10-12T20:58:29+00:00" + "time": "2024-01-04T10:50:34+00:00" + }, + { + "name": "laminas/laminas-hydrator", + "version": "4.15.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-hydrator.git", + "reference": "43ccca88313fdcceca37865109dffc69ecd2cf8f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/43ccca88313fdcceca37865109dffc69ecd2cf8f", + "reference": "43ccca88313fdcceca37865109dffc69ecd2cf8f", + "shasum": "" + }, + "require": { + "laminas/laminas-stdlib": "^3.3", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "webmozart/assert": "^1.10" + }, + "conflict": { + "laminas/laminas-servicemanager": "<3.14.0", + "zendframework/zend-hydrator": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-eventmanager": "^3.12", + "laminas/laminas-modulemanager": "^2.15.0", + "laminas/laminas-serializer": "^2.17.0", + "laminas/laminas-servicemanager": "^3.22.1", + "phpbench/phpbench": "^1.2.14", + "phpunit/phpunit": "^10.4.2", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.15" + }, + "suggest": { + "laminas/laminas-eventmanager": "^3.2, to support aggregate hydrator usage", + "laminas/laminas-serializer": "^2.9, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^3.14, to support hydrator plugin manager usage" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\Hydrator", + "config-provider": "Laminas\\Hydrator\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Hydrator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Serialize objects to arrays, and vice versa", + "homepage": "https://laminas.dev", + "keywords": [ + "hydrator", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-hydrator/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-hydrator/issues", + "rss": "https://github.com/laminas/laminas-hydrator/releases.atom", + "source": "https://github.com/laminas/laminas-hydrator" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2023-11-08T11:11:45+00:00" }, { "name": "laminas/laminas-inputfilter", - "version": "2.35.0", + "version": "2.30.1", "source": { "type": "git", "url": "https://github.com/laminas/laminas-inputfilter.git", - "reference": "326d2dac38814f70902a3a9e0062f740d06f89c5" + "reference": "f07a908df1052f28b18904d3745cdd5b183938c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/326d2dac38814f70902a3a9e0062f740d06f89c5", - "reference": "326d2dac38814f70902a3a9e0062f740d06f89c5", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/f07a908df1052f28b18904d3745cdd5b183938c9", + "reference": "f07a908df1052f28b18904d3745cdd5b183938c9", "shasum": "" }, "require": { "laminas/laminas-filter": "^2.19", "laminas/laminas-servicemanager": "^3.21.0", - "laminas/laminas-stdlib": "^3.19", - "laminas/laminas-validator": "^2.60.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", - "psr/container": "^1.1 || ^2.0" + "laminas/laminas-stdlib": "^3.0", + "laminas/laminas-validator": "^2.52", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "conflict": { "zendframework/zend-inputfilter": "*" }, "require-dev": { "ext-json": "*", - "laminas/laminas-coding-standard": "^3.1.0", - "phpunit/phpunit": "^11.5.46", - "psalm/plugin-phpunit": "^0.19.5", + "laminas/laminas-coding-standard": "~2.5.0", + "phpunit/phpunit": "^10.5.15", + "psalm/plugin-phpunit": "^0.19.0", "psr/http-message": "^2.0", - "vimeo/psalm": "^6.14.3" + "vimeo/psalm": "^5.23.1", + "webmozart/assert": "^1.11" }, "suggest": { "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" @@ -1174,25 +1680,113 @@ "type": "community_bridge" } ], - "time": "2026-01-10T15:07:43+00:00" + "time": "2024-04-03T15:14:05+00:00" + }, + { + "name": "laminas/laminas-log", + "version": "2.17.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-log.git", + "reference": "f24c4c78d3024bb59610845328d7876d6c797065" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/f24c4c78d3024bb59610845328d7876d6c797065", + "reference": "f24c4c78d3024bb59610845328d7876d6c797065", + "shasum": "" + }, + "require": { + "laminas/laminas-servicemanager": "^3.21.0", + "laminas/laminas-stdlib": "^3.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "psr/log": "^1.1.2" + }, + "conflict": { + "zendframework/zend-log": "*" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "ext-dom": "*", + "ext-json": "*", + "ext-xml": "*", + "firephp/firephp-core": "^0.5.3", + "laminas/laminas-coding-standard": "~2.3.0", + "laminas/laminas-db": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-filter": "^2.5", + "laminas/laminas-mail": "^2.6.1", + "laminas/laminas-validator": "^2.10.1", + "mikey179/vfsstream": "^1.6.7", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5.10" + }, + "suggest": { + "ext-mongo": "mongo extension to use Mongo writer", + "ext-mongodb": "mongodb extension to use MongoDB writer", + "laminas/laminas-db": "Laminas\\Db component to use the database log writer", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML log formatter", + "laminas/laminas-mail": "Laminas\\Mail component to use the email log writer", + "laminas/laminas-validator": "Laminas\\Validator component to block invalid log messages" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\Log", + "config-provider": "Laminas\\Log\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Log\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Robust, composite logger with filtering, formatting, and PSR-3 support", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "log", + "logging" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-log/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-log/issues", + "rss": "https://github.com/laminas/laminas-log/releases.atom", + "source": "https://github.com/laminas/laminas-log" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2023-12-05T18:27:50+00:00" }, { "name": "laminas/laminas-servicemanager", - "version": "3.24.0", + "version": "3.22.1", "source": { "type": "git", "url": "https://github.com/laminas/laminas-servicemanager.git", - "reference": "b172a0df568bf37ebdfb3658263156eefe3c1e8c" + "reference": "de98d297d4743956a0558a6d71616979ff779328" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/b172a0df568bf37ebdfb3658263156eefe3c1e8c", - "reference": "b172a0df568bf37ebdfb3658263156eefe3c1e8c", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/de98d297d4743956a0558a6d71616979ff779328", + "reference": "de98d297d4743956a0558a6d71616979ff779328", "shasum": "" }, "require": { - "laminas/laminas-stdlib": "^3.19", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "laminas/laminas-stdlib": "^3.17", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "psr/container": "^1.0" }, "conflict": { @@ -1209,15 +1803,15 @@ }, "require-dev": { "composer/package-versions-deprecated": "^1.11.99.5", - "friendsofphp/proxy-manager-lts": "^1.0.18", - "laminas/laminas-code": "^4.16.0", + "friendsofphp/proxy-manager-lts": "^1.0.14", + "laminas/laminas-code": "^4.10.0", "laminas/laminas-coding-standard": "~2.5.0", "laminas/laminas-container-config-test": "^0.8", - "mikey179/vfsstream": "^1.6.12", - "phpbench/phpbench": "^1.4.1", - "phpunit/phpunit": "^10.5.58", + "mikey179/vfsstream": "^1.6.11", + "phpbench/phpbench": "^1.2.9", + "phpunit/phpunit": "^10.4", "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.26.1" + "vimeo/psalm": "^5.8.0" }, "suggest": { "friendsofphp/proxy-manager-lts": "ProxyManager ^2.1.1 to handle lazy initialization of services" @@ -1264,34 +1858,34 @@ "type": "community_bridge" } ], - "time": "2025-10-14T09:03:51+00:00" + "time": "2023-10-24T11:19:47+00:00" }, { "name": "laminas/laminas-stdlib", - "version": "3.21.0", + "version": "3.19.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-stdlib.git", - "reference": "b1c81514cfe158aadf724c42b34d3d0a8164c096" + "reference": "6a192dd0882b514e45506f533b833b623b78fff3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/b1c81514cfe158aadf724c42b34d3d0a8164c096", - "reference": "b1c81514cfe158aadf724c42b34d3d0a8164c096", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/6a192dd0882b514e45506f533b833b623b78fff3", + "reference": "6a192dd0882b514e45506f533b833b623b78fff3", "shasum": "" }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "conflict": { "zendframework/zend-stdlib": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "^3.1.0", - "phpbench/phpbench": "^1.4.1", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1" + "laminas/laminas-coding-standard": "^2.5", + "phpbench/phpbench": "^1.2.15", + "phpunit/phpunit": "^10.5.8", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.20.0" }, "type": "library", "autoload": { @@ -1323,26 +1917,26 @@ "type": "community_bridge" } ], - "time": "2025-10-11T18:13:12+00:00" + "time": "2024-01-19T12:39:49+00:00" }, { "name": "laminas/laminas-stratigility", - "version": "3.14.0", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-stratigility.git", - "reference": "d23d128a22f79a67e1f9682df4c51719e3553c9d" + "reference": "4dee4580a8efea63a8b2b24dbf4604ee480e8cd6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-stratigility/zipball/d23d128a22f79a67e1f9682df4c51719e3553c9d", - "reference": "d23d128a22f79a67e1f9682df4c51719e3553c9d", + "url": "https://api.github.com/repos/laminas/laminas-stratigility/zipball/4dee4580a8efea63a8b2b24dbf4604ee480e8cd6", + "reference": "4dee4580a8efea63a8b2b24dbf4604ee480e8cd6", "shasum": "" }, "require": { "fig/http-message-util": "^1.1", "laminas/laminas-escaper": "^2.10.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "psr/http-message": "^1.0 || ^2.0", "psr/http-server-middleware": "^1.0.2" }, @@ -1350,11 +1944,11 @@ "zendframework/zend-stratigility": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "^3.1.0", - "laminas/laminas-diactoros": "^2.25 || ^3.8.0", - "phpunit/phpunit": "^10.5.58", - "psalm/plugin-phpunit": "^0.19.0", - "vimeo/psalm": "^5.26.1" + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-diactoros": "^2.25 || ^3.3", + "phpunit/phpunit": "^10.4.2", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.15.0" }, "suggest": { "psr/http-message-implementation": "Please install a psr/http-message-implementation to consume Stratigility; e.g., laminas/laminas-diactoros" @@ -1402,83 +1996,145 @@ "type": "community_bridge" } ], - "time": "2025-11-12T05:23:21+00:00" + "time": "2023-10-31T16:26:05+00:00" }, { "name": "laminas/laminas-validator", - "version": "2.65.0", + "version": "2.60.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-validator.git", - "reference": "f0767ca83e0dd91a6f8ccdd4f0887eb132c0ea49" + "reference": "66ab091fc08a8b1e2851eec62dda4bafa977fe9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/66ab091fc08a8b1e2851eec62dda4bafa977fe9c", + "reference": "66ab091fc08a8b1e2851eec62dda4bafa977fe9c", + "shasum": "" + }, + "require": { + "laminas/laminas-servicemanager": "^3.21.0", + "laminas/laminas-stdlib": "^3.13", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "psr/http-message": "^1.0.1 || ^2.0.0" + }, + "conflict": { + "zendframework/zend-validator": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "^2.5", + "laminas/laminas-db": "^2.20", + "laminas/laminas-filter": "^2.35.2", + "laminas/laminas-i18n": "^2.26.0", + "laminas/laminas-session": "^2.20", + "laminas/laminas-uri": "^2.11.0", + "phpunit/phpunit": "^10.5.20", + "psalm/plugin-phpunit": "^0.19.0", + "psr/http-client": "^1.0.3", + "psr/http-factory": "^1.1.0", + "vimeo/psalm": "^5.24.0" + }, + "suggest": { + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators", + "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Validator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "validator" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-validator/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-validator/issues", + "rss": "https://github.com/laminas/laminas-validator/releases.atom", + "source": "https://github.com/laminas/laminas-validator" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2024-06-25T20:11:22+00:00" + }, + { + "name": "laminas/laminas-zendframework-bridge", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-zendframework-bridge.git", + "reference": "eb0d96c708b92177a92bc2239543d3ed523452c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/f0767ca83e0dd91a6f8ccdd4f0887eb132c0ea49", - "reference": "f0767ca83e0dd91a6f8ccdd4f0887eb132c0ea49", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/eb0d96c708b92177a92bc2239543d3ed523452c6", + "reference": "eb0d96c708b92177a92bc2239543d3ed523452c6", "shasum": "" }, "require": { - "laminas/laminas-servicemanager": "^3.21.0", - "laminas/laminas-stdlib": "^3.19", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", - "psr/http-message": "^1.0.1 || ^2.0.0" - }, - "conflict": { - "zendframework/zend-validator": "*" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "require-dev": { - "laminas/laminas-coding-standard": "^2.5", - "laminas/laminas-db": "^2.20", - "laminas/laminas-filter": "^2.41.0", - "laminas/laminas-i18n": "^2.30.0", - "laminas/laminas-session": "^2.25.1", - "laminas/laminas-uri": "^2.13.0", - "phpunit/phpunit": "^10.5.58", - "psalm/plugin-phpunit": "^0.19.0", - "psr/http-client": "^1.0.3", - "psr/http-factory": "^1.1.0", - "vimeo/psalm": "^5.26.1" - }, - "suggest": { - "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", - "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", - "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", - "laminas/laminas-i18n-resources": "Translations of validator messages", - "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", - "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators", - "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators" + "phpunit/phpunit": "^10.4", + "psalm/plugin-phpunit": "^0.18.0", + "squizlabs/php_codesniffer": "^3.7.1", + "vimeo/psalm": "^5.16.0" }, "type": "library", "extra": { "laminas": { - "component": "Laminas\\Validator", - "config-provider": "Laminas\\Validator\\ConfigProvider" + "module": "Laminas\\ZendFrameworkBridge" } }, "autoload": { + "files": [ + "src/autoload.php" + ], "psr-4": { - "Laminas\\Validator\\": "src/" + "Laminas\\ZendFrameworkBridge\\": "src//" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", - "homepage": "https://laminas.dev", + "description": "Alias legacy ZF class names to Laminas Project equivalents.", "keywords": [ + "ZendFramework", + "autoloading", "laminas", - "validator" + "zf" ], "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-validator/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-validator/issues", - "rss": "https://github.com/laminas/laminas-validator/releases.atom", - "source": "https://github.com/laminas/laminas-validator" + "forum": "https://discourse.laminas.dev/", + "issues": "https://github.com/laminas/laminas-zendframework-bridge/issues", + "rss": "https://github.com/laminas/laminas-zendframework-bridge/releases.atom", + "source": "https://github.com/laminas/laminas-zendframework-bridge" }, "funding": [ { @@ -1486,34 +2142,35 @@ "type": "community_bridge" } ], - "time": "2025-10-13T14:40:30+00:00" + "abandoned": true, + "time": "2023-11-24T13:56:19+00:00" }, { "name": "mezzio/mezzio", - "version": "3.23.2", + "version": "3.19.0", "source": { "type": "git", "url": "https://github.com/mezzio/mezzio.git", - "reference": "988d39687683c9ae70d213c68c75c89965caad30" + "reference": "e9bbc0addbf2dbde6721e4d30a965d8512c5ce54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mezzio/mezzio/zipball/988d39687683c9ae70d213c68c75c89965caad30", - "reference": "988d39687683c9ae70d213c68c75c89965caad30", + "url": "https://api.github.com/repos/mezzio/mezzio/zipball/e9bbc0addbf2dbde6721e4d30a965d8512c5ce54", + "reference": "e9bbc0addbf2dbde6721e4d30a965d8512c5ce54", "shasum": "" }, "require": { "fig/http-message-util": "^1.1.5", "laminas/laminas-httphandlerrunner": "^2.1", "laminas/laminas-stratigility": "^3.5", - "mezzio/mezzio-router": "^3.15.0", + "mezzio/mezzio-router": "^3.7", "mezzio/mezzio-template": "^2.2", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "psr/container": "^1.0||^2.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0.1 || ^2.0.0", "psr/http-server-middleware": "^1.0", - "webmozart/assert": "^1.11.0" + "webmozart/assert": "^1.10" }, "conflict": { "container-interop/container-interop": "<1.2.0", @@ -1525,15 +2182,16 @@ "zendframework/zend-expressive": "*" }, "require-dev": { - "filp/whoops": "^2.18.4", - "laminas/laminas-coding-standard": "^3.1.0", - "laminas/laminas-diactoros": "^3.8.0", - "laminas/laminas-servicemanager": "^3.23.1", - "mezzio/mezzio-fastroute": "^3.14", - "mezzio/mezzio-laminasrouter": "^3.12", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1" + "filp/whoops": "^2.15.4", + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-diactoros": "^3.3.0", + "laminas/laminas-servicemanager": "^3.22.1", + "mezzio/mezzio-aurarouter": "^3.7", + "mezzio/mezzio-fastroute": "^3.11", + "mezzio/mezzio-laminasrouter": "^3.9", + "phpunit/phpunit": "^10.5.9", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.21.1" }, "suggest": { "filp/whoops": "^2.1 to use the Whoops error handler", @@ -1592,98 +2250,28 @@ "type": "community_bridge" } ], - "time": "2025-10-22T10:56:04+00:00" - }, - { - "name": "mezzio/mezzio-cors", - "version": "1.15.0", - "source": { - "type": "git", - "url": "https://github.com/mezzio/mezzio-cors.git", - "reference": "0465bd920f326033de0191f3a56692ddbd44b994" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mezzio/mezzio-cors/zipball/0465bd920f326033de0191f3a56692ddbd44b994", - "reference": "0465bd920f326033de0191f3a56692ddbd44b994", - "shasum": "" - }, - "require": { - "fig/http-message-util": "^1.1", - "mezzio/mezzio-router": "^3.5 || ^4.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", - "psr/container": "^1.0 || ^2.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0 || ^2.0", - "psr/http-server-handler": "^1.0", - "psr/http-server-middleware": "^1.0", - "webmozart/assert": "^1.11.0" - }, - "require-dev": { - "laminas/laminas-coding-standard": "~3.1.0", - "laminas/laminas-diactoros": "^3.8.0", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1" - }, - "type": "library", - "extra": { - "laminas": { - "config-provider": "Mezzio\\Cors\\ConfigProvider" - } - }, - "autoload": { - "psr-4": { - "Mezzio\\Cors\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "CORS component for Mezzio and other PSR-15 middleware runners.", - "keywords": [ - "cors", - "mezzio", - "psr-15", - "psr-7" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/mezzio-cors/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/mezzio/mezzio-cors/issues", - "rss": "https://github.com/mezzio/mezzio-cli/releases.atom", - "source": "https://github.com/mezzio/mezzio-cors" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2025-10-12T19:30:45+00:00" + "time": "2024-02-14T10:42:11+00:00" }, { "name": "mezzio/mezzio-fastroute", - "version": "3.14.0", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/mezzio/mezzio-fastroute.git", - "reference": "00b1dd8560566d745a5a3a18582d1242ad51dd64" + "reference": "118ef1009c7252dc408c28b3041f4b04751d7321" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mezzio/mezzio-fastroute/zipball/00b1dd8560566d745a5a3a18582d1242ad51dd64", - "reference": "00b1dd8560566d745a5a3a18582d1242ad51dd64", + "url": "https://api.github.com/repos/mezzio/mezzio-fastroute/zipball/118ef1009c7252dc408c28b3041f4b04751d7321", + "reference": "118ef1009c7252dc408c28b3041f4b04751d7321", "shasum": "" }, "require": { "fig/http-message-util": "^1.1.2", - "laminas/laminas-stdlib": "^3.19.0", - "mezzio/mezzio-router": "^3.18 || ^4.0.1", + "laminas/laminas-stdlib": "^3.1", + "mezzio/mezzio-router": "^3.14", "nikic/fast-route": "^1.2", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "psr/container": "^1.0 || ^2.0", "psr/http-message": "^1.0.1 || ^2.0.0" }, @@ -1692,13 +2280,13 @@ "zendframework/zend-expressive-fastroute": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "~3.1.0", - "laminas/laminas-diactoros": "^3.6.0", - "laminas/laminas-stratigility": "^4.2.0", - "mikey179/vfsstream": "^1.6.12", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1" + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-diactoros": "^3.3.0", + "laminas/laminas-stratigility": "^3.11", + "mikey179/vfsstream": "^1.6.11", + "phpunit/phpunit": "^10.4.2", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.15" }, "type": "library", "extra": { @@ -1740,42 +2328,39 @@ "type": "community_bridge" } ], - "time": "2025-10-11T08:43:04+00:00" + "time": "2023-11-01T11:16:57+00:00" }, { "name": "mezzio/mezzio-helpers", - "version": "5.20.0", + "version": "5.16.0", "source": { "type": "git", "url": "https://github.com/mezzio/mezzio-helpers.git", - "reference": "a26ba04bd449d5cdb5ad38b17ce672365dbc9d90" + "reference": "39ede1ba9ac6398d535339c1fbabcd6e40a55110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mezzio/mezzio-helpers/zipball/a26ba04bd449d5cdb5ad38b17ce672365dbc9d90", - "reference": "a26ba04bd449d5cdb5ad38b17ce672365dbc9d90", + "url": "https://api.github.com/repos/mezzio/mezzio-helpers/zipball/39ede1ba9ac6398d535339c1fbabcd6e40a55110", + "reference": "39ede1ba9ac6398d535339c1fbabcd6e40a55110", "shasum": "" }, "require": { - "mezzio/mezzio-router": "^3.18 || ^4.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "mezzio/mezzio-router": "^3.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "psr/container": "^1.0 || ^2.0", "psr/http-message": "^1.0.1 || ^2.0.0", "psr/http-server-middleware": "^1.0" }, "conflict": { - "amphp/amp": "<2.6.4", - "amphp/dns": "<2.1.2", - "amphp/socket": "<2.3.1", "zendframework/zend-expressive-helpers": "*" }, "require-dev": { "ext-json": "*", - "laminas/laminas-coding-standard": "~3.1.0", - "laminas/laminas-diactoros": "^3.6", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1" + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-diactoros": "^3.3", + "phpunit/phpunit": "^10.4.2", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.15" }, "suggest": { "ext-json": "If you wish to use the JsonStrategy with BodyParamsMiddleware" @@ -1819,25 +2404,25 @@ "type": "community_bridge" } ], - "time": "2025-10-11T08:40:34+00:00" + "time": "2023-11-01T11:19:55+00:00" }, { "name": "mezzio/mezzio-router", - "version": "3.19.0", + "version": "3.17.0", "source": { "type": "git", "url": "https://github.com/mezzio/mezzio-router.git", - "reference": "3df4363e70611ddf096db95c62df6aa98817872c" + "reference": "78573e16144a70ccf02039e1a2600788119c0dbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mezzio/mezzio-router/zipball/3df4363e70611ddf096db95c62df6aa98817872c", - "reference": "3df4363e70611ddf096db95c62df6aa98817872c", + "url": "https://api.github.com/repos/mezzio/mezzio-router/zipball/78573e16144a70ccf02039e1a2600788119c0dbb", + "reference": "78573e16144a70ccf02039e1a2600788119c0dbb", "shasum": "" }, "require": { "fig/http-message-util": "^1.1.5", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "psr/container": "^1.1.2 || ^2.0", "psr/http-factory": "^1.0.2", "psr/http-message": "^1.0.1 || ^2.0.0", @@ -1849,13 +2434,13 @@ "zendframework/zend-expressive-router": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "~3.1.0", - "laminas/laminas-diactoros": "^3.6.0", - "laminas/laminas-servicemanager": "^4.4.0", - "laminas/laminas-stratigility": "^4.2.0", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.0", - "vimeo/psalm": "^6.13.1" + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-diactoros": "^3.3.0", + "laminas/laminas-servicemanager": "^3.22.1", + "laminas/laminas-stratigility": "^3.11.0", + "phpunit/phpunit": "^10.4.2", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.15" }, "suggest": { "mezzio/mezzio-aurarouter": "^3.0 to use the Aura.Router routing adapter", @@ -1901,33 +2486,33 @@ "type": "community_bridge" } ], - "time": "2025-10-11T08:41:44+00:00" + "time": "2023-10-31T17:23:17+00:00" }, { "name": "mezzio/mezzio-template", - "version": "2.13.0", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/mezzio/mezzio-template.git", - "reference": "ad72bb31036d0639a5c5a502af234217faf6932f" + "reference": "2cc943c996b8a63f1f945a2b891346ecbc8f7a33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mezzio/mezzio-template/zipball/ad72bb31036d0639a5c5a502af234217faf6932f", - "reference": "ad72bb31036d0639a5c5a502af234217faf6932f", + "url": "https://api.github.com/repos/mezzio/mezzio-template/zipball/2cc943c996b8a63f1f945a2b891346ecbc8f7a33", + "reference": "2cc943c996b8a63f1f945a2b891346ecbc8f7a33", "shasum": "" }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "conflict": { "zendframework/zend-expressive-template": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "~3.1.0", - "phpunit/phpunit": "^11.5.42", - "psalm/plugin-phpunit": "^0.19.5", - "vimeo/psalm": "^6.13.1" + "laminas/laminas-coding-standard": "~2.5.0", + "phpunit/phpunit": "^10.4.2", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.15" }, "suggest": { "mezzio/mezzio-laminasviewrenderer": "^2.0 to use the laminas-view PhpRenderer template renderer", @@ -1965,20 +2550,20 @@ "type": "community_bridge" } ], - "time": "2025-10-11T08:45:28+00:00" + "time": "2024-01-08T15:20:45+00:00" }, { "name": "mezzio/mezzio-tooling", - "version": "2.12.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/mezzio/mezzio-tooling.git", - "reference": "41e8242b27398d0511223d48bbd9efc97d6a2e40" + "reference": "6108df988eb6f7d977d870a2ab7af035cbe8a0ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mezzio/mezzio-tooling/zipball/41e8242b27398d0511223d48bbd9efc97d6a2e40", - "reference": "41e8242b27398d0511223d48bbd9efc97d6a2e40", + "url": "https://api.github.com/repos/mezzio/mezzio-tooling/zipball/6108df988eb6f7d977d870a2ab7af035cbe8a0ae", + "reference": "6108df988eb6f7d977d870a2ab7af035cbe8a0ae", "shasum": "" }, "require": { @@ -1989,22 +2574,17 @@ "laminas/laminas-stratigility": "^3.9.0", "mezzio/mezzio": "^3.13.0", "mezzio/mezzio-router": "^3.9.0", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.1", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "symfony/process": "^6.0.11" }, - "conflict": { - "amphp/amp": "<2.6.4", - "symfony/console": "<5.4.45", - "symfony/string": "<5.4.45" - }, "require-dev": { "laminas/laminas-coding-standard": "~2.5.0", "laminas/laminas-diactoros": "^3.3", - "mikey179/vfsstream": "^1.6.12", - "mockery/mockery": "^1.6.10", + "mikey179/vfsstream": "^1.6.11", + "mockery/mockery": "^1.6.7", "php-mock/php-mock-phpunit": "^2.9.0", "phpdocumentor/reflection-docblock": "^5.3.0", - "phpunit/phpunit": "^10.5.35", + "phpunit/phpunit": "^9.6.15", "psalm/plugin-mockery": "^0.11.0", "psalm/plugin-phpunit": "^0.18.4", "vimeo/psalm": "^5.17.0" @@ -2048,110 +2628,7 @@ "type": "community_bridge" } ], - "time": "2025-09-12T08:39:37+00:00" - }, - { - "name": "monolog/monolog", - "version": "3.10.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", - "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/log": "^2.0 || ^3.0" - }, - "provide": { - "psr/log-implementation": "3.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^3.0", - "doctrine/couchdb": "~1.0@dev", - "elasticsearch/elasticsearch": "^7 || ^8", - "ext-json": "*", - "graylog2/gelf-php": "^1.4.2 || ^2.0", - "guzzlehttp/guzzle": "^7.4.5", - "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8 || ^2.0", - "php-amqplib/php-amqplib": "~2.4 || ^3", - "php-console/php-console": "^3.1.8", - "phpstan/phpstan": "^2", - "phpstan/phpstan-deprecation-rules": "^2", - "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "^10.5.17 || ^11.0.7", - "predis/predis": "^1.1 || ^2", - "rollbar/rollbar": "^4.0", - "ruflin/elastica": "^7 || ^8", - "symfony/mailer": "^5.4 || ^6", - "symfony/mime": "^5.4 || ^6" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", - "ext-mbstring": "Allow to work properly with unicode symbols", - "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", - "ext-openssl": "Required to send log messages using SSL", - "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Monolog\\": "src/Monolog" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" - } - ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "https://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" - ], - "support": { - "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.10.0" - }, - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } - ], - "time": "2026-01-02T08:56:05+00:00" + "time": "2023-12-12T16:13:23+00:00" }, { "name": "nikic/fast-route", @@ -2205,16 +2682,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.7.0", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { @@ -2233,7 +2710,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.x-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -2257,56 +2734,58 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2025-12-06T11:56:16+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { - "name": "phpstan/phpdoc-parser", - "version": "2.3.1", + "name": "psr/cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374" + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/16dbf9937da8d4528ceb2145c9c7c0bd29e26374", - "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^5.3.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6", - "symfony/process": "^5.2" + "php": ">=8.0.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] + "Psr\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.1" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, - "time": "2026-01-12T11:33:04+00:00" + "time": "2021-02-03T23:26:27+00:00" }, { "name": "psr/container", @@ -2463,16 +2942,16 @@ }, { "name": "psr/http-message", - "version": "2.0", + "version": "1.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", "shasum": "" }, "require": { @@ -2481,7 +2960,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -2496,7 +2975,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", @@ -2510,9 +2989,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" + "source": "https://github.com/php-fig/http-message/tree/1.1" }, - "time": "2023-04-04T09:54:51+00:00" + "time": "2023-04-04T09:50:52+00:00" }, { "name": "psr/http-server-handler", @@ -2629,30 +3108,30 @@ }, { "name": "psr/log", - "version": "3.0.2", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "src" + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2673,84 +3152,22 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" - }, - "time": "2024-09-11T13:17:53+00:00" - }, - { - "name": "radebatz/type-info-extras", - "version": "1.0.4", - "source": { - "type": "git", - "url": "https://github.com/DerManoMann/type-info-extras.git", - "reference": "23b74c4690fb3d147a1b34c4dc090a9ddc6dc31a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DerManoMann/type-info-extras/zipball/23b74c4690fb3d147a1b34c4dc090a9ddc6dc31a", - "reference": "23b74c4690fb3d147a1b34c4dc090a9ddc6dc31a", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "phpstan/phpdoc-parser": "^2.0", - "symfony/type-info": "^7.3.8 || ^7.4.1 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.70", - "phpstan/phpstan": "^2.1", - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Radebatz\\TypeInfoExtras\\": "src" - }, - "exclude-from-classmap": [ - "/tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Martin Rademacher", - "email": "mano@radebatz.org" - } - ], - "description": "Extras for symfony/type-info", - "homepage": "http://radebatz.net/mano/", - "keywords": [ - "component", - "symfony", - "type-info", - "types" - ], - "support": { - "issues": "https://github.com/DerManoMann/type-info-extras/issues", - "source": "https://github.com/DerManoMann/type-info-extras/tree/1.0.4" + "source": "https://github.com/php-fig/log/tree/1.1.4" }, - "time": "2026-01-12T21:15:50+00:00" + "time": "2021-05-03T11:20:27+00:00" }, { "name": "ramsey/collection", - "version": "2.1.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", - "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", "shasum": "" }, "require": { @@ -2758,22 +3175,25 @@ }, "require-dev": { "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.45", - "fakerphp/faker": "^1.24", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^2.1", - "mockery/mockery": "^1.6", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.4", - "phpspec/prophecy-phpunit": "^2.3", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-mockery": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^10.5", - "ramsey/coding-standard": "^2.3", - "ramsey/conventional-commits": "^1.6", - "roave/security-advisories": "dev-latest" + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" }, "type": "library", "extra": { @@ -2811,26 +3231,37 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.1.1" + "source": "https://github.com/ramsey/collection/tree/2.0.0" }, - "time": "2025-03-22T05:38:12+00:00" + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" }, { "name": "ramsey/uuid", - "version": "4.9.2", + "version": "4.7.6", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "8429c78ca35a09f27565311b98101e2826affde0" + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", - "reference": "8429c78ca35a09f27565311b98101e2826affde0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", "shasum": "" }, "require": { - "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "ext-json": "*", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -2838,23 +3269,26 @@ "rhumsaa/uuid": "self.version" }, "require-dev": { - "captainhook/captainhook": "^5.25", + "captainhook/captainhook": "^5.10", "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "ergebnis/composer-normalize": "^2.47", - "mockery/mockery": "^1.6", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.6", - "php-mock/php-mock-mockery": "^1.5", - "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpbench/phpbench": "^1.2.14", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-mockery": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^9.6", - "slevomat/coding-standard": "^8.18", - "squizlabs/php_codesniffer": "^3.13" + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" }, "suggest": { "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", @@ -2889,53 +3323,63 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.2" + "source": "https://github.com/ramsey/uuid/tree/4.7.6" }, - "time": "2025-12-14T04:43:48+00:00" + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2024-04-27T21:32:50+00:00" }, { "name": "symfony/console", - "version": "v7.4.3", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6" + "reference": "6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/732a9ca6cd9dfd940c639062d5edbde2f6727fb6", - "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "url": "https://api.github.com/repos/symfony/console/zipball/6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9", + "reference": "6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2|^8.0" + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -2969,7 +3413,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.3" + "source": "https://github.com/symfony/console/tree/v6.4.9" }, "funding": [ { @@ -2980,29 +3424,25 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-12-23T14:50:43+00:00" + "time": "2024-06-28T09:49:33+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { @@ -3010,12 +3450,12 @@ }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -3040,7 +3480,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -3056,20 +3496,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.4.0", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d" + "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d", - "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", "shasum": "" }, "require": { @@ -3086,14 +3526,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/error-handler": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/framework-bundle": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0|^8.0" + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -3121,7 +3560,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1" }, "funding": [ { @@ -3132,29 +3571,25 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-10-28T09:38:46+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", "shasum": "" }, "require": { @@ -3163,12 +3598,12 @@ }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -3201,7 +3636,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" }, "funding": [ { @@ -3217,27 +3652,27 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/finder", - "version": "v7.4.3", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "fffe05569336549b20a1be64250b40516d6e8d06" + "reference": "3ef977a43883215d560a2cecb82ec8e62131471c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/fffe05569336549b20a1be64250b40516d6e8d06", - "reference": "fffe05569336549b20a1be64250b40516d6e8d06", + "url": "https://api.github.com/repos/symfony/finder/zipball/3ef977a43883215d560a2cecb82ec8e62131471c", + "reference": "3ef977a43883215d560a2cecb82ec8e62131471c", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.1" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0|^8.0" + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -3265,7 +3700,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.3" + "source": "https://github.com/symfony/finder/tree/v6.4.8" }, "funding": [ { @@ -3276,48 +3711,48 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-12-23T14:50:43+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/mailer", - "version": "v8.0.3", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "02e033db6e00a42c66b8b8992e4e565ea7464a28" + "reference": "e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/02e033db6e00a42c66b8b8992e4e565ea7464a28", - "reference": "02e033db6e00a42c66b8b8992e4e565ea7464a28", + "url": "https://api.github.com/repos/symfony/mailer/zipball/e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45", + "reference": "e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45", "shasum": "" }, "require": { "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=8.4", + "php": ">=8.1", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^7.4|^8.0", - "symfony/mime": "^7.4|^8.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/http-client-contracts": "<2.5" + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/messenger": "<6.2", + "symfony/mime": "<6.2", + "symfony/twig-bridge": "<6.2.1" }, "require-dev": { - "symfony/console": "^7.4|^8.0", - "symfony/http-client": "^7.4|^8.0", - "symfony/messenger": "^7.4|^8.0", - "symfony/twig-bridge": "^7.4|^8.0" + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.0" }, "type": "library", "autoload": { @@ -3345,7 +3780,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v8.0.3" + "source": "https://github.com/symfony/mailer/tree/v6.4.9" }, "funding": [ { @@ -3356,50 +3791,48 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-12-16T08:10:18+00:00" + "time": "2024-06-28T07:59:05+00:00" }, { "name": "symfony/mime", - "version": "v8.0.0", + "version": "v7.1.2", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "7576ce3b2b4d3a2a7fe7020a07a392065d6ffd40" + "reference": "26a00b85477e69a4bab63b66c5dce64f18b0cbfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/7576ce3b2b4d3a2a7fe7020a07a392065d6ffd40", - "reference": "7576ce3b2b4d3a2a7fe7020a07a392065d6ffd40", + "url": "https://api.github.com/repos/symfony/mime/zipball/26a00b85477e69a4bab63b66c5dce64f18b0cbfc", + "reference": "26a00b85477e69a4bab63b66c5dce64f18b0cbfc", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.2", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "egulias/email-validator": "~3.0.0", "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0" + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/process": "^7.4|^8.0", - "symfony/property-access": "^7.4|^8.0", - "symfony/property-info": "^7.4|^8.0", - "symfony/serializer": "^7.4|^8.0" + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" }, "type": "library", "autoload": { @@ -3431,7 +3864,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v8.0.0" + "source": "https://github.com/symfony/mime/tree/v7.1.2" }, "funding": [ { @@ -3442,33 +3875,29 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-11-16T10:17:21+00:00" + "time": "2024-06-28T10:03:55+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.1" }, "provide": { "ext-ctype": "*" @@ -3479,8 +3908,8 @@ "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -3514,7 +3943,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" }, "funding": [ { @@ -3525,33 +3954,29 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance" @@ -3559,8 +3984,8 @@ "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -3596,7 +4021,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" }, "funding": [ { @@ -3607,34 +4032,31 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", + "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", "shasum": "" }, "require": { - "php": ">=7.2", - "symfony/polyfill-intl-normalizer": "^1.10" + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" }, "suggest": { "ext-intl": "For best performance" @@ -3642,8 +4064,8 @@ "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -3683,7 +4105,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.30.0" }, "funding": [ { @@ -3694,33 +4116,29 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-10T14:38:51+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance" @@ -3728,8 +4146,8 @@ "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -3768,7 +4186,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" }, "funding": [ { @@ -3779,34 +4197,29 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", "shasum": "" }, "require": { - "ext-iconv": "*", - "php": ">=7.2" + "php": ">=7.1" }, "provide": { "ext-mbstring": "*" @@ -3817,8 +4230,8 @@ "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -3853,72 +4266,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-12-23T08:48:59+00:00" - }, - { - "name": "symfony/process", - "version": "v6.4.31", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "8541b7308fca001320e90bca8a73a28aa5604a6e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/8541b7308fca001320e90bca8a73a28aa5604a6e", - "reference": "8541b7308fca001320e90bca8a73a28aa5604a6e", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v6.4.31" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" }, "funding": [ { @@ -3929,56 +4277,44 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-12-15T19:26:35+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { - "name": "symfony/service-contracts", - "version": "v3.6.1", + "name": "symfony/polyfill-php72", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "10112722600777e02d2745716b70c5db4ca70442" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/10112722600777e02d2745716b70c5db4ca70442", + "reference": "10112722600777e02d2745716b70c5db4ca70442", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" + "php": ">=7.1" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + "Symfony\\Polyfill\\Php72\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3994,18 +4330,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to writing services", + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.30.0" }, "funding": [ { @@ -4016,58 +4350,46 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-07-15T11:30:57+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { - "name": "symfony/string", - "version": "v8.0.1", + "name": "symfony/polyfill-php80", + "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "shasum": "" }, "require": { - "php": ">=8.4", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-intl-grapheme": "^1.33", - "symfony/polyfill-intl-normalizer": "^1.0", - "symfony/polyfill-mbstring": "^1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/emoji": "^7.4|^8.0", - "symfony/http-client": "^7.4|^8.0", - "symfony/intl": "^7.4|^8.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^7.4|^8.0" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, "autoload": { "files": [ - "Resources/functions.php" + "bootstrap.php" ], "psr-4": { - "Symfony\\Component\\String\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -4075,6 +4397,10 @@ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -4084,18 +4410,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.1" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" }, "funding": [ { @@ -4106,45 +4430,34 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-12-01T09:13:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/type-info", - "version": "v8.0.1", + "name": "symfony/process", + "version": "v6.4.14", "source": { "type": "git", - "url": "https://github.com/symfony/type-info.git", - "reference": "bb091cec1f70383538c7d000699781813f8d1a6a" + "url": "https://github.com/symfony/process.git", + "reference": "25214adbb0996d18112548de20c281be9f27279f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/bb091cec1f70383538c7d000699781813f8d1a6a", - "reference": "bb091cec1f70383538c7d000699781813f8d1a6a", + "url": "https://api.github.com/repos/symfony/process/zipball/25214adbb0996d18112548de20c281be9f27279f", + "reference": "25214adbb0996d18112548de20c281be9f27279f", "shasum": "" }, "require": { - "php": ">=8.4", - "psr/container": "^1.1|^2.0" - }, - "conflict": { - "phpstan/phpdoc-parser": "<1.30" - }, - "require-dev": { - "phpstan/phpdoc-parser": "^1.30|^2.0" + "php": ">=8.1" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\TypeInfo\\": "" + "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -4156,28 +4469,18 @@ ], "authors": [ { - "name": "Mathias Arlaud", - "email": "mathias.arlaud@gmail.com" - }, - { - "name": "Baptiste LEDUC", - "email": "baptiste.leduc@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Extracts PHP types information.", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "keywords": [ - "PHPStan", - "phpdoc", - "symfony", - "type" - ], "support": { - "source": "https://github.com/symfony/type-info/tree/v8.0.1" + "source": "https://github.com/symfony/process/tree/v6.4.14" }, "funding": [ { @@ -4188,52 +4491,51 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-12-05T14:08:45+00:00" + "time": "2024-11-06T09:25:01+00:00" }, { - "name": "symfony/yaml", - "version": "v7.4.1", + "name": "symfony/service-contracts", + "version": "v3.5.0", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "24dd4de28d2e3988b311751ac49e684d783e2345" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/24dd4de28d2e3988b311751ac49e684d783e2345", - "reference": "24dd4de28d2e3988b311751ac49e684d783e2345", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "^1.8" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { - "symfony/console": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0" + "ext-psr": "<1.1|>=2" }, - "bin": [ - "Resources/bin/yaml-lint" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, "autoload": { "psr-4": { - "Symfony\\Component\\Yaml\\": "" + "Symfony\\Contracts\\Service\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -4242,18 +4544,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" }, "funding": [ { @@ -4264,111 +4574,118 @@ "url": "https://github.com/fabpot", "type": "github" }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-12-04T18:11:45+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { - "name": "webimpress/safe-writer", - "version": "2.2.0", + "name": "symfony/stopwatch", + "version": "v7.1.1", "source": { "type": "git", - "url": "https://github.com/webimpress/safe-writer.git", - "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6" + "url": "https://github.com/symfony/stopwatch.git", + "reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webimpress/safe-writer/zipball/9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", - "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d", + "reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d", "shasum": "" }, "require": { - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.4", - "vimeo/psalm": "^4.7", - "webimpress/coding-standard": "^1.2.2" + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.2.x-dev", - "dev-develop": "2.3.x-dev", - "dev-release-1.0": "1.0.x-dev" - } - }, "autoload": { "psr-4": { - "Webimpress\\SafeWriter\\": "src/" - } + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-2-Clause" + "MIT" ], - "description": "Tool to write files safely, to avoid race conditions", - "keywords": [ - "concurrent write", - "file writer", - "race condition", - "safe writer", - "webimpress" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/webimpress/safe-writer/issues", - "source": "https://github.com/webimpress/safe-writer/tree/2.2.0" + "source": "https://github.com/symfony/stopwatch/tree/v7.1.1" }, "funding": [ { - "url": "https://github.com/michalbundyra", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2021-04-19T16:34:45+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { - "name": "webmozart/assert", - "version": "1.12.1", + "name": "symfony/string", + "version": "v7.1.2", "source": { "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + "url": "https://github.com/symfony/string.git", + "reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "url": "https://api.github.com/repos/symfony/string/zipball/14221089ac66cf82e3cf3d1c1da65de305587ff8", + "reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8", "shasum": "" }, "require": { - "ext-ctype": "*", - "ext-date": "*", - "ext-filter": "*", - "php": "^7.2 || ^8.0" + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" }, - "suggest": { - "ext-intl": "", - "ext-simplexml": "", - "ext-spl": "" + "conflict": { + "symfony/translation-contracts": "<2.5" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" }, + "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { - "Webmozart\\Assert\\": "src/" - } + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4376,511 +4693,425 @@ ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Assertions to validate method input/output with nice error messages.", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", "keywords": [ - "assert", - "check", - "validate" + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" ], "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.1" + "source": "https://github.com/symfony/string/tree/v7.1.2" }, - "time": "2025-10-29T15:56:20+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-28T09:27:18+00:00" }, { - "name": "zircote/swagger-php", - "version": "6.0.1", + "name": "symfony/var-exporter", + "version": "v7.1.2", "source": { "type": "git", - "url": "https://github.com/zircote/swagger-php.git", - "reference": "cf332956e6603fe4c8da6223a98b7ba65e20231e" + "url": "https://github.com/symfony/var-exporter.git", + "reference": "b80a669a2264609f07f1667f891dbfca25eba44c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/cf332956e6603fe4c8da6223a98b7ba65e20231e", - "reference": "cf332956e6603fe4c8da6223a98b7ba65e20231e", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/b80a669a2264609f07f1667f891dbfca25eba44c", + "reference": "b80a669a2264609f07f1667f891dbfca25eba44c", "shasum": "" }, "require": { - "ext-json": "*", - "nikic/php-parser": "^4.19 || ^5.0", - "php": ">=8.2", - "phpstan/phpdoc-parser": "^2.0", - "psr/log": "^1.1 || ^2.0 || ^3.0", - "radebatz/type-info-extras": "^1.0.2", - "symfony/deprecation-contracts": "^2 || ^3", - "symfony/finder": "^5.0 || ^6.0 || ^7.0 || ^8.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0" - }, - "conflict": { - "symfony/process": ">=6, <6.4.14" + "php": ">=8.2" }, "require-dev": { - "composer/package-versions-deprecated": "^1.11", - "doctrine/annotations": "^2.0", - "friendsofphp/php-cs-fixer": "^3.62.0", - "phpstan/phpstan": "^2.0", - "phpunit/phpunit": "^11.5", - "rector/rector": "^2.3.1" + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, - "bin": [ - "bin/openapi" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.x-dev" - } - }, "autoload": { "psr-4": { - "OpenApi\\": "src" - } + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], "authors": [ { - "name": "Robert Allen", - "email": "zircote@gmail.com" - }, - { - "name": "Bob Fanger", - "email": "bfanger@gmail.com", - "homepage": "https://bfanger.nl" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Martin Rademacher", - "email": "mano@radebatz.net", - "homepage": "https://radebatz.net" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Generate interactive documentation for your RESTful API using PHP attributes (preferred) or PHPDoc annotations", - "homepage": "https://github.com/zircote/swagger-php", + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", "keywords": [ - "api", - "json", - "rest", - "service discovery" + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" ], "support": { - "issues": "https://github.com/zircote/swagger-php/issues", - "source": "https://github.com/zircote/swagger-php/tree/6.0.1" + "source": "https://github.com/symfony/var-exporter/tree/v7.1.2" }, "funding": [ { - "url": "https://github.com/zircote", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2026-01-15T19:54:20+00:00" - } - ], - "packages-dev": [ + "time": "2024-06-28T08:00:31+00:00" + }, { - "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.2.0", + "name": "symfony/yaml", + "version": "v6.4.8", "source": { "type": "git", - "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1" + "url": "https://github.com/symfony/yaml.git", + "reference": "52903de178d542850f6f341ba92995d3d63e60c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1", - "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1", + "url": "https://api.github.com/repos/symfony/yaml/zipball/52903de178d542850f6f341ba92995d3d63e60c9", + "reference": "52903de178d542850f6f341ba92995d3d63e60c9", "shasum": "" }, "require": { - "composer-plugin-api": "^2.2", - "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" }, - "require-dev": { - "composer/composer": "^2.2", - "ext-json": "*", - "ext-zip": "*", - "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev", - "yoast/phpunit-polyfills": "^1.0" + "conflict": { + "symfony/console": "<5.4" }, - "type": "composer-plugin", - "extra": { - "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", "autoload": { "psr-4": { - "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" - } + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Franck Nijhof", - "email": "opensource@frenck.dev", - "homepage": "https://frenck.dev", - "role": "Open source developer" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "keywords": [ - "PHPCodeSniffer", - "PHP_CodeSniffer", - "code quality", - "codesniffer", - "composer", - "installer", - "phpcbf", - "phpcs", - "plugin", - "qa", - "quality", - "standard", - "standards", - "style guide", - "stylecheck", - "tests" + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/PHPCSStandards/composer-installer/issues", - "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", - "source": "https://github.com/PHPCSStandards/composer-installer" + "source": "https://github.com/symfony/yaml/tree/v6.4.8" }, "funding": [ { - "url": "https://github.com/PHPCSStandards", - "type": "github" + "url": "https://symfony.com/sponsor", + "type": "custom" }, { - "url": "https://github.com/jrfnl", + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - }, - { - "url": "https://thanks.dev/u/gh/phpcsstandards", - "type": "thanks_dev" + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2025-11-11T04:32:07+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { - "name": "doctrine/dbal", - "version": "4.4.1", + "name": "webimpress/safe-writer", + "version": "2.2.0", "source": { "type": "git", - "url": "https://github.com/doctrine/dbal.git", - "reference": "3d544473fb93f5c25b483ea4f4ce99f8c4d9d44c" + "url": "https://github.com/webimpress/safe-writer.git", + "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/3d544473fb93f5c25b483ea4f4ce99f8c4d9d44c", - "reference": "3d544473fb93f5c25b483ea4f4ce99f8c4d9d44c", + "url": "https://api.github.com/repos/webimpress/safe-writer/zipball/9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", + "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.1.5", - "php": "^8.2", - "psr/cache": "^1|^2|^3", - "psr/log": "^1|^2|^3" + "php": "^7.3 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "14.0.0", - "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2023.2", - "phpstan/phpstan": "2.1.30", - "phpstan/phpstan-phpunit": "2.0.7", - "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "11.5.23", - "slevomat/coding-standard": "8.24.0", - "squizlabs/php_codesniffer": "4.0.0", - "symfony/cache": "^6.3.8|^7.0|^8.0", - "symfony/console": "^5.4|^6.3|^7.0|^8.0" - }, - "suggest": { - "symfony/console": "For helpful console commands such as SQL execution and import of files." + "phpunit/phpunit": "^9.5.4", + "vimeo/psalm": "^4.7", + "webimpress/coding-standard": "^1.2.2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev", + "dev-develop": "2.3.x-dev", + "dev-release-1.0": "1.0.x-dev" + } + }, "autoload": { "psr-4": { - "Doctrine\\DBAL\\": "src" + "Webimpress\\SafeWriter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - } + "BSD-2-Clause" ], - "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", - "homepage": "https://www.doctrine-project.org/projects/dbal.html", + "description": "Tool to write files safely, to avoid race conditions", "keywords": [ - "abstraction", - "database", - "db2", - "dbal", - "mariadb", - "mssql", - "mysql", - "oci8", - "oracle", - "pdo", - "pgsql", - "postgresql", - "queryobject", - "sasql", - "sql", - "sqlite", - "sqlserver", - "sqlsrv" + "concurrent write", + "file writer", + "race condition", + "safe writer", + "webimpress" ], "support": { - "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/4.4.1" + "issues": "https://github.com/webimpress/safe-writer/issues", + "source": "https://github.com/webimpress/safe-writer/tree/2.2.0" }, "funding": [ { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", - "type": "tidelift" + "url": "https://github.com/michalbundyra", + "type": "github" } ], - "time": "2025-12-04T10:11:03+00:00" + "time": "2021-04-19T16:34:45+00:00" }, { - "name": "doctrine/deprecations", - "version": "1.1.5", + "name": "webmozart/assert", + "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", - "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", - "psr/log": "^1 || ^2 || ^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + "phpunit/phpunit": "^8.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "psr-4": { - "Doctrine\\Deprecations\\": "src" + "Webmozart\\Assert\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2022-06-03T18:03:27+00:00" }, { - "name": "doctrine/event-manager", - "version": "2.0.1", + "name": "zircote/swagger-php", + "version": "4.10.0", "source": { "type": "git", - "url": "https://github.com/doctrine/event-manager.git", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" + "url": "https://github.com/zircote/swagger-php.git", + "reference": "2d983ce67b9eb7e18403ae7bc5e765f8ce7b8d56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/2d983ce67b9eb7e18403ae7bc5e765f8ce7b8d56", + "reference": "2d983ce67b9eb7e18403ae7bc5e765f8ce7b8d56", "shasum": "" }, "require": { - "php": "^8.1" - }, - "conflict": { - "doctrine/common": "<2.9" + "ext-json": "*", + "php": ">=7.2", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "symfony/deprecation-contracts": "^2 || ^3", + "symfony/finder": ">=2.2", + "symfony/yaml": ">=3.3" }, "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.8.8", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "^5.24" + "composer/package-versions-deprecated": "^1.11", + "doctrine/annotations": "^1.7 || ^2.0", + "friendsofphp/php-cs-fixer": "^2.17 || ^3.47.1", + "phpstan/phpstan": "^1.6", + "phpunit/phpunit": ">=8", + "vimeo/psalm": "^4.23" }, + "suggest": { + "doctrine/annotations": "^1.7 || ^2.0" + }, + "bin": [ + "bin/openapi" + ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, "autoload": { "psr-4": { - "Doctrine\\Common\\": "src" + "OpenApi\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], "authors": [ { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" + "name": "Robert Allen", + "email": "zircote@gmail.com" }, { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Bob Fanger", + "email": "bfanger@gmail.com", + "homepage": "https://bfanger.nl" }, { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" + "name": "Martin Rademacher", + "email": "mano@radebatz.net", + "homepage": "https://radebatz.net" } ], - "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", - "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations", + "homepage": "https://github.com/zircote/swagger-php/", "keywords": [ - "event", - "event dispatcher", - "event manager", - "event system", - "events" + "api", + "json", + "rest", + "service discovery" ], "support": { - "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/2.0.1" + "issues": "https://github.com/zircote/swagger-php/issues", + "source": "https://github.com/zircote/swagger-php/tree/4.10.0" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", - "type": "tidelift" - } - ], - "time": "2024-05-22T20:47:39+00:00" - }, + "time": "2024-06-06T22:42:02+00:00" + } + ], + "packages-dev": [ { - "name": "doctrine/migrations", - "version": "3.9.5", + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/doctrine/migrations.git", - "reference": "1b823afbc40f932dae8272574faee53f2755eac5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/1b823afbc40f932dae8272574faee53f2755eac5", - "reference": "1b823afbc40f932dae8272574faee53f2755eac5", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2", - "doctrine/dbal": "^3.6 || ^4", - "doctrine/deprecations": "^0.5.3 || ^1", - "doctrine/event-manager": "^1.2 || ^2.0", - "php": "^8.1", - "psr/log": "^1.1.3 || ^2 || ^3", - "symfony/console": "^5.4 || ^6.0 || ^7.0 || ^8.0", - "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0 || ^8.0", - "symfony/var-exporter": "^6.2 || ^7.0 || ^8.0" + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "4be43904336affa5c2f70744a348312336afd0da" }, - "conflict": { - "doctrine/orm": "<2.12 || >=4" + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", + "reference": "4be43904336affa5c2f70744a348312336afd0da", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" }, "require-dev": { - "doctrine/coding-standard": "^14", - "doctrine/orm": "^2.13 || ^3", - "doctrine/persistence": "^2 || ^3 || ^4", - "doctrine/sql-formatter": "^1.0", - "ext-pdo_sqlite": "*", - "fig/log-test": "^1", - "phpstan/phpstan": "^2", - "phpstan/phpstan-deprecation-rules": "^2", - "phpstan/phpstan-phpunit": "^2", - "phpstan/phpstan-strict-rules": "^2", - "phpstan/phpstan-symfony": "^2", - "phpunit/phpunit": "^10.3 || ^11.0 || ^12.0", - "symfony/cache": "^5.4 || ^6.0 || ^7.0 || ^8.0", - "symfony/process": "^5.4 || ^6.0 || ^7.0 || ^8.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0" + "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" }, - "suggest": { - "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.", - "symfony/yaml": "Allows the use of yaml for migration configuration files." + "type": "composer-plugin", + "extra": { + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" }, - "bin": [ - "bin/doctrine-migrations" - ], - "type": "library", "autoload": { "psr-4": { - "Doctrine\\Migrations\\": "src" + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4889,67 +5120,64 @@ ], "authors": [ { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" }, { - "name": "Michael Simonson", - "email": "contact@mikesimonson.com" + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" } ], - "description": "PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to use and a powerful tool.", - "homepage": "https://www.doctrine-project.org/projects/migrations.html", + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", "keywords": [ - "database", - "dbal", - "migrations" + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" ], "support": { - "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.9.5" + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "source": "https://github.com/PHPCSStandards/composer-installer" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fmigrations", - "type": "tidelift" - } - ], - "time": "2025-11-20T11:15:36+00:00" + "time": "2023-01-05T11:28:13+00:00" }, { "name": "filp/whoops", - "version": "2.18.4", + "version": "2.15.4", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0", + "php": "^5.5.9 || ^7.0 || ^8.0", "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^4.0 || ^5.0" + "mockery/mockery": "^0.9 || ^1.0", + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -4989,7 +5217,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.4" + "source": "https://github.com/filp/whoops/tree/2.15.4" }, "funding": [ { @@ -4997,32 +5225,32 @@ "type": "github" } ], - "time": "2025-08-08T12:00:00+00:00" + "time": "2023-11-03T12:00:00+00:00" }, { "name": "helmich/phpunit-json-assert", - "version": "v3.5.3", + "version": "v3.5.2", "source": { "type": "git", "url": "https://github.com/martin-helmich/phpunit-json-assert.git", - "reference": "82cedf4ee0a7a2e6a619fbbdab9db77c53fe3793" + "reference": "f8958119e9e9ea1339d6b4d6fd2df24536261b50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/martin-helmich/phpunit-json-assert/zipball/82cedf4ee0a7a2e6a619fbbdab9db77c53fe3793", - "reference": "82cedf4ee0a7a2e6a619fbbdab9db77c53fe3793", + "url": "https://api.github.com/repos/martin-helmich/phpunit-json-assert/zipball/f8958119e9e9ea1339d6b4d6fd2df24536261b50", + "reference": "f8958119e9e9ea1339d6b4d6fd2df24536261b50", "shasum": "" }, "require": { "justinrainbow/json-schema": "^5.0", - "php": "^8.1", + "php": "^8.0", "softcreatr/jsonpath": "^0.8" }, "conflict": { - "phpunit/phpunit": "<8.0 || >= 13.0" + "phpunit/phpunit": "<8.0 || >= 12.0" }, "require-dev": { - "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0" + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0 || ^11.0" }, "type": "library", "autoload": { @@ -5043,67 +5271,7 @@ "description": "PHPUnit assertions for JSON documents", "support": { "issues": "https://github.com/martin-helmich/phpunit-json-assert/issues", - "source": "https://github.com/martin-helmich/phpunit-json-assert/tree/v3.5.3" - }, - "funding": [ - { - "url": "https://donate.helmich.me", - "type": "custom" - }, - { - "url": "https://github.com/martin-helmich", - "type": "github" - } - ], - "time": "2025-05-19T22:04:17+00:00" - }, - { - "name": "helmich/phpunit-psr7-assert", - "version": "v4.4.1", - "source": { - "type": "git", - "url": "https://github.com/martin-helmich/phpunit-psr7-assert.git", - "reference": "f35fa69e07cc16977b52805d3abd873cc16747fd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/martin-helmich/phpunit-psr7-assert/zipball/f35fa69e07cc16977b52805d3abd873cc16747fd", - "reference": "f35fa69e07cc16977b52805d3abd873cc16747fd", - "shasum": "" - }, - "require": { - "helmich/phpunit-json-assert": "^3.4", - "php": "^8.0", - "psr/http-message": "^1.1 || ^2.0" - }, - "conflict": { - "phpunit/phpunit": "<8.0 || >= 11.0" - }, - "require-dev": { - "guzzlehttp/psr7": "^2.4", - "mockery/mockery": "^1.4.1", - "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Helmich\\Psr7Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Martin Helmich", - "email": "m.helmich@mittwald.de" - } - ], - "description": "PHPUnit assertions for testing PSR7-compliant applications", - "support": { - "issues": "https://github.com/martin-helmich/phpunit-psr7-assert/issues", - "source": "https://github.com/martin-helmich/phpunit-psr7-assert/tree/v4.4.1" + "source": "https://github.com/martin-helmich/phpunit-json-assert/tree/v3.5.2" }, "funding": [ { @@ -5115,24 +5283,24 @@ "type": "github" } ], - "time": "2023-07-26T19:04:29+00:00" + "time": "2024-05-20T11:18:46+00:00" }, { "name": "justinrainbow/json-schema", - "version": "5.3.1", + "version": "v5.2.13", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "b5a44b6391a3bbb75c9f2b73e1ef03d6045e1e20" + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/b5a44b6391a3bbb75c9f2b73e1ef03d6045e1e20", - "reference": "b5a44b6391a3bbb75c9f2b73e1ef03d6045e1e20", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=5.3.3" }, "require-dev": { "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", @@ -5143,6 +5311,11 @@ "bin/validate-json" ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, "autoload": { "psr-4": { "JsonSchema\\": "src/JsonSchema/" @@ -5178,22 +5351,22 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/5.3.1" + "source": "https://github.com/jsonrainbow/json-schema/tree/v5.2.13" }, - "time": "2025-12-12T08:56:22+00:00" + "time": "2023-09-26T02:20:38+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.13.4", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -5232,7 +5405,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -5240,40 +5413,76 @@ "type": "tidelift" } ], - "time": "2025-08-01T08:46:24+00:00" + "time": "2024-06-12T14:39:25+00:00" + }, + { + "name": "n98/junit-xml", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/cmuench/junit-xml.git", + "reference": "0017dd92ac8cb619f02e32f4cffd768cfe327c73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cmuench/junit-xml/zipball/0017dd92ac8cb619f02e32f4cffd768cfe327c73", + "reference": "0017dd92ac8cb619f02e32f4cffd768cfe327c73", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "N98\\JUnitXml\\": "src/N98/JUnitXml" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Münch", + "email": "c.muench@netz98.de" + } + ], + "description": "JUnit XML Document generation library", + "support": { + "issues": "https://github.com/cmuench/junit-xml/issues", + "source": "https://github.com/cmuench/junit-xml/tree/1.1.0" + }, + "time": "2020-12-25T09:08:58+00:00" }, { "name": "overtrue/phplint", - "version": "9.6.3", + "version": "5.5.0", "source": { "type": "git", "url": "https://github.com/overtrue/phplint.git", - "reference": "b0ec1d07b37a37e7fc872c8bdbddacadcecbe047" + "reference": "6698f0c68abc1e7586fb3e1c8dd7246604fd06e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/overtrue/phplint/zipball/b0ec1d07b37a37e7fc872c8bdbddacadcecbe047", - "reference": "b0ec1d07b37a37e7fc872c8bdbddacadcecbe047", + "url": "https://api.github.com/repos/overtrue/phplint/zipball/6698f0c68abc1e7586fb3e1c8dd7246604fd06e6", + "reference": "6698f0c68abc1e7586fb3e1c8dd7246604fd06e6", "shasum": "" }, "require": { - "composer-runtime-api": "^2.0", - "ext-dom": "*", "ext-json": "*", - "ext-mbstring": "*", + "n98/junit-xml": "1.1.0", "php": "^8.1", - "symfony/cache": "^6.4 || ^7.0", - "symfony/console": "^6.4 || ^7.0", - "symfony/event-dispatcher": "^6.4 || ^7.0", - "symfony/finder": "^6.4 || ^7.0", - "symfony/options-resolver": "^6.4 || ^7.0", - "symfony/process": "^6.4 || ^7.0", - "symfony/yaml": "^6.4 || ^7.0" + "symfony/console": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/process": "^5.4 || ^6.0", + "symfony/yaml": "^5.4 || ^6.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.4", - "brainmaestro/composer-git-hooks": "^3.0.0", - "jetbrains/phpstorm-stubs": "^2024.1", + "brainmaestro/composer-git-hooks": "^2.8.5", + "friendsofphp/php-cs-fixer": "^3.4.0", + "jetbrains/phpstorm-stubs": "^2021.3 || ^2022.0", "php-parallel-lint/php-console-highlighter": "^1.0" }, "bin": [ @@ -5283,17 +5492,8 @@ "extra": { "hooks": { "pre-commit": [ - "composer style:fix", - "composer code:check" + "composer fix-style" ] - }, - "bamarni-bin": { - "bin-links": true, - "forward-command": true, - "target-directory": "vendor-bin" - }, - "branch-alias": { - "dev-main": "9.6.x-dev" } }, "autoload": { @@ -5309,10 +5509,6 @@ { "name": "overtrue", "email": "anzhengchao@gmail.com" - }, - { - "name": "Laurent Laville", - "homepage": "https://github.com/llaville" } ], "description": "`phplint` is a tool that can speed up linting of php files by running several lint processes at once.", @@ -5320,12 +5516,11 @@ "check", "lint", "phplint", - "static analysis", "syntax" ], "support": { "issues": "https://github.com/overtrue/phplint/issues", - "source": "https://github.com/overtrue/phplint/tree/9.6.3" + "source": "https://github.com/overtrue/phplint/tree/5.5.0" }, "funding": [ { @@ -5333,7 +5528,7 @@ "type": "github" } ], - "time": "2025-11-27T13:49:59+00:00" + "time": "2022-12-28T13:45:44+00:00" }, { "name": "phar-io/manifest", @@ -5453,17 +5648,69 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.29.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" + }, + "time": "2024-05-31T08:52:43+00:00" + }, { "name": "phpstan/phpstan", - "version": "2.1.33", + "version": "1.11.6", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "6ac78f1165346c83b4a753f7e4186d969c6ad0ee" + }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f", - "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6ac78f1165346c83b4a753f7e4186d969c6ad0ee", + "reference": "6ac78f1165346c83b4a753f7e4186d969c6ad0ee", "shasum": "" }, "require": { - "php": "^7.4|^8.0" + "php": "^7.2|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -5504,30 +5751,30 @@ "type": "github" } ], - "time": "2025-12-05T10:24:31+00:00" + "time": "2024-07-01T15:33:06+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", - "version": "2.0.3", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-deprecation-rules.git", - "reference": "468e02c9176891cc901143da118f09dc9505fc2f" + "reference": "fa8cce7720fa782899a0aa97b6a41225d1bb7b26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/468e02c9176891cc901143da118f09dc9505fc2f", - "reference": "468e02c9176891cc901143da118f09dc9505fc2f", + "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/fa8cce7720fa782899a0aa97b6a41225d1bb7b26", + "reference": "fa8cce7720fa782899a0aa97b6a41225d1bb7b26", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.15" + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.11" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^9.6" + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5" }, "type": "phpstan-extension", "extra": { @@ -5549,38 +5796,38 @@ "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", "support": { "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", - "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.3" + "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.2.0" }, - "time": "2025-05-14T10:56:57+00:00" + "time": "2024-04-20T06:39:48+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.16", + "version": "10.1.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.19.1 || ^5.1.0", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-text-template": "^3.0.1", - "sebastian/code-unit-reverse-lookup": "^3.0.0", - "sebastian/complexity": "^3.2.0", - "sebastian/environment": "^6.1.0", - "sebastian/lines-of-code": "^2.0.2", - "sebastian/version": "^4.0.1", - "theseer/tokenizer": "^1.2.3" + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { "phpunit/phpunit": "^10.1" @@ -5592,7 +5839,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1.x-dev" + "dev-main": "10.1-dev" } }, "autoload": { @@ -5621,7 +5868,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15" }, "funding": [ { @@ -5629,7 +5876,7 @@ "type": "github" } ], - "time": "2024-08-22T04:31:57+00:00" + "time": "2024-06-29T08:25:15+00:00" }, { "name": "phpunit/php-file-iterator", @@ -5876,16 +6123,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.60", + "version": "10.5.24", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f2e26f52f80ef77832e359205f216eeac00e320c" + "reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f2e26f52f80ef77832e359205f216eeac00e320c", - "reference": "f2e26f52f80ef77832e359205f216eeac00e320c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f124e3e3e561006047b532fd0431bf5bb6b9015", + "reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015", "shasum": "" }, "require": { @@ -5895,26 +6142,26 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.4", - "phar-io/manifest": "^2.0.4", - "phar-io/version": "^3.2.1", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.16", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-invoker": "^4.0.0", - "phpunit/php-text-template": "^3.0.1", - "phpunit/php-timer": "^6.0.0", - "sebastian/cli-parser": "^2.0.1", - "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.4", - "sebastian/diff": "^5.1.1", - "sebastian/environment": "^6.1.0", - "sebastian/exporter": "^5.1.4", - "sebastian/global-state": "^6.0.2", - "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.1", - "sebastian/type": "^4.0.0", - "sebastian/version": "^4.0.1" + "phpunit/php-code-coverage": "^10.1.5", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-invoker": "^4.0", + "phpunit/php-text-template": "^3.0", + "phpunit/php-timer": "^6.0", + "sebastian/cli-parser": "^2.0", + "sebastian/code-unit": "^2.0", + "sebastian/comparator": "^5.0", + "sebastian/diff": "^5.0", + "sebastian/environment": "^6.0", + "sebastian/exporter": "^5.1", + "sebastian/global-state": "^6.0.1", + "sebastian/object-enumerator": "^5.0", + "sebastian/recursion-context": "^5.0", + "sebastian/type": "^4.0", + "sebastian/version": "^4.0" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -5957,7 +6204,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.60" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.24" }, "funding": [ { @@ -5969,119 +6216,49 @@ "type": "github" }, { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2025-12-06T07:50:42+00:00" - }, - { - "name": "psr/cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } ], - "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" - }, - "time": "2021-02-03T23:26:27+00:00" + "time": "2024-06-20T13:09:54+00:00" }, { "name": "roave/security-advisories", - "version": "dev-master", + "version": "dev-latest", "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "95fda149b750941a5d7bd292e712107ca3227a04" + "reference": "27714b56f04815b654c3805502ab77207505ac19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/95fda149b750941a5d7bd292e712107ca3227a04", - "reference": "95fda149b750941a5d7bd292e712107ca3227a04", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/27714b56f04815b654c3805502ab77207505ac19", + "reference": "27714b56f04815b654c3805502ab77207505ac19", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", - "adaptcms/adaptcms": "<=1.3", - "admidio/admidio": "<=4.3.16", - "adodb/adodb-php": "<=5.22.9", + "admidio/admidio": "<4.2.13", + "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", "aheinze/cockpit": "<2.2", - "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", - "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", "aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7", - "aimeos/ai-cms-grapesjs": ">=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.9|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.10.8|>=2025.04.1,<2025.10.2", - "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9|==2024.04.1", "aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7", - "aimeos/aimeos-laravel": "==2021.10", "aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5", "airesvsg/acf-to-rest-api": "<=3.1", "akaunting/akaunting": "<2.1.13", "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", - "alextselegidis/easyappointments": "<=1.5.2", - "alexusmai/laravel-file-manager": "<=3.3.1", - "algolia/algoliasearch-magento-2": "<=3.16.1|>=3.17.0.0-beta1,<=3.17.1", - "alt-design/alt-redirect": "<1.6.4", - "altcha-org/altcha": "<1.3.1", + "alextselegidis/easyappointments": "<1.5", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", "amazing/media2click": ">=1,<1.3.3", - "ameos/ameos_tarteaucitron": "<1.2.23", "amphp/artax": "<1.0.6|>=2,<2.0.6", "amphp/http": "<=1.7.2|>=2,<=2.1", "amphp/http-client": ">=4,<4.4", "anchorcms/anchor-cms": "<=0.12.7", "andreapollastri/cipi": "<=3.1.15", "andrewhaine/silverstripe-form-capture": ">=0.2,<=0.2.3|>=1,<1.0.2|>=2,<2.2.5", - "aoe/restler": "<1.7.1", "apache-solr-for-typo3/solr": "<2.8.3", "apereo/phpcas": "<1.6", - "api-platform/core": "<3.4.17|>=4,<4.0.22|>=4.1,<4.1.5", - "api-platform/graphql": "<3.4.17|>=4,<4.0.22|>=4.1,<4.1.5", + "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6|>=2.6,<2.7.10|>=3,<3.0.12|>=3.1,<3.1.3", "appwrite/server-ce": "<=1.2.1", "arc/web": "<3", "area17/twill": "<1.2.5|>=2,<2.5.3", @@ -6089,38 +6266,28 @@ "asymmetricrypt/asymmetricrypt": "<9.9.99", "athlon1600/php-proxy": "<=5.1", "athlon1600/php-proxy-app": "<=3", - "athlon1600/youtube-downloader": "<=4", "austintoddj/canvas": "<=3.4.2", - "auth0/auth0-php": ">=3.3,<8.18", - "auth0/login": "<7.20", - "auth0/symfony": "<=5.5", - "auth0/wordpress": "<=5.4", - "automad/automad": "<2.0.0.0-alpha5", + "automad/automad": "<=1.10.9", "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", - "aws/aws-sdk-php": "<3.368", - "azuracast/azuracast": "<=0.23.1", - "b13/seo_basics": "<0.8.2", - "backdrop/backdrop": "<=1.32", + "aws/aws-sdk-php": "<3.288.1", + "azuracast/azuracast": "<0.18.3", + "backdrop/backdrop": "<1.24.2", "backpack/crud": "<3.4.9", - "backpack/filemanager": "<2.0.2|>=3,<3.0.9", - "bacula-web/bacula-web": "<9.7.1", - "badaso/core": "<=2.9.11", - "bagisto/bagisto": "<2.3.10", + "bacula-web/bacula-web": "<8.0.0.0-RC2-dev", + "badaso/core": "<2.7", + "bagisto/bagisto": "<2.1", "barrelstrength/sprout-base-email": "<1.2.7", "barrelstrength/sprout-forms": "<3.9", - "barryvdh/laravel-translation-manager": "<0.6.8", + "barryvdh/laravel-translation-manager": "<0.6.2", "barzahlen/barzahlen-php": "<2.0.1", - "baserproject/basercms": "<=5.1.1", + "baserproject/basercms": "<5.0.9", "bassjobsen/bootstrap-3-typeahead": ">4.0.2", "bbpress/bbpress": "<2.6.5", - "bcit-ci/codeigniter": "<3.1.3", "bcosca/fatfree": "<3.7.2", "bedita/bedita": "<4", - "bednee/cooluri": "<1.0.30", "bigfork/silverstripe-form-capture": ">=3,<3.1.1", - "billz/raspap-webgui": "<3.3.6", - "binarytorch/larecipe": "<2.8.1", + "billz/raspap-webgui": "<2.9.5", "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", "blueimp/jquery-file-upload": "==6.4.4", "bmarshall511/wordpress_zero_spam": "<5.2.13", @@ -6135,10 +6302,8 @@ "brotkrueml/typo3-matomo-integration": "<1.3.2", "buddypress/buddypress": "<7.2.1", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", - "bvbmedia/multishop": "<2.0.39", "bytefury/crater": "<6.0.2", "cachethq/cachet": "<2.5.1", - "cadmium-org/cadmium-cms": "<=0.4.9", "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", "cakephp/database": ">=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", "cardgate/magento2": "<2.0.33", @@ -6147,66 +6312,47 @@ "cart2quote/module-quotation-encoded": ">=4.1.6,<=4.4.5|>=5,<5.4.4", "cartalyst/sentry": "<=2.1.6", "catfan/medoo": "<1.7.5", - "causal/oidc": "<4", + "causal/oidc": "<2.1", "cecil/cecil": "<7.47.1", "centreon/centreon": "<22.10.15", "cesnet/simplesamlphp-module-proxystatistics": "<3.1", "chriskacerguis/codeigniter-restserver": "<=2.7.1", - "chrome-php/chrome": "<1.14", "civicrm/civicrm-core": ">=4.2,<4.2.9|>=4.3,<4.3.3", - "ckeditor/ckeditor": "<4.25", - "clickstorm/cs-seo": ">=6,<6.8|>=7,<7.5|>=8,<8.4|>=9,<9.3", - "co-stack/fal_sftp": "<0.2.6", - "cockpit-hq/cockpit": "<2.11.4", - "code16/sharp": "<9.11.1", + "ckeditor/ckeditor": "<4.24", + "cockpit-hq/cockpit": "<2.7|==2.7", "codeception/codeception": "<3.1.3|>=4,<4.1.22", - "codeigniter/framework": "<3.1.10", - "codeigniter4/framework": "<4.6.2", + "codeigniter/framework": "<3.1.9", + "codeigniter4/framework": "<4.4.7", "codeigniter4/shield": "<1.0.0.0-beta8", "codiad/codiad": "<=2.8.4", - "codingms/additional-tca": ">=1.7,<1.15.17|>=1.16,<1.16.9", - "codingms/modules": "<4.3.11|>=5,<5.7.4|>=6,<6.4.2|>=7,<7.5.5", - "commerceteam/commerce": ">=0.9.6,<0.9.9", - "components/jquery": ">=1.0.3,<3.5", - "composer/composer": "<1.10.27|>=2,<2.2.26|>=2.3,<2.9.3", - "concrete5/concrete5": "<9.4.3", + "composer/composer": "<1.10.27|>=2,<2.2.24|>=2.3,<2.7.7", + "concrete5/concrete5": "<9.2.8", "concrete5/core": "<8.5.8|>=9,<9.1", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", - "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.13.56|>=5,<5.3.38|>=5.4.0.0-RC1-dev,<5.6.1", + "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.9.40|>=4.10,<4.11.7|>=4.13,<4.13.21|>=5.1,<5.1.4", "contao/core": "<3.5.39", - "contao/core-bundle": "<4.13.57|>=5,<5.3.42|>=5.4,<5.6.5", + "contao/core-bundle": "<4.13.40|>=5,<5.3.4", "contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8", "contao/managed-edition": "<=1.5", - "coreshop/core-shop": "<=4.1.7", "corveda/phpsandbox": "<1.3.5", "cosenary/instagram": "<=2.3", - "couleurcitron/tarteaucitron-wp": "<0.3", - "craftcms/cms": "<=4.16.16|>=5,<=5.8.20", - "croogo/croogo": "<=4.0.7", + "craftcms/cms": "<4.6.2", + "croogo/croogo": "<4", "cuyz/valinor": "<0.12", - "czim/file-handling": "<1.5|>=2,<2.3", "czproject/git-php": "<4.0.3", - "damienharper/auditor-bundle": "<5.2.6", "dapphp/securimage": "<3.6.6", "darylldoyle/safe-svg": "<1.9.10", "datadog/dd-trace": ">=0.30,<0.30.2", - "datahihi1/tiny-env": "<1.0.3|>=1.0.9,<1.0.11", "datatables/datatables": "<1.10.10", "david-garcia/phpwhois": "<=4.3.1", "dbrisinajumi/d2files": "<1", - "dcat/laravel-admin": "<=2.1.3|==2.2.0.0-beta|==2.2.2.0-beta", + "dcat/laravel-admin": "<=2.1.3.0-beta", "derhansen/fe_change_pwd": "<2.0.5|>=3,<3.0.3", "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1|>=7,<7.4", "desperado/xml-bundle": "<=0.1.7", - "dev-lancer/minecraft-motd-parser": "<=1.0.5", - "devcode-it/openstamanager": "<=2.9.4", "devgroup/dotplant": "<2020.09.14-dev", - "digimix/wp-svg-upload": "<=1", "directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2", - "dl/yag": "<3.0.1", - "dmk/webkitpdf": "<1.1.4", - "dnadesign/silverstripe-elemental": "<5.3.12", "doctrine/annotations": "<1.2.7", "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", "doctrine/common": "<2.4.3|>=2.5,<2.5.1", @@ -6216,59 +6362,24 @@ "doctrine/mongodb-odm": "<1.0.2", "doctrine/mongodb-odm-bundle": "<3.0.1", "doctrine/orm": ">=1,<1.2.4|>=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<21.0.3", + "dolibarr/dolibarr": "<19.0.2", "dompdf/dompdf": "<2.0.4", "doublethreedigital/guest-entries": "<3.1.2", - "drupal-pattern-lab/unified-twig-extensions": "<=0.1", - "drupal/access_code": "<2.0.5", - "drupal/acquia_dam": "<1.1.5", - "drupal/admin_audit_trail": "<1.0.5", - "drupal/ai": "<1.0.5", - "drupal/alogin": "<2.0.6", - "drupal/cache_utility": "<1.2.1", - "drupal/civictheme": "<1.12", - "drupal/commerce_alphabank_redirect": "<1.0.3", - "drupal/commerce_eurobank_redirect": "<2.1.1", - "drupal/config_split": "<1.10|>=2,<2.0.2", - "drupal/core": ">=6,<6.38|>=7,<7.103|>=8,<10.4.9|>=10.5,<10.5.6|>=11,<11.1.9|>=11.2,<11.2.8", - "drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", - "drupal/currency": "<3.5", - "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", - "drupal/email_tfa": "<2.0.6", - "drupal/formatter_suite": "<2.1", - "drupal/gdpr": "<3.0.1|>=3.1,<3.1.2", - "drupal/google_tag": "<1.8|>=2,<2.0.8", - "drupal/ignition": "<1.0.4", - "drupal/json_field": "<1.5", - "drupal/lightgallery": "<1.6", - "drupal/link_field_display_mode_formatter": "<1.6", - "drupal/matomo": "<1.24", - "drupal/oauth2_client": "<4.1.3", - "drupal/oauth2_server": "<2.1", - "drupal/obfuscate": "<2.0.1", - "drupal/plausible_tracking": "<1.0.2", - "drupal/quick_node_block": "<2", - "drupal/rapidoc_elements_field_formatter": "<1.0.1", - "drupal/reverse_proxy_header": "<1.1.2", - "drupal/simple_multistep": "<2", - "drupal/simple_oauth": ">=6,<6.0.7", - "drupal/spamspan": "<3.2.1", - "drupal/tfa": "<1.10", - "drupal/umami_analytics": "<1.0.1", + "drupal/core": ">=6,<6.38|>=7,<7.96|>=8,<10.1.8|>=10.2,<10.2.2", + "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", "duncanmcclean/guest-entries": "<3.1.2", "dweeves/magmi": "<=0.7.24", "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", "ecodev/newsletter": "<=4", "ectouch/ectouch": "<=2.7.2", - "egroupware/egroupware": "<23.1.20240624", + "egroupware/egroupware": "<16.1.20170922", "elefant/cms": "<2.0.7", "elgg/elgg": "<3.3.24|>=4,<4.0.5", "elijaa/phpmemcacheadmin": "<=1.3", - "elmsln/haxcms": "<11.0.14", "encore/laravel-admin": "<=1.8.19", "endroid/qr-code-bundle": "<3.4.2", "enhavo/enhavo-app": "<=0.13.1", - "enshrined/svg-sanitize": "<0.22", + "enshrined/svg-sanitize": "<0.15", "erusev/parsedown": "<1.7.2", "ether/logs": "<3.0.4", "evolutioncms/evolution": "<=3.2.3", @@ -6279,47 +6390,40 @@ "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1-dev", "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1-dev|>=5.4,<5.4.11.1-dev|>=2017.12,<2017.12.0.1-dev", "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", - "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.39|>=3.3,<3.3.39", - "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1|>=5.3.0.0-beta1,<5.3.5", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.26", + "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", "ezsystems/ezplatform-graphql": ">=1.0.0.0-RC1-dev,<1.0.13|>=2.0.0.0-beta1,<2.3.12", - "ezsystems/ezplatform-http-cache": "<2.3.16", "ezsystems/ezplatform-kernel": "<1.2.5.1-dev|>=1.3,<1.3.35", "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", - "ezsystems/ezplatform-richtext": ">=2.3,<2.3.26|>=3.3,<3.3.40", + "ezsystems/ezplatform-richtext": ">=2.3,<2.3.7.1-dev", "ezsystems/ezplatform-solr-search-engine": ">=1.7,<1.7.12|>=2,<2.0.2|>=3.3,<3.3.15", "ezsystems/ezplatform-user": ">=1,<1.0.1", "ezsystems/ezpublish-kernel": "<6.13.8.2-dev|>=7,<7.5.31", "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.03.5.1", "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", "ezsystems/repository-forms": ">=2.3,<2.3.2.1-dev|>=2.5,<2.5.15", - "ezyang/htmlpurifier": "<=4.2", + "ezyang/htmlpurifier": "<4.1.1", "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", - "facturascripts/facturascripts": "<=2025.4|==2025.11|==2025.41|==2025.43", + "facturascripts/facturascripts": "<=2022.08", "fastly/magento2": "<1.2.26", "feehi/cms": "<=2.1.1", "feehi/feehicms": "<=2.1.1", "fenom/fenom": "<=2.12.1", - "filament/actions": ">=3.2,<3.2.123", - "filament/filament": ">=4,<4.3.1", - "filament/infolists": ">=3,<3.2.115", - "filament/tables": ">=3,<3.2.115", "filegator/filegator": "<7.8", "filp/whoops": "<2.1.13", "fineuploader/php-traditional-server": "<=1.2.2", "firebase/php-jwt": "<6", - "fisharebest/webtrees": "<=2.1.18", "fixpunkt/fp-masterquiz": "<2.2.1|>=3,<3.5.2", - "fixpunkt/fp-newsletter": "<1.1.1|>=1.2,<2.1.2|>=2.2,<3.2.6", - "flarum/core": "<1.8.10", + "fixpunkt/fp-newsletter": "<1.1.1|>=2,<2.1.2|>=2.2,<3.2.6", + "flarum/core": "<1.8.5", "flarum/flarum": "<0.1.0.0-beta8", - "flarum/framework": "<1.8.10", + "flarum/framework": "<1.8.5", "flarum/mentions": "<1.6.3", "flarum/sticky": ">=0.1.0.0-beta14,<=0.1.0.0-beta15", "flarum/tags": "<=0.1.0.0-beta13", "floriangaerber/magnesium": "<0.3.1", "fluidtypo3/vhs": "<5.1.1", "fof/byobu": ">=0.3.0.0-beta2,<1.1.7", - "fof/pretty-mail": "<=1.1.2", "fof/upload": "<1.2.3", "foodcoopshop/foodcoopshop": ">=3.2,<3.6.1", "fooman/tcpdf": "<6.2.22", @@ -6331,41 +6435,35 @@ "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", "friendsofsymfony/user-bundle": ">=1,<1.3.5", "friendsofsymfony1/swiftmailer": ">=4,<5.4.13|>=6,<6.2.5", - "friendsofsymfony1/symfony1": ">=1.1,<1.5.19", + "friendsofsymfony1/symfony1": ">=1.1,<1.15.19", "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", "friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6", - "froala/wysiwyg-editor": "<=4.3", - "froxlor/froxlor": "<=2.2.5", + "froala/wysiwyg-editor": "<3.2.7|>=4.0.1,<=4.1.3", + "froxlor/froxlor": "<2.1.9", "frozennode/administrator": "<=5.0.12", "fuel/core": "<1.8.1", - "funadmin/funadmin": "<=5.0.2", + "funadmin/funadmin": "<=3.2|>=3.3.2,<=3.3.3", "gaoming13/wechat-php-sdk": "<=1.10.2", "genix/cms": "<=1.1.11", - "georgringer/news": "<1.3.3", - "geshi/geshi": "<=1.0.9.1", - "getformwork/formwork": "<2.2", - "getgrav/grav": "<1.11.0.0-beta1", - "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1|>=5,<=5.2.1", - "getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", + "getformwork/formwork": "<1.13.1|==2.0.0.0-beta1", + "getgrav/grav": "<1.7.46", + "getkirby/cms": "<4.1.1", + "getkirby/kirby": "<=2.5.12", "getkirby/panel": "<2.5.14", "getkirby/starterkit": "<=3.7.0.2", "gilacms/gila": "<=1.15.4", "gleez/cms": "<=1.3|==2", "globalpayments/php-sdk": "<2", - "goalgorilla/open_social": "<12.3.11|>=12.4,<12.4.10|>=13.0.0.0-alpha1,<13.0.0.0-alpha11", "gogentooss/samlbase": "<1.2.7", - "google/protobuf": "<3.4", + "google/protobuf": "<3.15", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", - "gp247/core": "<1.1.24", "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", "grumpydictator/firefly-iii": "<6.1.17", "gugoan/economizzer": "<=0.9.0.0-beta1", "guzzlehttp/guzzle": "<6.5.8|>=7,<7.4.5", - "guzzlehttp/oauth-subscriber": "<0.8.1", "guzzlehttp/psr7": "<1.9.1|>=2,<2.4.5", "haffner/jh_captcha": "<=2.1.3|>=3,<=3.0.2", - "handcraftedinthealps/goodby-csv": "<1.4.3", "harvesthq/chosen": "<1.8.7", "helloxz/imgurl": "<=2.31", "hhxsv5/laravel-s": "<3.7.36", @@ -6375,15 +6473,12 @@ "hov/jobfair": "<1.0.13|>=2,<2.0.2", "httpsoft/http-message": "<1.0.12", "hyn/multi-tenant": ">=5.6,<5.7.2", - "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.25|>=5,<5.0.3", - "ibexa/admin-ui-assets": ">=4.6.0.0-alpha1,<4.6.21", + "ibexa/admin-ui": ">=4.2,<4.2.3", "ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3|>=4.5,<4.5.6|>=4.6,<4.6.2", - "ibexa/fieldtype-richtext": ">=4.6,<4.6.25|>=5,<5.0.3", "ibexa/graphql": ">=2.5,<2.5.31|>=3.3,<3.3.28|>=4.2,<4.2.3", - "ibexa/http-cache": ">=4.6,<4.6.14", - "ibexa/post-install": "<1.0.16|>=4.6,<4.6.14", + "ibexa/post-install": "<=1.0.4", "ibexa/solr": ">=4.5,<4.5.4", - "ibexa/user": ">=4,<4.4.3|>=5,<5.0.4", + "ibexa/user": ">=4,<4.4.3", "icecoder/icecoder": "<=8.1", "idno/known": "<=1.3.1", "ilicmiljan/secure-props": ">=1.2,<1.2.2", @@ -6394,157 +6489,117 @@ "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", "imdbphp/imdbphp": "<=5.1.1", "impresscms/impresscms": "<=1.4.5", - "impresspages/impresspages": "<1.0.13", - "in2code/femanager": "<6.4.2|>=7,<7.5.3|>=8,<8.3.1", + "impresspages/impresspages": "<=1.0.12", + "in2code/femanager": "<5.5.3|>=6,<6.3.4|>=7,<7.2.3", "in2code/ipandlanguageredirect": "<5.1.2", "in2code/lux": "<17.6.1|>=18,<24.0.2", - "in2code/powermail": "<7.5.1|>=8,<8.5.1|>=9,<10.9.1|>=11,<12.5.3|==13", "innologi/typo3-appointments": "<2.0.6", "intelliants/subrion": "<4.2.2", "inter-mediator/inter-mediator": "==5.5", - "ipl/web": "<0.10.1", - "islandora/crayfish": "<4.1", "islandora/islandora": ">=2,<2.4.1", "ivankristianto/phpwhois": "<=4.3", "jackalope/jackalope-doctrine-dbal": "<1.7.4", - "jambagecom/div2007": "<0.10.2", "james-heinrich/getid3": "<1.9.21", - "james-heinrich/phpthumb": "<=1.7.23", + "james-heinrich/phpthumb": "<1.7.12", "jasig/phpcas": "<1.3.3", - "jbartels/wec-map": "<3.0.3", "jcbrand/converse.js": "<3.3.3", - "joelbutcher/socialstream": "<5.6|>=6,<6.2", - "johnbillion/wp-crontrol": "<1.16.2|>=1.17,<1.19.2", + "johnbillion/wp-crontrol": "<1.16.2", "joomla/application": "<1.0.13", "joomla/archive": "<1.1.12|>=2,<2.0.1", - "joomla/database": ">=1,<2.2|>=3,<3.4", "joomla/filesystem": "<1.6.2|>=2,<2.0.1", - "joomla/filter": "<2.0.6|>=3,<3.0.5|==4", + "joomla/filter": "<1.4.4|>=2,<2.0.1", "joomla/framework": "<1.5.7|>=2.5.4,<=3.8.12", "joomla/input": ">=2,<2.0.2", - "joomla/joomla-cms": "<3.9.12|>=4,<4.4.13|>=5,<5.2.6", - "joomla/joomla-platform": "<1.5.4", + "joomla/joomla-cms": ">=2.5,<3.9.12", "joomla/session": "<1.3.1", "joyqi/hyper-down": "<=2.4.27", "jsdecena/laracom": "<2.0.9", "jsmitty12/phpwhois": "<5.1", - "juzaweb/cms": "<=3.4.2", + "juzaweb/cms": "<=3.4", "jweiland/events2": "<8.3.8|>=9,<9.0.6", - "jweiland/kk-downloader": "<1.2.2", "kazist/phpwhois": "<=4.2.6", "kelvinmo/simplexrd": "<3.1.1", "kevinpapst/kimai2": "<1.16.7", "khodakhah/nodcms": "<=3", - "kimai/kimai": "<=2.20.1", + "kimai/kimai": "<2.16", "kitodo/presentation": "<3.2.3|>=3.3,<3.3.4", "klaviyo/magento2-extension": ">=1,<3", "knplabs/knp-snappy": "<=1.4.2", "kohana/core": "<3.3.3", - "koillection/koillection": "<1.6.12", - "krayin/laravel-crm": "<=1.3", + "krayin/laravel-crm": "<1.2.2", "kreait/firebase-php": ">=3.2,<3.8.1", "kumbiaphp/kumbiapp": "<=1.1.1", "la-haute-societe/tcpdf": "<6.2.22", "laminas/laminas-diactoros": "<2.18.1|==2.19|==2.20|==2.21|==2.22|==2.23|>=2.24,<2.24.2|>=2.25,<2.25.2", "laminas/laminas-form": "<2.17.1|>=3,<3.0.2|>=3.1,<3.1.1", "laminas/laminas-http": "<2.14.2", - "lara-zeus/artemis": ">=1,<=1.0.6", - "lara-zeus/dynamic-dashboard": ">=3,<=3.0.1", "laravel/fortify": "<1.11.1", - "laravel/framework": "<10.48.29|>=11,<11.44.1|>=12,<12.1.1", + "laravel/framework": "<6.20.44|>=7,<7.30.6|>=8,<8.75", "laravel/laravel": ">=5.4,<5.4.22", - "laravel/pulse": "<1.3.1", - "laravel/reverb": "<1.4", "laravel/socialite": ">=1,<2.0.10", "latte/latte": "<2.10.8", "lavalite/cms": "<=9|==10.1", - "lavitto/typo3-form-to-database": "<2.2.5|>=3,<3.2.2|>=4,<4.2.3|>=5,<5.0.2", "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", - "league/commonmark": "<2.7", + "league/commonmark": "<0.18.3", "league/flysystem": "<1.1.4|>=2,<2.1.1", "league/oauth2-server": ">=8.3.2,<8.4.2|>=8.5,<8.5.3", - "leantime/leantime": "<3.3", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", "libreform/libreform": ">=2,<=2.0.8", - "librenms/librenms": "<25.12", + "librenms/librenms": "<2017.08.18", "liftkit/database": "<2.13.2", "lightsaml/lightsaml": "<1.3.5", - "limesurvey/limesurvey": "<6.5.12", + "limesurvey/limesurvey": "<3.27.19", "livehelperchat/livehelperchat": "<=3.91", - "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.6.4", - "livewire/volt": "<1.7", + "livewire/livewire": ">2.2.4,<2.2.6|>=3.3.5,<3.4.9", "lms/routes": "<2.1.1", "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", - "lomkit/laravel-rest-api": "<2.13", - "luracast/restler": "<3.1", "luyadev/yii-helpers": "<1.2.1", - "macropay-solutions/laravel-crud-wizard-free": "<3.4.17", - "maestroerror/php-heic-to-jpg": "<1.0.5", - "magento/community-edition": "<2.4.6.0-patch13|>=2.4.7.0-beta1,<2.4.7.0-patch8|>=2.4.8.0-beta1,<2.4.8.0-patch3|>=2.4.9.0-alpha1,<2.4.9.0-alpha3|==2.4.9", + "magento/community-edition": "<2.4.5|==2.4.5|>=2.4.5.0-patch1,<2.4.5.0-patch8|==2.4.6|>=2.4.6.0-patch1,<2.4.6.0-patch6|==2.4.7", "magento/core": "<=1.9.4.5", "magento/magento1ce": "<1.9.4.3-dev", "magento/magento1ee": ">=1,<1.14.4.3-dev", - "magento/product-community-edition": "<2.4.4.0-patch9|>=2.4.5,<2.4.5.0-patch8|>=2.4.6,<2.4.6.0-patch6|>=2.4.7,<2.4.7.0-patch1", - "magento/project-community-edition": "<=2.0.2", + "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2.0-patch2", "magneto/core": "<1.9.4.4-dev", - "mahocommerce/maho": "<25.9", "maikuolan/phpmussel": ">=1,<1.6", "mainwp/mainwp": "<=4.4.3.3", - "manogi/nova-tiptap": "<=3.2.6", - "mantisbt/mantisbt": "<2.27.2", + "mantisbt/mantisbt": "<2.26.2", "marcwillmann/turn": "<0.3.3", - "marshmallow/nova-tiptap": "<5.7", - "matomo/matomo": "<1.11", "matyhtf/framework": "<3.0.6", - "mautic/core": "<5.2.9|>=6,<6.0.7", - "mautic/core-lib": ">=1.0.0.0-beta,<4.4.13|>=5.0.0.0-alpha,<5.1.1", - "mautic/grapes-js-builder-bundle": ">=4,<4.4.18|>=5,<5.2.9|>=6,<6.0.7", - "maximebf/debugbar": "<1.19", + "mautic/core": "<4.4.12|>=5.0.0.0-alpha,<5.0.4", "mdanter/ecc": "<2", - "mediawiki/abuse-filter": "<1.39.9|>=1.40,<1.41.3|>=1.42,<1.42.2", - "mediawiki/cargo": "<3.8.3", - "mediawiki/core": "<1.39.5|==1.40", - "mediawiki/data-transfer": ">=1.39,<1.39.11|>=1.41,<1.41.3|>=1.42,<1.42.2", + "mediawiki/core": "<1.36.2", "mediawiki/matomo": "<2.4.3", "mediawiki/semantic-media-wiki": "<4.0.2", - "mehrwert/phpmyadmin": "<3.2", "melisplatform/melis-asset-manager": "<5.0.1", - "melisplatform/melis-cms": "<5.3.4", - "melisplatform/melis-cms-slider": "<5.3.1", - "melisplatform/melis-core": "<5.3.11", + "melisplatform/melis-cms": "<5.0.1", "melisplatform/melis-front": "<5.0.1", "mezzio/mezzio-swoole": "<3.7|>=4,<4.3", "mgallegos/laravel-jqgrid": "<=1.3", "microsoft/microsoft-graph": ">=1.16,<1.109.1|>=2,<2.0.1", "microsoft/microsoft-graph-beta": "<2.0.1", "microsoft/microsoft-graph-core": "<2.0.2", - "microweber/microweber": "<=2.0.19", + "microweber/microweber": "<=2.0.4", "mikehaertl/php-shellcommand": "<1.6.1", - "mineadmin/mineadmin": "<=3.0.9", "miniorange/miniorange-saml": "<1.4.3", "mittwald/typo3_forum": "<1.2.1", "mobiledetect/mobiledetectlib": "<2.8.32", - "modx/revolution": "<=3.1", + "modx/revolution": "<=2.8.3.0-patch", "mojo42/jirafeau": "<4.4", "mongodb/mongodb": ">=1,<1.9.2", - "mongodb/mongodb-extension": "<1.21.2", "monolog/monolog": ">=1.8,<1.12", - "moodle/moodle": "<4.4.11|>=4.5.0.0-beta,<4.5.7|>=5.0.0.0-beta,<5.0.3", - "moonshine/moonshine": "<=3.12.5", + "moodle/moodle": "<4.3.5|>=4.4.0.0-beta,<4.4.1", "mos/cimage": "<0.7.19", "movim/moxl": ">=0.8,<=0.10", "movingbytes/social-network": "<=1.2.1", "mpdf/mpdf": "<=7.1.7", - "munkireport/comment": "<4", + "munkireport/comment": "<4.1", "munkireport/managedinstalls": "<2.6", "munkireport/munki_facts": "<1.5", + "munkireport/munkireport": ">=2.5.3,<5.6.3", "munkireport/reportdata": "<3.5", "munkireport/softwareupdate": "<1.6", "mustache/mustache": ">=2,<2.14.1", - "mwdelaney/wp-enable-svg": "<=0.2", "namshi/jose": "<2.2", - "nasirkhan/laravel-starter": "<11.11", - "nategood/httpful": "<1", "neoan3-apps/template": "<1.1.1", "neorazorx/facturascripts": "<2022.04", "neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", @@ -6552,19 +6607,14 @@ "neos/media-browser": "<7.3.19|>=8,<8.0.16|>=8.1,<8.1.11|>=8.2,<8.2.11|>=8.3,<8.3.9", "neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<5.3.10|>=7,<7.0.9|>=7.1,<7.1.7|>=7.2,<7.2.6|>=7.3,<7.3.4|>=8,<8.0.2", "neos/swiftmailer": "<5.4.5", - "nesbot/carbon": "<2.72.6|>=3,<3.8.4", - "netcarver/textile": "<=4.1.2", "netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15", "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", - "neuron-core/neuron-ai": "<=2.8.11", - "nilsteampassnet/teampass": "<3.1.3.1-dev", - "nitsan/ns-backup": "<13.0.1", + "nilsteampassnet/teampass": "<3.0.10", "nonfiction/nterchange": "<4.1.1", "notrinos/notrinos-erp": "<=0.7", "noumo/easyii": "<=0.9", "novaksolutions/infusionsoft-php-sdk": "<1", - "novosga/novosga": "<=2.2.12", "nukeviet/nukeviet": "<4.5.02", "nyholm/psr7": "<1.6.1", "nystudio107/craft-seomatic": "<3.4.12", @@ -6572,28 +6622,26 @@ "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", "october/backend": "<1.1.2", "october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1", - "october/october": "<3.7.5", + "october/october": "<=3.4.4", "october/rain": "<1.0.472|>=1.1,<1.1.2", - "october/system": "<=3.7.12|>=4,<=4.0.11", - "oliverklee/phpunit": "<3.5.15", + "october/system": "<1.0.476|>=1.1,<1.1.12|>=2,<2.2.34|>=3,<3.5.15", "omeka/omeka-s": "<4.0.3", - "onelogin/php-saml": "<2.21.1|>=3,<3.8.1|>=4,<4.3.1", + "onelogin/php-saml": "<2.10.4", "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", - "open-web-analytics/open-web-analytics": "<1.8.1", - "opencart/opencart": ">=0", + "open-web-analytics/open-web-analytics": "<1.7.4", + "opencart/opencart": "<=3.0.3.9|>=4", "openid/php-openid": "<2.3", - "openmage/magento-lts": "<20.16", + "openmage/magento-lts": "<20.5", "opensolutions/vimbadmin": "<=3.0.15", - "opensource-workshop/connect-cms": "<1.8.7|>=2,<2.4.7", - "orchid/platform": ">=8,<14.43", + "opensource-workshop/connect-cms": "<1.7.2|>=2,<2.3.2", + "orchid/platform": ">=9,<9.4.4|>=14.0.0.0-alpha4,<14.5", "oro/calendar-bundle": ">=4.2,<=4.2.6|>=5,<=5.0.6|>=5.1,<5.1.1", "oro/commerce": ">=4.1,<5.0.11|>=5.1,<5.1.1", "oro/crm": ">=1.7,<1.7.4|>=3.1,<4.1.17|>=4.2,<4.2.7", "oro/crm-call-bundle": ">=4.2,<=4.2.5|>=5,<5.0.4|>=5.1,<5.1.1", "oro/customer-portal": ">=4.1,<=4.1.13|>=4.2,<=4.2.10|>=5,<=5.0.11|>=5.1,<=5.1.3", "oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<=4.2.10|>=5,<=5.0.12|>=5.1,<=5.1.3", - "oveleon/contao-cookiebar": "<1.16.3|>=2,<2.1.3", - "oxid-esales/oxideshop-ce": "<=7.0.5", + "oxid-esales/oxideshop-ce": "<4.5", "oxid-esales/paymorrow-module": ">=1,<1.0.2|>=2,<2.0.1", "packbackbooks/lti-1-3-php-library": "<5", "padraic/humbug_get_contents": "<1.1.2", @@ -6601,7 +6649,6 @@ "pagekit/pagekit": "<=1.0.18", "paragonie/ecc": "<2.0.1", "paragonie/random_compat": "<2", - "paragonie/sodium_compat": "<1.24|>=2,<2.5", "passbolt/passbolt_api": "<4.6.2", "paypal/adaptivepayments-sdk-php": "<=3.9.2", "paypal/invoice-sdk-php": "<=3.9", @@ -6610,7 +6657,6 @@ "pear/archive_tar": "<1.4.14", "pear/auth": "<1.2.4", "pear/crypt_gpg": "<1.6.7", - "pear/http_request2": "<2.7", "pear/pear": "<=1.10.1", "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", "personnummer/personnummer": "<3.0.2", @@ -6618,18 +6664,16 @@ "phenx/php-svg-lib": "<0.5.2", "php-censor/php-censor": "<2.0.13|>=2.1,<2.1.5", "php-mod/curl": "<2.3.2", - "phpbb/phpbb": "<3.3.11", + "phpbb/phpbb": "<3.2.10|>=3.3,<3.3.1", "phpems/phpems": ">=6,<=6.1.3", "phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7", "phpmailer/phpmailer": "<6.5", "phpmussel/phpmussel": ">=1,<1.6", - "phpmyadmin/phpmyadmin": "<5.2.2", - "phpmyfaq/phpmyfaq": "<=4.0.13", + "phpmyadmin/phpmyadmin": "<5.2.1", + "phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5", "phpoffice/common": "<0.2.9", - "phpoffice/math": "<=0.2", - "phpoffice/phpexcel": "<=1.8.2", - "phpoffice/phpspreadsheet": "<1.30|>=2,<2.1.12|>=2.2,<2.4|>=3,<3.10|>=4,<5", - "phppgadmin/phppgadmin": "<=7.13", + "phpoffice/phpexcel": "<1.8", + "phpoffice/phpspreadsheet": "<1.16", "phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36", "phpservermon/phpservermon": "<3.6", "phpsysinfo/phpsysinfo": "<3.4.3", @@ -6638,20 +6682,17 @@ "phpxmlrpc/extras": "<0.6.1", "phpxmlrpc/phpxmlrpc": "<4.9.2", "pi/pi": "<=2.5", - "pimcore/admin-ui-classic-bundle": "<=1.7.15|>=2.0.0.0-RC1-dev,<=2.2.2", - "pimcore/customer-management-framework-bundle": "<4.2.1", + "pimcore/admin-ui-classic-bundle": "<=1.4.2", + "pimcore/customer-management-framework-bundle": "<4.0.6", "pimcore/data-hub": "<1.2.4", - "pimcore/data-importer": "<1.8.9|>=1.9,<1.9.3", "pimcore/demo": "<10.3", "pimcore/ecommerce-framework-bundle": "<1.0.10", "pimcore/perspective-editor": "<1.5.1", - "pimcore/pimcore": "<=11.5.13|>=12.0.0.0-RC1-dev,<12.3.1", - "pimcore/web2print-tools-bundle": "<=5.2.1|>=6.0.0.0-RC1-dev,<=6.1", - "piwik/piwik": "<1.11", - "pixelfed/pixelfed": "<0.12.5", + "pimcore/pimcore": "<11.2.4", + "pixelfed/pixelfed": "<0.11.11", "plotly/plotly.js": "<2.25.2", "pocketmine/bedrock-protocol": "<8.0.2", - "pocketmine/pocketmine-mp": "<5.32.1", + "pocketmine/pocketmine-mp": "<5.11.2", "pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1", "pressbooks/pressbooks": "<5.18", "prestashop/autoupgrade": ">=4,<4.10.1", @@ -6659,25 +6700,21 @@ "prestashop/blockwishlist": ">=2,<2.1.1", "prestashop/contactform": ">=1.0.1,<4.3", "prestashop/gamification": "<2.3.2", - "prestashop/prestashop": "<8.2.3", + "prestashop/prestashop": "<8.1.6", "prestashop/productcomments": "<5.0.2", - "prestashop/ps_checkout": "<4.4.1|>=5,<5.0.5", - "prestashop/ps_contactinfo": "<=3.3.2", "prestashop/ps_emailsubscription": "<2.6.1", "prestashop/ps_facetedsearch": "<3.4.1", "prestashop/ps_linklist": "<3.1", - "privatebin/privatebin": "<1.4|>=1.5,<1.7.4|>=1.7.7,<2.0.3", - "processwire/processwire": "<=3.0.246", + "privatebin/privatebin": "<1.4", + "processwire/processwire": "<=3.0.210", "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7", "propel/propel1": ">=1,<=1.7.1", - "pterodactyl/panel": "<1.12", + "pterodactyl/panel": "<1.11.6", "ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2", "ptrofimov/beanstalk_console": "<1.7.14", "pubnub/pubnub": "<6.1", - "punktde/pt_extbase": "<1.5.1", "pusher/pusher-php-server": "<2.2.1", "pwweb/laravel-core": "<=0.3.6.0-beta", - "pxlrbt/filament-excel": "<1.1.14|>=2.0.0.0-alpha,<2.3.3", "pyrocms/pyrocms": "<=3.9.1", "qcubed/qcubed": "<=3.1.1", "quickapps/cms": "<=2.0.0.0-beta2", @@ -6688,111 +6725,90 @@ "rap2hpoutre/laravel-log-viewer": "<0.13", "react/http": ">=0.7,<1.9", "really-simple-plugins/complianz-gdpr": "<6.4.2", - "redaxo/source": "<=5.20.1", + "redaxo/source": "<=5.15.1", "remdex/livehelperchat": "<4.29", - "renolit/reint-downloadmanager": "<4.0.2|>=5,<5.0.1", "reportico-web/reportico": "<=8.1", "rhukster/dom-sanitizer": "<1.0.7", "rmccue/requests": ">=1.6,<1.8", - "robrichards/xmlseclibs": "<=3.1.3", + "robrichards/xmlseclibs": ">=1,<3.0.4", "roots/soil": "<4.1", - "roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11", "rudloff/alltube": "<3.0.3", - "rudloff/rtmpdump-bin": "<=2.3.1", - "s-cart/core": "<=9.0.5", + "s-cart/core": "<6.9", "s-cart/s-cart": "<6.9", "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", "sabre/dav": ">=1.6,<1.7.11|>=1.8,<1.8.9", - "samwilson/unlinked-wikibase": "<1.42", "scheb/two-factor-bundle": "<3.26|>=4,<4.11", "sensiolabs/connect": "<4.2.3", "serluck/phpwhois": "<=4.2.6", - "setasign/fpdi": "<2.6.4", "sfroemken/url_redirect": "<=1.2.1", - "sheng/yiicms": "<1.2.1", - "shopware/core": "<6.6.10.9-dev|>=6.7,<6.7.6.1-dev", - "shopware/platform": "<6.6.10.7-dev|>=6.7,<6.7.3.1-dev", + "sheng/yiicms": "<=1.2", + "shopware/core": "<6.5.8.8-dev|>=6.6.0.0-RC1-dev,<6.6.1", + "shopware/platform": "<6.5.8.8-dev|>=6.6.0.0-RC1-dev,<6.6.1", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<=5.7.17|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.6.1-dev", - "shopware/storefront": "<6.6.10.10-dev|>=6.7,<6.7.5.1-dev", - "shopxo/shopxo": "<=6.4", + "shopware/shopware": "<6.2.3", + "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", + "shopxo/shopxo": "<2.2.6", "showdoc/showdoc": "<2.10.4", - "shuchkin/simplexlsx": ">=1.0.12,<1.1.13", "silverstripe-australia/advancedreports": ">=1,<=2", "silverstripe/admin": "<1.13.19|>=2,<2.1.8", "silverstripe/assets": ">=1,<1.11.1", "silverstripe/cms": "<4.11.3", "silverstripe/comments": ">=1.3,<3.1.1", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": "<5.3.23", + "silverstripe/framework": "<4.13.39|>=5,<5.1.11", "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.8.2|>=4,<4.3.7|>=5,<5.1.3", "silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1", "silverstripe/recipe-cms": ">=4.5,<4.5.3", "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", - "silverstripe/reports": "<5.2.3", "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4|>=2.1,<2.1.2", "silverstripe/silverstripe-omnipay": "<2.5.2|>=3,<3.0.2|>=3.1,<3.1.4|>=3.2,<3.2.1", "silverstripe/subsites": ">=2,<2.6.1", "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1", "silverstripe/userforms": "<3|>=5,<5.4.2", "silverstripe/versioned-admin": ">=1,<1.11.1", - "simogeo/filemanager": "<=2.5", "simple-updates/phpwhois": "<=1", - "simplesamlphp/saml2": "<=4.16.15|>=5.0.0.0-alpha1,<=5.0.0.0-alpha19", - "simplesamlphp/saml2-legacy": "<=4.16.15", + "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4|==5.0.0.0-alpha12", "simplesamlphp/simplesamlphp": "<1.18.6", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", "simplesamlphp/simplesamlphp-module-openid": "<1", "simplesamlphp/simplesamlphp-module-openidprovider": "<0.9", - "simplesamlphp/xml-common": "<1.20", "simplesamlphp/xml-security": "==1.6.11", "simplito/elliptic-php": "<1.0.6", "sitegeist/fluid-components": "<3.5", - "sjbr/sr-feuser-register": "<2.6.2|>=5.1,<12.5", "sjbr/sr-freecap": "<2.4.6|>=2.5,<2.5.3", - "sjbr/static-info-tables": "<2.3.1", "slim/psr7": "<1.4.1|>=1.5,<1.5.1|>=1.6,<1.6.1", "slim/slim": "<2.6", "slub/slub-events": "<3.0.3", "smarty/smarty": "<4.5.3|>=5,<5.1.1", - "snipe/snipe-it": "<=8.3.4", + "snipe/snipe-it": "<6.4.2", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", - "solspace/craft-freeform": "<4.1.29|>=5,<5.10.16", - "soosyze/soosyze": "<=2", - "spatie/browsershot": "<5.0.5", + "spatie/browsershot": "<3.57.4", "spatie/image-optimizer": "<1.7.3", - "spencer14420/sp-php-email-handler": "<1", "spipu/html2pdf": "<5.2.8", - "spiral/roadrunner": "<2025.1", "spoon/library": "<1.4.1", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", - "ssddanbrown/bookstack": "<24.05.1", - "starcitizentools/citizen-skin": ">=1.9.4,<3.9", - "starcitizentools/short-description": ">=4,<4.0.1", - "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2|>=3,<3.1.1", - "starcitizenwiki/embedvideo": "<=4", - "statamic/cms": "<=5.22", + "ssddanbrown/bookstack": "<22.02.3", + "statamic/cms": "<4.46|>=5.3,<5.6.2", "stormpath/sdk": "<9.9.99", - "studio-42/elfinder": "<=2.1.64", + "studio-42/elfinder": "<2.1.62", "studiomitte/friendlycaptcha": "<0.1.4", "subhh/libconnect": "<7.0.8|>=8,<8.1", "sukohi/surpass": "<1", "sulu/form-bundle": ">=2,<2.5.3", - "sulu/sulu": "<1.6.44|>=2,<2.5.25|>=2.6,<2.6.9|>=3.0.0.0-alpha1,<3.0.0.0-alpha3", + "sulu/sulu": "<1.6.44|>=2,<2.4.17|>=2.5,<2.5.13", "sumocoders/framework-user-bundle": "<1.4", "superbig/craft-audit": "<3.0.2", - "svewap/a21glossary": "<=0.4.10", "swag/paypal": "<5.4.4", "swiftmailer/swiftmailer": "<6.2.5", "swiftyedit/swiftyedit": "<1.2", "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", "sylius/grid-bundle": "<1.10.1", - "sylius/paypal-plugin": "<1.6.2|>=1.7,<1.7.2|>=2,<2.0.2", + "sylius/paypal-plugin": ">=1,<1.2.4|>=1.3,<1.3.1", "sylius/resource-bundle": ">=1,<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", - "sylius/sylius": "<1.12.19|>=1.13.0.0-alpha1,<1.13.4", + "sylius/sylius": "<1.9.10|>=1.10,<1.10.11|>=1.11,<1.11.2|>=1.12.0.0-alpha1,<1.12.16|>=1.13.0.0-alpha1,<1.13.1", "symbiote/silverstripe-multivaluefield": ">=3,<3.1", "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", "symbiote/silverstripe-seed": "<6.0.3", @@ -6803,8 +6819,7 @@ "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4", - "symfony/http-client": ">=4.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", - "symfony/http-foundation": "<5.4.50|>=6,<6.4.29|>=7,<7.3.7", + "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", "symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", @@ -6812,24 +6827,20 @@ "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/polyfill": ">=1,<1.10", "symfony/polyfill-php55": ">=1,<1.10", - "symfony/process": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/routing": ">=2,<2.0.19", - "symfony/runtime": ">=5.3,<5.4.46|>=6,<6.4.14|>=7,<7.1.7", "symfony/security": ">=2,<2.7.51|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.8", - "symfony/security-bundle": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.4.10|>=7,<7.0.10|>=7.1,<7.1.3", + "symfony/security-bundle": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.3.2|>=5.4,<5.4.31|>=6,<6.3.8", "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", - "symfony/symfony": "<5.4.50|>=6,<6.4.29|>=7,<7.3.7", + "symfony/symfony": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8", "symfony/translation": ">=2,<2.0.17", "symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8", "symfony/ux-autocomplete": "<2.11.2", - "symfony/ux-live-component": "<2.25.1", - "symfony/ux-twig-component": "<2.25.1", - "symfony/validator": "<5.4.43|>=6,<6.4.11|>=7,<7.1.4", + "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", "symfony/webhook": ">=6.3,<6.3.8", @@ -6838,56 +6849,39 @@ "t3/dce": "<0.11.5|>=2.2,<2.6.2", "t3g/svg-sanitizer": "<1.0.3", "t3s/content-consent": "<1.0.3|>=2,<2.0.2", - "tastyigniter/tastyigniter": "<4", - "tcg/voyager": "<=1.8", - "tecnickcom/tc-lib-pdf-font": "<2.6.4", - "tecnickcom/tcpdf": "<6.8", + "tastyigniter/tastyigniter": "<3.3", + "tcg/voyager": "<=1.4", + "tecnickcom/tcpdf": "<=6.7.4", "terminal42/contao-tablelookupwizard": "<3.3.5", "thelia/backoffice-default-template": ">=2.1,<2.1.2", "thelia/thelia": ">=2.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", "thinkcmf/thinkcmf": "<6.0.8", - "thorsten/phpmyfaq": "<4.0.16|>=4.1.0.0-alpha,<=4.1.0.0-beta2", + "thorsten/phpmyfaq": "<3.2.2", "tikiwiki/tiki-manager": "<=17.1", "timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1", "tinymce/tinymce": "<7.2", "tinymighty/wiki-seo": "<1.2.2", "titon/framework": "<9.9.99", - "tltneon/lgsl": "<7", "tobiasbg/tablepress": "<=2.0.0.0-RC1", - "topthink/framework": "<6.0.17|>=6.1,<=8.0.4", + "topthink/framework": "<6.0.17|>=6.1,<6.1.5|>=8,<8.0.4", "topthink/think": "<=6.1.1", - "topthink/thinkphp": "<=3.2.3|>=6.1.3,<=8.0.4", - "torrentpier/torrentpier": "<=2.8.8", + "topthink/thinkphp": "<=3.2.3", + "torrentpier/torrentpier": "<=2.4.1", "tpwd/ke_search": "<4.0.3|>=4.1,<4.6.6|>=5,<5.0.2", - "tribalsystems/zenario": "<=9.7.61188", + "tribalsystems/zenario": "<9.5.60602", "truckersmp/phpwhois": "<=4.3.1", "ttskch/pagination-service-provider": "<1", - "twbs/bootstrap": "<3.4.1|>=4,<4.3.1", - "twig/twig": "<3.11.2|>=3.12,<3.14.1|>=3.16,<3.19", + "twig/twig": "<1.44.7|>=2,<2.15.3|>=3,<3.4.3", "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", - "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", - "typo3/cms-belog": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", - "typo3/cms-beuser": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", - "typo3/cms-core": "<=8.7.56|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", - "typo3/cms-dashboard": ">=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", + "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", + "typo3/cms-core": "<=8.7.56|>=9,<=9.5.47|>=10,<=10.4.44|>=11,<=11.5.36|>=12,<=12.4.14|>=13,<=13.1", "typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1", - "typo3/cms-extensionmanager": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", - "typo3/cms-felogin": ">=4.2,<4.2.3", "typo3/cms-fluid": "<4.3.4|>=4.4,<4.4.1", - "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", "typo3/cms-frontend": "<4.3.9|>=4.4,<4.4.5", - "typo3/cms-indexed-search": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", - "typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8|==13.4.2", - "typo3/cms-lowlevel": ">=11,<=11.5.41", - "typo3/cms-recordlist": ">=11,<11.5.48", - "typo3/cms-recycler": ">=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", - "typo3/cms-redirects": ">=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", + "typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8", "typo3/cms-rte-ckeditor": ">=9.5,<9.5.42|>=10,<10.4.39|>=11,<11.5.30", - "typo3/cms-scheduler": ">=11,<=11.5.41", - "typo3/cms-setup": ">=9,<=9.5.50|>=10,<=10.4.49|>=11,<=11.5.43|>=12,<=12.4.30|>=13,<=13.4.11", - "typo3/cms-webhooks": ">=12,<=12.4.30|>=13,<=13.4.11", - "typo3/cms-workspaces": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", "typo3/html-sanitizer": ">=1,<=1.5.2|>=2,<=2.1.3", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", @@ -6896,48 +6890,39 @@ "typo3fluid/fluid": ">=2,<2.0.8|>=2.1,<2.1.7|>=2.2,<2.2.4|>=2.3,<2.3.7|>=2.4,<2.4.4|>=2.5,<2.5.11|>=2.6,<2.6.10", "ua-parser/uap-php": "<3.8", "uasoft-indonesia/badaso": "<=2.9.7", - "unisharp/laravel-filemanager": "<2.9.1", - "universal-omega/dynamic-page-list3": "<3.6.4", - "unopim/unopim": "<=0.3", + "unisharp/laravel-filemanager": "<2.6.4", "userfrosting/userfrosting": ">=0.3.1,<4.6.3", "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", "uvdesk/community-skeleton": "<=1.1.1", "uvdesk/core-framework": "<=1.1.1", "vanilla/safecurl": "<0.9.2", "verbb/comments": "<1.5.5", - "verbb/formie": "<=2.1.43", + "verbb/formie": "<2.1.6", "verbb/image-resizer": "<2.0.9", "verbb/knock-knock": "<1.2.8", "verot/class.upload.php": "<=2.1.6", - "vertexvaar/falsftp": "<0.2.6", "villagedefrance/opencart-overclocked": "<=1.11.1", "vova07/yii2-fileapi-widget": "<0.1.9", - "vrana/adminer": "<=4.8.1", + "vrana/adminer": "<4.8.1", "vufind/vufind": ">=2,<9.1.1", "waldhacker/hcaptcha": "<2.1.2", "wallabag/tcpdf": "<6.2.22", - "wallabag/wallabag": "<2.6.11", + "wallabag/wallabag": "<2.6.7", "wanglelecc/laracms": "<=1.0.3", - "wapplersystems/a21glossary": "<=0.4.10", - "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9", - "web-auth/webauthn-lib": ">=4.5,<4.9", + "web-auth/webauthn-framework": ">=3.3,<3.3.4", "web-feet/coastercms": "==5.5", - "web-tp3/wec_map": "<3.0.3", "webbuilders-group/silverstripe-kapost-bridge": "<0.4", "webcoast/deferred-image-processing": "<1.0.2", "webklex/laravel-imap": "<5.3", "webklex/php-imap": "<5.3", "webpa/webpa": "<3.1.2", - "webreinvent/vaahcms": "<=2.3.1", "wikibase/wikibase": "<=1.39.3", "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", "winter/wn-backend-module": "<1.2.4", - "winter/wn-cms-module": "<1.0.476|>=1.1,<1.1.11|>=1.2,<1.2.7", "winter/wn-dusk-plugin": "<2.1", "winter/wn-system-module": "<1.2.4", "wintercms/winter": "<=1.2.3", - "wireui/wireui": "<1.19.3|>=2,<2.1.3", "woocommerce/woocommerce": "<6.6|>=8.8,<8.8.5|>=8.9,<8.9.3", "wp-cli/wp-cli": ">=0.12,<2.5", "wp-graphql/wp-graphql": "<=1.14.5", @@ -6949,25 +6934,23 @@ "xataface/xataface": "<3", "xpressengine/xpressengine": "<3.0.15", "yab/quarx": "<2.4.5", - "yeswiki/yeswiki": "<=4.5.4", - "yetiforce/yetiforce-crm": "<6.5", + "yeswiki/yeswiki": "<4.1", + "yetiforce/yetiforce-crm": "<=6.4", "yidashi/yii2cmf": "<=2", "yii2mod/yii2-cms": "<1.9.2", - "yiisoft/yii": "<1.1.31", - "yiisoft/yii2": "<2.0.52", + "yiisoft/yii": "<1.1.29", + "yiisoft/yii2": "<2.0.50", "yiisoft/yii2-authclient": "<2.2.15", "yiisoft/yii2-bootstrap": "<2.0.4", - "yiisoft/yii2-dev": "<=2.0.45", + "yiisoft/yii2-dev": "<2.0.43", "yiisoft/yii2-elasticsearch": "<2.0.5", "yiisoft/yii2-gii": "<=2.2.4", "yiisoft/yii2-jui": "<2.0.4", - "yiisoft/yii2-redis": "<2.0.20", + "yiisoft/yii2-redis": "<2.0.8", "yikesinc/yikes-inc-easy-mailchimp-extender": "<6.8.6", "yoast-seo-for-typo3/yoast_seo": "<7.2.3", - "yourls/yourls": "<=1.10.2", + "yourls/yourls": "<=1.8.2", "yuan1994/tpadmin": "<=1.3.12", - "yungifez/skuul": "<=2.6.5", - "z-push/z-push-dev": "<2.7.6", "zencart/zencart": "<=1.5.7.0-beta", "zendesk/zendesk_api_client_php": "<2.2.11", "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", @@ -7006,6 +6989,7 @@ "zfr/zfr-oauth2-server-module": "<0.1.2", "zoujingli/thinkadmin": "<=6.1.53" }, + "default-branch": true, "type": "metapackage", "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7041,7 +7025,7 @@ "type": "tidelift" } ], - "time": "2026-01-15T23:06:28+00:00" + "time": "2024-06-26T15:05:17+00:00" }, { "name": "sebastian/cli-parser", @@ -7213,16 +7197,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.4", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", "shasum": "" }, "require": { @@ -7233,7 +7217,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^10.3" }, "type": "library", "extra": { @@ -7278,27 +7262,15 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", - "type": "tidelift" } ], - "time": "2025-09-07T05:25:07+00:00" + "time": "2023-08-14T13:18:12+00:00" }, { "name": "sebastian/complexity", @@ -7491,16 +7463,16 @@ }, { "name": "sebastian/exporter", - "version": "5.1.4", + "version": "5.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "0735b90f4da94969541dac1da743446e276defa6" + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", - "reference": "0735b90f4da94969541dac1da743446e276defa6", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", "shasum": "" }, "require": { @@ -7509,7 +7481,7 @@ "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { @@ -7557,27 +7529,15 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", - "type": "tidelift" } ], - "time": "2025-09-24T06:09:11+00:00" + "time": "2024-03-02T07:17:12+00:00" }, { "name": "sebastian/global-state", @@ -7705,265 +7665,26 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:08:32+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:06:18+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "5.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", - "type": "tidelift" - } - ], - "time": "2025-08-10T07:50:56+00:00" - }, - { - "name": "sebastian/type", - "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:10:45+00:00" - }, - { - "name": "sebastian/version", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -7978,15 +7699,14 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" }, "funding": [ { @@ -7994,426 +7714,330 @@ "type": "github" } ], - "time": "2023-02-07T11:34:05+00:00" + "time": "2023-02-03T07:08:32+00:00" }, { - "name": "slevomat/coding-standard", - "version": "8.26.0", + "name": "sebastian/object-reflector", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/slevomat/coding-standard.git", - "reference": "d247cdc04b91956bdcfaa0b1313c01960b189d3c" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/d247cdc04b91956bdcfaa0b1313c01960b189d3c", - "reference": "d247cdc04b91956bdcfaa0b1313c01960b189d3c", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.2.0", - "php": "^7.4 || ^8.0", - "phpstan/phpdoc-parser": "^2.3.0", - "squizlabs/php_codesniffer": "^4.0.1" + "php": ">=8.1" }, "require-dev": { - "phing/phing": "3.0.1|3.1.0", - "php-parallel-lint/php-parallel-lint": "1.4.0", - "phpstan/phpstan": "2.1.33", - "phpstan/phpstan-deprecation-rules": "2.0.3", - "phpstan/phpstan-phpunit": "2.0.11", - "phpstan/phpstan-strict-rules": "2.0.7", - "phpunit/phpunit": "9.6.31|10.5.60|11.4.4|11.5.46|12.5.4" + "phpunit/phpunit": "^10.0" }, - "type": "phpcodesniffer-standard", + "type": "library", "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-main": "3.0-dev" } }, "autoload": { - "psr-4": { - "SlevomatCodingStandard\\": "SlevomatCodingStandard/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", - "keywords": [ - "dev", - "phpcs" + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.26.0" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, "funding": [ { - "url": "https://github.com/kukulich", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", - "type": "tidelift" } ], - "time": "2025-12-21T18:01:15+00:00" + "time": "2023-02-03T07:06:18+00:00" }, { - "name": "softcreatr/jsonpath", - "version": "0.8.3", + "name": "sebastian/recursion-context", + "version": "5.0.0", "source": { "type": "git", - "url": "https://github.com/SoftCreatR/JSONPath.git", - "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SoftCreatR/JSONPath/zipball/fc12dee0b46f3fa3a175c4051dbab60984acef4b", - "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", "shasum": "" }, "require": { - "ext-json": "*", - "php": ">=8.0" - }, - "replace": { - "flow/jsonpath": "*" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.6", - "roave/security-advisories": "dev-latest" + "phpunit/phpunit": "^10.0" }, "type": "library", - "autoload": { - "psr-4": { - "Flow\\JSONPath\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Stephen Frank", - "email": "stephen@flowsa.com", - "homepage": "https://prismaticbytes.com", - "role": "Developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Sascha Greuel", - "email": "hello@1-2.dev", - "homepage": "https://1-2.dev", - "role": "Developer" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "JSONPath implementation for parsing, searching and flattening arrays", + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "email": "hello@1-2.dev", - "forum": "https://github.com/SoftCreatR/JSONPath/discussions", - "issues": "https://github.com/SoftCreatR/JSONPath/issues", - "source": "https://github.com/SoftCreatR/JSONPath" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" }, "funding": [ { - "url": "https://ecologi.com/softcreatr?r=61212ab3fc69b8eb8a2014f4", - "type": "custom" - }, - { - "url": "https://github.com/softcreatr", + "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2023-08-17T20:14:00+00:00" + "time": "2023-02-03T07:05:40+00:00" }, { - "name": "squizlabs/php_codesniffer", - "version": "4.0.1", + "name": "sebastian/type", + "version": "4.0.0", "source": { "type": "git", - "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "0525c73950de35ded110cffafb9892946d7771b5" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5", - "reference": "0525c73950de35ded110cffafb9892946d7771b5", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=7.2.0" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31" + "phpunit/phpunit": "^10.0" }, - "bin": [ - "bin/phpcbf", - "bin/phpcs" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { - "name": "Greg Sherwood", - "role": "Former lead" - }, - { - "name": "Juliette Reinders Folmer", - "role": "Current lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", - "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", - "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, "funding": [ { - "url": "https://github.com/PHPCSStandards", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - }, - { - "url": "https://thanks.dev/u/gh/phpcsstandards", - "type": "thanks_dev" } ], - "time": "2025-11-10T16:43:36+00:00" + "time": "2023-02-03T07:10:45+00:00" }, { - "name": "symfony/cache", - "version": "v7.4.3", + "name": "sebastian/version", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/symfony/cache.git", - "reference": "642117d18bc56832e74b68235359ccefab03dd11" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/642117d18bc56832e74b68235359ccefab03dd11", - "reference": "642117d18bc56832e74b68235359ccefab03dd11", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/cache": "^2.0|^3.0", - "psr/log": "^1.1|^2|^3", - "symfony/cache-contracts": "^3.6", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/service-contracts": "^2.5|^3", - "symfony/var-exporter": "^6.4|^7.0|^8.0" - }, - "conflict": { - "doctrine/dbal": "<3.6", - "ext-redis": "<6.1", - "ext-relay": "<0.12.1", - "symfony/dependency-injection": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/var-dumper": "<6.4" - }, - "provide": { - "psr/cache-implementation": "2.0|3.0", - "psr/simple-cache-implementation": "1.0|2.0|3.0", - "symfony/cache-implementation": "1.1|2.0|3.0" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/dbal": "^3.6|^4", - "predis/predis": "^1.1|^2.0", - "psr/simple-cache": "^1.0|^2.0|^3.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/filesystem": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "php": ">=8.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Cache\\": "" - }, "classmap": [ - "Traits/ValueWrapper.php" - ], - "exclude-from-classmap": [ - "/Tests/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", - "homepage": "https://symfony.com", - "keywords": [ - "caching", - "psr6" - ], - "support": { - "source": "https://github.com/symfony/cache/tree/v7.4.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2025-12-28T10:45:24+00:00" + "time": "2023-02-07T11:34:05+00:00" }, { - "name": "symfony/cache-contracts", - "version": "v3.6.0", + "name": "slevomat/coding-standard", + "version": "8.15.0", "source": { "type": "git", - "url": "https://github.com/symfony/cache-contracts.git", - "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868" + "url": "https://github.com/slevomat/coding-standard.git", + "reference": "7d1d957421618a3803b593ec31ace470177d7817" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/5d68a57d66910405e5c0b63d6f0af941e66fc868", - "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/7d1d957421618a3803b593ec31ace470177d7817", + "reference": "7d1d957421618a3803b593ec31ace470177d7817", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/cache": "^3.0" + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", + "php": "^7.2 || ^8.0", + "phpstan/phpdoc-parser": "^1.23.1", + "squizlabs/php_codesniffer": "^3.9.0" }, - "type": "library", + "require-dev": { + "phing/phing": "2.17.4", + "php-parallel-lint/php-parallel-lint": "1.3.2", + "phpstan/phpstan": "1.10.60", + "phpstan/phpstan-deprecation-rules": "1.1.4", + "phpstan/phpstan-phpunit": "1.3.16", + "phpstan/phpstan-strict-rules": "1.5.2", + "phpunit/phpunit": "8.5.21|9.6.8|10.5.11" + }, + "type": "phpcodesniffer-standard", "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-master": "8.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Contracts\\Cache\\": "" + "SlevomatCodingStandard\\": "SlevomatCodingStandard/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to caching", - "homepage": "https://symfony.com", + "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "dev", + "phpcs" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v3.6.0" + "issues": "https://github.com/slevomat/coding-standard/issues", + "source": "https://github.com/slevomat/coding-standard/tree/8.15.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/kukulich", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", "type": "tidelift" } ], - "time": "2025-03-13T15:25:07+00:00" + "time": "2024-03-09T15:20:58+00:00" }, { - "name": "symfony/options-resolver", - "version": "v7.4.0", + "name": "softcreatr/jsonpath", + "version": "0.8.3", "source": { "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "b38026df55197f9e39a44f3215788edf83187b80" + "url": "https://github.com/SoftCreatR/JSONPath.git", + "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b38026df55197f9e39a44f3215788edf83187b80", - "reference": "b38026df55197f9e39a44f3215788edf83187b80", + "url": "https://api.github.com/repos/SoftCreatR/JSONPath/zipball/fc12dee0b46f3fa3a175c4051dbab60984acef4b", + "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" + "ext-json": "*", + "php": ">=8.0" + }, + "replace": { + "flow/jsonpath": "*" + }, + "require-dev": { + "phpunit/phpunit": "^9.6", + "roave/security-advisories": "dev-latest" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Flow\\JSONPath\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -8421,202 +8045,268 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Stephen Frank", + "email": "stephen@flowsa.com", + "homepage": "https://prismaticbytes.com", + "role": "Developer" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sascha Greuel", + "email": "hello@1-2.dev", + "homepage": "https://1-2.dev", + "role": "Developer" } ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], + "description": "JSONPath implementation for parsing, searching and flattening arrays", "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.4.0" + "email": "hello@1-2.dev", + "forum": "https://github.com/SoftCreatR/JSONPath/discussions", + "issues": "https://github.com/SoftCreatR/JSONPath/issues", + "source": "https://github.com/SoftCreatR/JSONPath" }, "funding": [ { - "url": "https://symfony.com/sponsor", + "url": "https://ecologi.com/softcreatr?r=61212ab3fc69b8eb8a2014f4", "type": "custom" }, { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/softcreatr", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2025-11-12T15:39:26+00:00" + "time": "2023-08-17T20:14:00+00:00" }, { - "name": "symfony/stopwatch", - "version": "v8.0.0", + "name": "squizlabs/php_codesniffer", + "version": "3.10.1", "source": { "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "67df1914c6ccd2d7b52f70d40cf2aea02159d942" + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/67df1914c6ccd2d7b52f70d40cf2aea02159d942", - "reference": "67df1914c6ccd2d7b52f70d40cf2aea02159d942", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/8f90f7a53ce271935282967f53d0894f8f1ff877", + "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877", "shasum": "" }, "require": { - "php": ">=8.4", - "symfony/service-contracts": "^2.5|^3" + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Greg Sherwood", + "role": "Former lead" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" } ], - "description": "Provides a way to profile code", - "homepage": "https://symfony.com", + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], "support": { - "source": "https://github.com/symfony/stopwatch/tree/v8.0.0" + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/PHPCSStandards", "type": "github" }, { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/jrfnl", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" } ], - "time": "2025-08-04T07:36:47+00:00" + "time": "2024-05-22T21:24:41+00:00" }, { - "name": "symfony/var-exporter", - "version": "v8.0.0", + "name": "thecodingmachine/safe", + "version": "v2.5.0", "source": { "type": "git", - "url": "https://github.com/symfony/var-exporter.git", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04" + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/3115ecd6b4391662b4931daac4eba6b07a2ac1f0", + "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0", "shasum": "" }, "require": { - "php": ">=8.4" + "php": "^8.0" }, "require-dev": { - "symfony/property-access": "^7.4|^8.0", - "symfony/serializer": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^8.0" + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.2", + "thecodingmachine/phpstan-strict-rules": "^1.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\VarExporter\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "deprecated/apc.php", + "deprecated/array.php", + "deprecated/datetime.php", + "deprecated/libevent.php", + "deprecated/misc.php", + "deprecated/password.php", + "deprecated/mssql.php", + "deprecated/stats.php", + "deprecated/strings.php", + "lib/special_cases.php", + "deprecated/mysqli.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "deprecated/Exceptions/", + "generated/Exceptions/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows exporting any serializable PHP data structure to plain PHP code", - "homepage": "https://symfony.com", - "keywords": [ - "clone", - "construct", - "export", - "hydrate", - "instantiate", - "lazy-loading", - "proxy", - "serialize" - ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", "support": { - "source": "https://github.com/symfony/var-exporter/tree/v8.0.0" + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v2.5.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-11-05T18:53:00+00:00" + "time": "2023-04-05T11:54:14+00:00" }, { "name": "theseer/tokenizer", - "version": "1.3.1", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -8645,7 +8335,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -8653,7 +8343,66 @@ "type": "github" } ], - "time": "2025-11-17T20:03:58+00:00" + "time": "2024-03-03T12:36:25+00:00" + }, + { + "name": "trinet/mezzio-test", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/trinet-at/mezzio-test.git", + "reference": "d64fe3a2b5161805679716e2e445b835ab22a468" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/trinet-at/mezzio-test/zipball/d64fe3a2b5161805679716e2e445b835ab22a468", + "reference": "d64fe3a2b5161805679716e2e445b835ab22a468", + "shasum": "" + }, + "require": { + "fig/http-message-util": "^1.1", + "laminas/laminas-config-aggregator": "^1.2", + "laminas/laminas-diactoros": "^2.2", + "laminas/laminas-stratigility": "^3.2", + "mezzio/mezzio": "^3.2", + "mezzio/mezzio-router": "^3.1", + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-message": "^1.0", + "thecodingmachine/safe": "^1.0 || ^2.0" + }, + "require-dev": { + "bnf/phpstan-psr-container": "^1.0", + "eventjet/coding-standard": "^3.1", + "infection/infection": "^0.26.0", + "laminas/laminas-servicemanager": "^3.4", + "maglnet/composer-require-checker": "^3.3 || ^4.0", + "mezzio/mezzio-fastroute": "^3.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.16.1", + "thecodingmachine/phpstan-safe-rule": "^1.0", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Trinet\\MezzioTest\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Testing helpers for mezzio projects", + "support": { + "issues": "https://github.com/trinet-at/mezzio-test/issues", + "source": "https://github.com/trinet-at/mezzio-test/tree/1.1.2" + }, + "time": "2022-02-25T15:51:27+00:00" } ], "aliases": [], @@ -8664,14 +8413,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "~8.4.0", + "php": "^8.3", "ext-json": "*", "ext-openssl": "*", "ext-pdo": "*" }, - "platform-dev": {}, - "platform-overrides": { - "php": "8.4.16" - }, - "plugin-api-version": "2.9.0" + "platform-dev": [], + "plugin-api-version": "2.6.0" } diff --git a/config/.gitignore b/config/.gitignore index 96043010..d5ce9da0 100644 --- a/config/.gitignore +++ b/config/.gitignore @@ -1 +1,2 @@ development.config.php +database.php diff --git a/config/autoload/.gitignore b/config/autoload/.gitignore index 1a83fda6..65f817e9 100644 --- a/config/autoload/.gitignore +++ b/config/autoload/.gitignore @@ -1,2 +1,4 @@ +*.production.php local.php -*.local.php +development.config.global.php +development.local.php diff --git a/config/autoload/cors.develope.php b/config/autoload/cors.develope.php deleted file mode 100644 index 19c8b962..00000000 --- a/config/autoload/cors.develope.php +++ /dev/null @@ -1,13 +0,0 @@ - [ - 'allowed_origins' => ['http://localhost:5173','http://localhost'], - 'allowed_headers' => ['x-ident', 'Authorization', 'Authentication', 'Content-Type'], // No custom headers allowed - 'allowed_max_age' => '3600', // 60 minutes - 'credentials_allowed' => true, // Disallow cookies - 'exposed_headers' => [], // No headers are exposed - ], -]; diff --git a/config/autoload/cors.global.php b/config/autoload/cors.global.php deleted file mode 100644 index 2c9c2000..00000000 --- a/config/autoload/cors.global.php +++ /dev/null @@ -1,13 +0,0 @@ - [ - 'allowed_origins' => [ConfigurationInterface::ANY_ORIGIN], - 'allowed_headers' => ['x-ident', 'Authorization', 'Authentication', 'Content-Type'], // No custom headers allowed - 'allowed_max_age' => '3600', // 60 minutes - 'credentials_allowed' => true, // Disallow cookies - 'exposed_headers' => [], // No headers are exposed - ], -]; diff --git a/config/autoload/database.action.php b/config/autoload/database.action.php deleted file mode 100644 index 13ee96f3..00000000 --- a/config/autoload/database.action.php +++ /dev/null @@ -1,20 +0,0 @@ - [ - 'driver' => 'mysql', - 'host' => '127.0.0.1', - 'port' => '3306', - 'user' => 'dev', - 'password' => 'dev', - 'dbname' => 'db', - 'charset' => 'utf8mb4', - 'defaultTableOptions' => [ - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_general_ci', - 'engine' => 'InnoDB', - ], - 'error' => PDO::ERRMODE_EXCEPTION, - 'emulate_prepares' => false, - ] -]; diff --git a/config/autoload/database.develope.php b/config/autoload/database.develope.php index 08271b9e..675fb163 100644 --- a/config/autoload/database.develope.php +++ b/config/autoload/database.develope.php @@ -3,7 +3,7 @@ return [ 'database' => [ 'driver' => 'mysql', - 'host' => 'database', + 'host' => 'hackathon-mariadb', 'port' => '3306', 'user' => 'dev', 'password' => 'dev', @@ -15,6 +15,5 @@ 'engine' => 'InnoDB', ], 'error' => PDO::ERRMODE_EXCEPTION, - 'emulate_prepares' => false, ] ]; diff --git a/config/autoload/database.global.php.dist b/config/autoload/database.global.php similarity index 92% rename from config/autoload/database.global.php.dist rename to config/autoload/database.global.php index 1152a4f8..fe149128 100644 --- a/config/autoload/database.global.php.dist +++ b/config/autoload/database.global.php @@ -15,6 +15,5 @@ 'engine' => 'InnoDB', ], 'error' => PDO::ERRMODE_EXCEPTION, - 'emulate_prepares' => false, ] ]; diff --git a/config/autoload/database.testing.php b/config/autoload/database.testing.php index 73522bc0..68e56478 100644 --- a/config/autoload/database.testing.php +++ b/config/autoload/database.testing.php @@ -2,19 +2,8 @@ return [ 'database' => [ - 'driver' => 'mysql', - 'host' => 'database-testing', - 'port' => '3306', - 'user' => 'dev', - 'password' => 'dev', - 'dbname' => 'db', - 'charset' => 'utf8mb4', - 'defaultTableOptions' => [ - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_general_ci', - 'engine' => 'InnoDB', - ], - 'error' => PDO::ERRMODE_EXCEPTION, - 'emulate_prepares' => false, - ] + 'driver' => 'sqlite', + 'path' => __DIR__ . '/../../database/database.sqlite', + 'memory' => false, + ], ]; diff --git a/config/autoload/dependencies.action.php b/config/autoload/dependencies.action.php deleted file mode 100644 index 955817c8..00000000 --- a/config/autoload/dependencies.action.php +++ /dev/null @@ -1,29 +0,0 @@ - [ - 'aliases' => [ - PDO::class => 'database', - Envms\FluentPDO\Query::class => 'query', - UuidFactoryInterface::class => 'uuid', - Psr\Log\LoggerInterface::class => 'logger', - Symfony\Component\Mailer\MailerInterface::class => 'mailer', - ], - 'invokables' => [ - ], - 'factories' => [ - 'database' => DatabaseFactory::class, - 'query' => QueryFactory::class, - 'logger' => LoggerFactory::class, - 'uuid' => UuidFactory::class, - 'mailer' => NullMailerFactory::class, - ], - ], -]; diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index 2a172689..a1774b71 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -1,40 +1,30 @@ - [ - // Use 'aliases' to alias a service name to another service. The - // key is the alias name, the value is the service to which it points. 'aliases' => [ PDO::class => 'database', Envms\FluentPDO\Query::class => 'query', + Ramsey\Uuid\Uuid::class => 'uuid', + Symfony\Component\Mailer\Mailer::class => 'mailer', Psr\Log\LoggerInterface::class => 'logger', - UuidFactoryInterface::class => 'uuid', - Symfony\Component\Mailer\MailerInterface::class => 'mailer', ], - // Use 'invokables' for constructor-less services, or services that do - // not require arguments to the constructor. Map a service name to the - // class name. 'invokables' => [ ], - // Use 'factories' for services provided by callbacks/factory classes. 'factories' => [ - 'database' => DatabaseFactory::class, - 'query' => QueryFactory::class, - 'logger' => LoggerFactory::class, - 'uuid' => UuidFactory::class, - 'mailer' => MailFactory::class, + 'database' => \Core\Factory\DatabaseFactory::class, + 'query' => \Core\Factory\QueryFactory::class, + 'uuid' => \Core\Factory\UuidFactory::class, + 'mailer' => \Core\Factory\MailFactory::class, + 'logger' => \Core\Factory\LoggerFactory::class, + ], + 'delegators' => [ + ErrorHandler::class => [ + LoggingErrorListenerDelegatorFactory::class, + ], ], ], ]; diff --git a/config/autoload/dependencies.testing.php b/config/autoload/dependencies.testing.php deleted file mode 100644 index 955817c8..00000000 --- a/config/autoload/dependencies.testing.php +++ /dev/null @@ -1,29 +0,0 @@ - [ - 'aliases' => [ - PDO::class => 'database', - Envms\FluentPDO\Query::class => 'query', - UuidFactoryInterface::class => 'uuid', - Psr\Log\LoggerInterface::class => 'logger', - Symfony\Component\Mailer\MailerInterface::class => 'mailer', - ], - 'invokables' => [ - ], - 'factories' => [ - 'database' => DatabaseFactory::class, - 'query' => QueryFactory::class, - 'logger' => LoggerFactory::class, - 'uuid' => UuidFactory::class, - 'mailer' => NullMailerFactory::class, - ], - ], -]; diff --git a/config/autoload/development.config.global.php.dist b/config/autoload/development.config.global.php.dist new file mode 100644 index 00000000..fe5fd1bc --- /dev/null +++ b/config/autoload/development.config.global.php.dist @@ -0,0 +1,5 @@ + true, +]; diff --git a/config/autoload/development.local.php.dist b/config/autoload/development.local.php.dist index 4aca5a7c..65372a01 100644 --- a/config/autoload/development.local.php.dist +++ b/config/autoload/development.local.php.dist @@ -17,6 +17,7 @@ declare(strict_types=1); use Mezzio\Container; use Mezzio\Middleware\ErrorResponseGenerator; + return [ 'dependencies' => [ 'factories' => [ diff --git a/config/autoload/discord.global.php b/config/autoload/discord.global.php new file mode 100644 index 00000000..5ab3b592 --- /dev/null +++ b/config/autoload/discord.global.php @@ -0,0 +1,9 @@ + [ + 'clientId' => '{discord-client-id}', + 'clientSecret' => '{discord-client-secret}', + 'redirectUri' => '{your-server-uri-to-this-script-here}' + ], +]; \ No newline at end of file diff --git a/config/autoload/mail.global.php b/config/autoload/mail.global.php new file mode 100644 index 00000000..3dd3456b --- /dev/null +++ b/config/autoload/mail.global.php @@ -0,0 +1,8 @@ + [ + 'dsn' => 'smtp://server:port', + 'from' => 'email@adress', + ], +]; diff --git a/config/autoload/mail.testing.php b/config/autoload/mail.testing.php deleted file mode 100644 index 7a17007e..00000000 --- a/config/autoload/mail.testing.php +++ /dev/null @@ -1,8 +0,0 @@ - [ - 'dsn' => 'smtp://mailhog:1025', - 'from' => 'hackathon@exdrals.de', - ], -]; diff --git a/config/autoload/mezzio.global.php b/config/autoload/mezzio.global.php index 64d701e7..623582c7 100644 --- a/config/autoload/mezzio.global.php +++ b/config/autoload/mezzio.global.php @@ -12,12 +12,11 @@ ConfigAggregator::ENABLE_CACHE => true, // Enable debugging; typically used to provide debugging information within templates. - 'debug' => false, 'mezzio' => [ // Provide templates for the error handling middleware to use when // generating responses. 'error_handler' => [ - 'template_404' => 'error::404', + 'template_404' => 'error::404', 'template_error' => 'error::error', ], ], diff --git a/config/autoload/mezzio.local.php b/config/autoload/mezzio.local.php new file mode 100644 index 00000000..623582c7 --- /dev/null +++ b/config/autoload/mezzio.local.php @@ -0,0 +1,23 @@ + true, + + // Enable debugging; typically used to provide debugging information within templates. + 'mezzio' => [ + // Provide templates for the error handling middleware to use when + // generating responses. + 'error_handler' => [ + 'template_404' => 'error::404', + 'template_error' => 'error::error', + ], + ], +]; diff --git a/config/autoload/migrations.action.php b/config/autoload/migrations.action.php deleted file mode 100644 index 166dbab1..00000000 --- a/config/autoload/migrations.action.php +++ /dev/null @@ -1,23 +0,0 @@ - [ - 'table_storage' => [ - 'table_name' => 'MigrationVersions', - 'version_column_name' => 'version', - 'version_column_length' => 192, - 'executed_at_column_name' => 'executedAt', - 'execution_time_column_name' => 'executionTime', - ], - - 'migrations_paths' => [ - 'Migrations' => __DIR__ . '/../../database/migrations', - 'TestDataMigrations' => __DIR__ . '/../../tests/FunctionalTest/database/migrations', - ], - - 'all_or_nothing' => true, - 'transactional' => true, - 'check_database_platform' => true, - 'organize_migrations' => 'none', - ], -]; diff --git a/config/autoload/migrations.testing.php b/config/autoload/migrations.testing.php index 166dbab1..0626d103 100644 --- a/config/autoload/migrations.testing.php +++ b/config/autoload/migrations.testing.php @@ -12,7 +12,6 @@ 'migrations_paths' => [ 'Migrations' => __DIR__ . '/../../database/migrations', - 'TestDataMigrations' => __DIR__ . '/../../tests/FunctionalTest/database/migrations', ], 'all_or_nothing' => true, diff --git a/config/autoload/project.global.php b/config/autoload/project.global.php index 7ef504b6..b99c1907 100644 --- a/config/autoload/project.global.php +++ b/config/autoload/project.global.php @@ -2,15 +2,14 @@ return [ 'project' => [ - 'uri' => 'dev.ownhackathon.de', + 'uri' => 'build.hackathon.exdrals.de', ], 'api' => [ 'access' => [ 'domain' => [ 'whitelist' => [ - 'build.ownhackathon.de', - 'dev.ownhackathon.de', - 'ownhackathon.de', + 'build.hackathon.exdrals.de', + 'hackathon.exdrals.de', ], ], ], diff --git a/config/autoload/routes.global.php b/config/autoload/routes.global.php deleted file mode 100644 index 30521647..00000000 --- a/config/autoload/routes.global.php +++ /dev/null @@ -1,29 +0,0 @@ - [ - //.. - 'invokables' => [ - /* ... */ - // Comment out or remove the following line: - // Mezzio\Router\RouterInterface::class => Mezzio\Router\FastRouteRouter::class, - /* ... */ - ], - 'factories' => [ - /* ... */ - // Add this line; the specified factory now creates the router instance: - /* ... */ - ], - ], - - // Add the following to enable caching support: - 'router' => [ - 'fastroute' => [ - // Enable caching support: - 'cache_enabled' => false, - // Optional (but recommended) cache file path: - 'cache_file' => './../../data/cache/fastroute.php.cache', - ], - ], - - 'routes' => [ /* ... */], -]; diff --git a/config/autoload/token.action.php b/config/autoload/token.action.php deleted file mode 100644 index 97d7be24..00000000 --- a/config/autoload/token.action.php +++ /dev/null @@ -1,20 +0,0 @@ - [ - 'refresh' => [ - 'key' => 'ixo>+W%!Rf/\@)m2UMok:/A_gL 'HS512', - 'duration' => 60 * 60 * 24 * 7 * 12, - 'iss' => 'localhost', - 'aud' => 'localhost', - ], - 'access' => [ - 'key' => 'b:?Y@5JCWF:yi{o>irc(3$HFcR-#b\SA', - 'algorithmus' => 'HS512', - 'duration' => 60 * 15, - 'iss' => 'localhost', - 'aud' => 'localhost', - ], - ], -]; diff --git a/config/autoload/token.develope.php b/config/autoload/token.develope.php index 97d7be24..f9d49b30 100644 --- a/config/autoload/token.develope.php +++ b/config/autoload/token.develope.php @@ -1,20 +1,18 @@ [ - 'refresh' => [ - 'key' => 'ixo>+W%!Rf/\@)m2UMok:/A_gL [ + 'auth' => [ + 'secret' => '7TtyzSrTbDFZXQ5Mjpj5xvv9iphO5oYy', 'algorithmus' => 'HS512', - 'duration' => 60 * 60 * 24 * 7 * 12, - 'iss' => 'localhost', - 'aud' => 'localhost', + 'duration' => 60 * 60, + 'refresh' => 24 * 60 * 60, ], - 'access' => [ - 'key' => 'b:?Y@5JCWF:yi{o>irc(3$HFcR-#b\SA', + 'csrf' => [ + 'secret' => 'Uq3FFwZEtK41LMF3pkJLoXlyJIzHjalT', 'algorithmus' => 'HS512', - 'duration' => 60 * 15, - 'iss' => 'localhost', - 'aud' => 'localhost', + 'duration' => 60 * 60, + 'refresh' => 24 * 60 * 60, ], ], ]; diff --git a/config/autoload/token.global.php b/config/autoload/token.global.php new file mode 100644 index 00000000..a6ad3c3e --- /dev/null +++ b/config/autoload/token.global.php @@ -0,0 +1,18 @@ + [ + 'auth' => [ + 'secret' => 'SYAyJL7CwbfH1fuXIiFNHQpuXzK0o1L3', + 'algorithmus' => 'HS512', + 'duration' => 60 * 60, + 'refresh' => 24 * 60 * 60, + ], + 'csrf' => [ + 'secret' => 'i97wWkFZcK43xcyfKXcyY4fkcqz3wavn', + 'algorithmus' => 'HS512', + 'duration' => 60 * 60, + 'refresh' => 24 * 60 * 60, + ], + ], +]; diff --git a/config/autoload/token.global.php.dist b/config/autoload/token.global.php.dist deleted file mode 100644 index ac8d71d3..00000000 --- a/config/autoload/token.global.php.dist +++ /dev/null @@ -1,20 +0,0 @@ - [ - 'refresh' => [ - 'key' => 'token secret', - 'algorithmus' => 'HS512', - 'duration' => 60 * 60 * 24 * 7 * 12, - 'iss' => 'Issuer of the token', - 'aud' => 'recipients of the token', - ], - 'access' => [ - 'key' =>'token secret', - 'algorithmus' => 'HS512', - 'duration' => 60 * 5, - 'iss' => 'Issuer of the token', - 'aud' => 'recipients of the token', - ], - ], -]; diff --git a/config/autoload/token.testing.php b/config/autoload/token.testing.php index 97d7be24..f9d49b30 100644 --- a/config/autoload/token.testing.php +++ b/config/autoload/token.testing.php @@ -1,20 +1,18 @@ [ - 'refresh' => [ - 'key' => 'ixo>+W%!Rf/\@)m2UMok:/A_gL [ + 'auth' => [ + 'secret' => '7TtyzSrTbDFZXQ5Mjpj5xvv9iphO5oYy', 'algorithmus' => 'HS512', - 'duration' => 60 * 60 * 24 * 7 * 12, - 'iss' => 'localhost', - 'aud' => 'localhost', + 'duration' => 60 * 60, + 'refresh' => 24 * 60 * 60, ], - 'access' => [ - 'key' => 'b:?Y@5JCWF:yi{o>irc(3$HFcR-#b\SA', + 'csrf' => [ + 'secret' => 'Uq3FFwZEtK41LMF3pkJLoXlyJIzHjalT', 'algorithmus' => 'HS512', - 'duration' => 60 * 15, - 'iss' => 'localhost', - 'aud' => 'localhost', + 'duration' => 60 * 60, + 'refresh' => 24 * 60 * 60, ], ], ]; diff --git a/config/config.php b/config/config.php index 49142d81..ac00f87a 100644 --- a/config/config.php +++ b/config/config.php @@ -1,6 +1,4 @@ - __DIR__ . '/../data/cache/config-cache.php', + 'config_cache_path' => 'data/cache/config-cache.php', ]; $aggregator = new ConfigAggregator([ - \Mezzio\Cors\ConfigProvider::class, - \Laminas\InputFilter\ConfigProvider::class, - \Laminas\Filter\ConfigProvider::class, - \Laminas\Validator\ConfigProvider::class, - \Mezzio\Helper\ConfigProvider::class, - \Mezzio\Tooling\ConfigProvider::class, - \Mezzio\Router\FastRouteRouter\ConfigProvider::class, - \Laminas\HttpHandlerRunner\ConfigProvider::class, + Mezzio\Helper\ConfigProvider::class, + Laminas\Log\ConfigProvider::class, + Laminas\InputFilter\ConfigProvider::class, + Laminas\Filter\ConfigProvider::class, + Laminas\Validator\ConfigProvider::class, + Laminas\Hydrator\ConfigProvider::class, + Mezzio\Tooling\ConfigProvider::class, + Mezzio\Router\FastRouteRouter\ConfigProvider::class, + Laminas\HttpHandlerRunner\ConfigProvider::class, // Include cache configuration new ArrayProvider($cacheConfig), - \Mezzio\ConfigProvider::class, - \Mezzio\Router\ConfigProvider::class, - \Laminas\Diactoros\ConfigProvider::class, + Mezzio\ConfigProvider::class, + Mezzio\Router\ConfigProvider::class, + Laminas\Diactoros\ConfigProvider::class, + // Swoole config to overwrite some services (if installed) - class_exists(\Mezzio\Swoole\ConfigProvider::class) - ? \Mezzio\Swoole\ConfigProvider::class + class_exists(Mezzio\Swoole\ConfigProvider::class) + ? Mezzio\Swoole\ConfigProvider::class : function (): array { return []; }, + // Default App module config - \Core\ConfigProvider::class, - \App\ConfigProvider::class, + App\ConfigProvider::class, + Core\ConfigProvider::class, + // Load application config in a pre-defined order in such a way that local settings // overwrite global settings. (Loaded as first to last): // - `global.php` @@ -47,6 +49,7 @@ class_exists(\Mezzio\Swoole\ConfigProvider::class) getenv('APP_ENV') ?: 'production' ) ), + // Load development config if it exists new PhpFileProvider(realpath(__DIR__) . '/development.config.php'), ], $cacheConfig['config_cache_path']); diff --git a/config/container.php b/config/container.php index b7358d18..34fa6f85 100644 --- a/config/container.php +++ b/config/container.php @@ -1,14 +1,10 @@ -pipe([ - ApiErrorHandlerMiddleware::class, - ServerUrlMiddleware::class, - BodyParamsMiddleware::class, + // The error handler should be the first (most outer) middleware to catch + // all Exceptions. + $app->pipe(ErrorHandler::class); + $app->pipe(HttpExceptionMiddleware::class); + $app->pipe(ServerUrlMiddleware::class); + $app->pipe(BodyParamsMiddleware::class); + + // Pipe more middleware here that you want to execute on every request: + // - bootstrapping + // - pre-conditions + // - modifications to outgoing responses + // + // Piped Middleware may be either callables or service names. Middleware may + // also be passed as an array; each item in the array must resolve to + // middleware eventually (i.e., callable or service name). + // + // Middleware can be attached to specific paths, allowing you to mix and match + // applications under a common domain. The handlers in each middleware + // attached this way will see a URI with the matched path segment removed. + // + // i.e., path of "/api/member/profile" only passes "/member/profile" to $apiMiddleware + // - $app->pipe('/api', $apiMiddleware); + // - $app->pipe('/docs', $apiDocMiddleware); + // - $app->pipe('/files', $filesMiddleware); - CorsMiddleware::class, - RouteMiddleware::class, + // Register the routing middleware in the middleware pipeline. + // This middleware registers the Mezzio\Router\RouteResult request attribute. + $app->pipe(RouteMiddleware::class); - ImplicitHeadMiddleware::class, - ImplicitOptionsMiddleware::class, - MethodNotAllowedMiddleware::class, + // The following handle routing failures for common conditions: + // - HEAD request but no routes answer that method + // - OPTIONS request but no routes answer that method + // - method not allowed + // Order here matters; the MethodNotAllowedMiddleware should be placed + // after the Implicit*Middleware. + $app->pipe(ImplicitHeadMiddleware::class); + $app->pipe(ImplicitOptionsMiddleware::class); + $app->pipe(MethodNotAllowedMiddleware::class); - UrlHelperMiddleware::class, + // Seed the UrlHelper with the routing results: + $app->pipe(UrlHelperMiddleware::class); - ClientIdentificationMiddleware::class, - RequestAuthenticationMiddleware::class, - LastAktivityUpdaterMiddleware::class, + // Add more middleware here that needs to introspect the routing results; this + // might include: + // + // - route-based authentication + // - route-based validation + // - etc. - DispatchMiddleware::class, + $app->pipe(ApiAccessMiddleware::class); + $app->pipe(JwtAuthenticationMiddleware::class); + $app->pipe(UpdateLastUserActionTimeMiddleware::class); - RouteNotFoundMiddleware::class, - NotFoundHandler::class, - ]); + // Register the dispatch middleware in the middleware pipeline + $app->pipe(DispatchMiddleware::class); + + // At this point, if no Response is returned by any middleware, the + // NotFoundHandler kicks in; alternately, you can provide other fallback + // middleware to execute. + $app->pipe(NotFoundHandler::class); }; + diff --git a/config/routes.php b/config/routes.php index 6299cde2..003d7552 100644 --- a/config/routes.php +++ b/config/routes.php @@ -1,91 +1,165 @@ get('/api/testmail', \App\Handler\System\TestMailHandler::class, \App\Handler\System\TestMailHandler::class); + $app->get('/api/ping[/]', \App\Handler\System\PingHandler::class, \App\Handler\System\PingHandler::class); + + /** ToDo OpenApi */ $app->get( - path: '/api/ping[/]', - middleware: [ - App\Handler\PingHandler::class, + '/api/event[/]', + [ + \App\Middleware\Event\EventListMiddleware::class, + \App\Handler\Event\EventListHandler::class, + ], + \App\Handler\Event\EventListHandler::class + ); + /** ToDo OpenApi */ + $app->post( + '/api/event[/]', + [ + \Core\Middleware\IsLoggedInAuthenticationMiddleware::class, + \App\Middleware\Event\EventCreateValidationMiddleware::class, + \App\Middleware\Event\EventCreateMiddleware::class, + \App\Handler\Event\EventCreateHandler::class, ], - name: RouteIdent::PING->value + \App\Handler\Event\EventCreateHandler::class ); + /** ToDo OpenApi */ $app->get( - path: '/api/token/refresh[/]', - middleware: [ - App\Middleware\Token\RefreshTokenValidationMiddleware::class, - App\Middleware\Token\RefreshTokenDatabaseExistenceMiddleware::class, - App\Middleware\Token\RefreshTokenMatchClientIdentificationMiddleware::class, - App\Middleware\Token\RefreshTokenAccountMiddleware::class, - App\Middleware\Token\GenerateAccessTokenMiddleware::class, - App\Handler\Account\AccessTokenHandler::class, + '/api/event/{eventId:\d+}[/]', + [ + \App\Middleware\Event\EventMiddleware::class, + \App\Handler\Event\EventHandler::class, ], - name: RouteIdent::ACCESS_TOKEN_REFRESH->value + \App\Handler\Event\EventHandler::class ); - $app->post( - path: '/api/account/authentication[/]', - middleware: [ - App\Middleware\Account\LoginAuthentication\AuthenticationConditionsMiddleware::class, - App\Middleware\Account\LoginAuthentication\AuthenticationValidationMiddleware::class, - App\Middleware\Account\LoginAuthentication\AuthenticationMiddleware::class, - App\Middleware\Token\GenerateRefreshTokenMiddleware::class, - App\Middleware\Token\GenerateAccessTokenMiddleware::class, - App\Middleware\Account\LoginAuthentication\PersistAuthenticationMiddleware::class, - App\Handler\Account\AuthenticationHandler::class, - ], - name: RouteIdent::ACCOUNT_AUTHENTICATE->value + /** ToDo OpenApi */ + $app->get( + '/api/event/{eventName}[/]', + [ + \App\Middleware\Event\EventNameMiddleware::class, + \App\Handler\Event\EventNameHandler::class, + ], + \App\Handler\Event\EventNameHandler::class ); - + /** ToDo OpenApi */ + $app->put( + '/api/event/participant/subscribe/{eventId:\d+}[/]', + [ + \Core\Middleware\IsLoggedInAuthenticationMiddleware::class, + \App\Middleware\Event\EventParticipantSubscribeMiddleware::class, + \App\Handler\Event\EventParticipantSubscribeHandler::class, + ], + \App\Handler\Event\EventParticipantSubscribeHandler::class + ); + /** ToDo OpenApi */ + $app->put( + '/api/event/participant/unsubscribe/{eventId:\d+}[/]', + [ + \Core\Middleware\IsLoggedInAuthenticationMiddleware::class, + \App\Middleware\Event\EventParticipantUnsubscribeMiddleware::class, + \App\Handler\Event\EventParticipantUnsubscribeHandler::class, + ], + \App\Handler\Event\EventParticipantUnsubscribeHandler::class + ); + /** ToDo OpenApi */ $app->post( - path: '/api/account', - middleware: [ - App\Middleware\Account\Validation\EmailInputValidatorMiddleware::class, - App\Middleware\Account\RegisterMiddleware::class, - App\Handler\Account\AccountRegisterHandler::class, + '/api/topic[/]', + [ + \Core\Middleware\IsLoggedInAuthenticationMiddleware::class, + \App\Middleware\Topic\TopicCreateValidationMiddleware::class, + \App\Middleware\Topic\TopicCreateSubmitMiddleware::class, + \App\Handler\Topic\TopicCreateHandler::class, ], - name: RouteIdent::ACCOUNT_CREATE->value + \App\Handler\Topic\TopicCreateHandler::class ); - + /** ToDo OpenApi */ + $app->get( + '/api/topics/available[/]', + [ + \Core\Middleware\IsLoggedInAuthenticationMiddleware::class, + \App\Middleware\Topic\TopicListAvailableMiddleware::class, + \App\Handler\Topic\TopicListAvailableHandler::class, + ], + \App\Handler\Topic\TopicListAvailableHandler::class + ); + /** ToDo OpenApi */ + $app->get( + '/api/user/me[/]', + [ + \App\Handler\System\ApiMeHandler::class, + ], + \App\Handler\System\ApiMeHandler::class + ); + /** ToDo OpenApi */ $app->post( - path: '/api/account/activation/[{token}[/]]', - middleware: [ - App\Middleware\Account\Validation\ActivationInputValidatorMiddleware::class, - App\Middleware\Account\ActivationMiddleware::class, - App\Handler\Account\AccountActivationHandler::class, + '/api/user/register[/]', + [ + \Core\Middleware\UserRegisterValidationMiddleware::class, + \Core\Middleware\UserRegisterMiddleware::class, + \Core\Handler\UserRegisterSubmitHandler::class, ], - name: RouteIdent::ACCOUNT_ACTIVATION->value + \Core\Handler\UserRegisterSubmitHandler::class ); - + /** ToDo OpenApi */ + $app->get( + '/api/user/{userUuid}[/]', + [ + \Core\Middleware\IsLoggedInAuthenticationMiddleware::class, + \Core\Middleware\UserMiddleware::class, + \Core\Handler\UserHandler::class, + ], + \Core\Handler\UserHandler::class + ); + /** ToDo OpenApi */ $app->post( - path: '/api/account/password/forgotten[/]', - middleware: [ - App\Middleware\Account\Validation\EmailInputValidatorMiddleware::class, - App\Middleware\Account\PasswordForgottenMiddleware::class, - App\Handler\Account\AccountPasswordForgottenHandler::class, + '/api/login[/]', + [ + \Core\Middleware\LoginValidationMiddleware::class, + \Core\Middleware\LoginAuthenticationMiddleware::class, + \Core\Handler\LoginHandler::class, ], - name: RouteIdent::ACCOUNT_PASSWORD_FORGOTTEN->value + \Core\Handler\LoginHandler::class ); - - $app->patch( - path: '/api/account/password/[{token}[/]]', - middleware: [ - App\Middleware\Account\Validation\PasswordInputValidatorMiddleware::class, - App\Middleware\Account\PasswordChangeMiddleware::class, - App\Handler\Account\AccountPasswordHandler::class, + /** ToDo OpenApi */ + $app->get( + '/api/logout[/]', + [ + \Core\Handler\LogoutHandler::class, ], - name: RouteIdent::ACCOUNT_PASSWORD_SET->value + \Core\Handler\LogoutHandler::class ); - + /** ToDo OpenApi */ + $app->post( + '/api/user/password/forgotten', + [ + \Core\Middleware\UserPasswordForgottenValidator::class, + \Core\Middleware\UserPasswordForgottenMiddleware::class, + \Core\Handler\UserPasswordForgottonHandler::class, + ], + \Core\Handler\UserPasswordForgottonHandler::class + ); + /** ToDo OpenApi */ $app->get( - path: '/api/account/logout', - middleware: [ - App\Middleware\Token\AccessTokenValidationMiddleware::class, - App\Middleware\Account\LogoutMiddleware::class, - App\Handler\Account\LogoutHandler::class, + '/api/user/password/{token}[/]', + [ + \Core\Middleware\UserPasswordVerifyTokenMiddleware::class, + \Core\Handler\UserPasswordVerifyTokenHandler::class, + ], + \Core\Handler\UserPasswordVerifyTokenHandler::class + ); + /** ToDo OpenApi */ + $app->post( + '/api/user/password/{token}[/]', + [ + \Core\Middleware\UserPasswordVerifyTokenMiddleware::class, + \Core\Middleware\UserPasswordChangeValidatorMiddleware::class, + \Core\Middleware\UserPasswordChangeMiddleware::class, + \Core\Handler\UserPasswordChangeHandler::class, ], - name: RouteIdent::ACCOUNT_LOGOUT->value + \Core\Handler\UserPasswordChangeHandler::class ); }; diff --git a/constants.php b/constants.php index c4e994f0..79cd618c 100644 --- a/constants.php +++ b/constants.php @@ -1,5 +1,3 @@ createTable('Role'); + + $table->addColumn('id', Types::INTEGER, ['autoincrement' => false, 'unsigned' => true,]); + $table->addColumn('uuid', Types::STRING, ['length' => 32,]); + $table->addColumn('name', Types::STRING, ['length' => 50,]); + $table->addColumn('description', Types::TEXT, ['default' => '',]); + + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['name'], 'role_name_UNIQUE'); + + } + + public function down(Schema $schema): void + { + $schema->dropTable('Role'); + } +} diff --git a/database/migrations/Version20220921183501CreateUserTable.php b/database/migrations/Version20220921183501CreateUserTable.php new file mode 100644 index 00000000..054612bf --- /dev/null +++ b/database/migrations/Version20220921183501CreateUserTable.php @@ -0,0 +1,42 @@ +createTable('User'); + + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); + $table->addColumn('uuid', Types::STRING, ['length' => 32,]); + $table->addColumn('roleId', Types::INTEGER, ['unsigned' => true, 'default' => 1]); + $table->addColumn('name', Types::STRING, ['length' => 50,]); + $table->addColumn('password', Types::STRING, ['length' => 255]); + $table->addColumn('email', Types::STRING, ['length' => 512]); + $table->addColumn('registrationAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); + $table->addColumn('lastActionAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); + + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['uuid'], 'user_uuid_UNIQUE'); + $table->addUniqueIndex(['name'], 'user_name_UNIQUE'); + $table->addUniqueIndex(['email'], 'user_email_UNIQUE'); + + $schema->getTable('User')->addForeignKeyConstraint( + 'Role', + ['roleId'], + ['id'], + name: 'fk_User_Role_idx' + ); + } + + public function down(Schema $schema): void + { + $schema->getTable('User')->dropIndex('fk_User_Role_idx'); + $schema->dropTable('User'); + } +} diff --git a/database/migrations/Version20220921184501CreateEventTable.php b/database/migrations/Version20220921184501CreateEventTable.php new file mode 100644 index 00000000..2f7482f5 --- /dev/null +++ b/database/migrations/Version20220921184501CreateEventTable.php @@ -0,0 +1,43 @@ +createTable('Event'); + + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); + $table->addColumn('uuid', Types::STRING, ['length' => 32]); + $table->addColumn('userId', Types::INTEGER, ['unsigned' => true,]); + $table->addColumn('title', Types::STRING, ['length' => 255]); + $table->addColumn('description', Types::STRING, ['length' => 1024, 'default' => '']); + $table->addColumn('eventText', Types::TEXT); + $table->addColumn('createdAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP']); + $table->addColumn('startedAt', Types::DATETIME_IMMUTABLE); + $table->addColumn('duration', Types::SMALLINT, ['unsigned' => true,]); + $table->addColumn('status', Types::SMALLINT, ['unsigned' => true, 'default' => 1,]); + $table->addColumn('ratingCompleted', Types::BOOLEAN, ['default' => false]); + + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['title'], 'event_title_UNIQUE'); + + $schema->getTable('Event')->addForeignKeyConstraint( + 'User', + ['userId'], + ['id'], + name: 'fk_Event_User_idx' + ); + } + + public function down(Schema $schema): void + { + $schema->getTable('Event')->dropIndex('fk_Event_User_idx'); + $schema->dropTable('Event'); + } +} diff --git a/database/migrations/Version20220921195606CreateTopicPoolTable.php b/database/migrations/Version20220921195606CreateTopicPoolTable.php new file mode 100644 index 00000000..52f5bb9d --- /dev/null +++ b/database/migrations/Version20220921195606CreateTopicPoolTable.php @@ -0,0 +1,39 @@ +createTable('TopicPool'); + + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); + $table->addColumn('uuid', Types::STRING, ['length' => 32,]); + $table->addColumn('eventId', Types::INTEGER, ['unsigned' => true, 'notnull' => false,]); + $table->addColumn('topic', Types::STRING, ['length' => 512, 'notnull' => false]); + $table->addColumn('description', Types::TEXT, ['default' => '',]); + $table->addColumn('accepted', Types::BOOLEAN, ['default' => NULL, 'notnull' => false,]); + + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['uuid'], 'topicpool_uuid_UNIQUE'); + $table->addUniqueIndex(['topic'], 'topicpool_topic_UNIQUE'); + + $schema->getTable('TopicPool')->addForeignKeyConstraint( + 'Event', + ['eventId'], + ['id'], + name: 'fk_TopicPool_Event_idx' + ); + } + + public function down(Schema $schema): void + { + $schema->getTable('TopicPool')->dropIndex('fk_TopicPool_Event_idx'); + $schema->dropTable('TopicPool'); + } +} diff --git a/database/migrations/Version20220921200325CreateParticipantTable.php b/database/migrations/Version20220921200325CreateParticipantTable.php new file mode 100644 index 00000000..96ec6779 --- /dev/null +++ b/database/migrations/Version20220921200325CreateParticipantTable.php @@ -0,0 +1,45 @@ +createTable('Participant'); + + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); + $table->addColumn('userId', Types::INTEGER, ['unsigned' => true,]); + $table->addColumn('eventId', Types::INTEGER, ['unsigned' => true,]); + $table->addColumn('requestedAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); + $table->addColumn('subscribed', Types::BOOLEAN, ['default' => true,]); + $table->addColumn('disqualified', Types::BOOLEAN, ['default' => false,]); + + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['userId', 'eventId'], 'UNIQUE_USER_EVENT'); + + $schema->getTable('Participant')->addForeignKeyConstraint( + 'Event', + ['eventId'], + ['id'], + name: 'fk_Participant_Event_idx' + ); + $schema->getTable('Participant')->addForeignKeyConstraint( + 'User', + ['userId'], + ['id'], + name: 'fk_Participant_User_idx' + ); + } + + public function down(Schema $schema): void + { + $schema->getTable('Participant')->dropIndex('fk__Participant_User_idx'); + $schema->getTable('Participant')->dropIndex('fk_Participant_Event_idx'); + $schema->dropTable('Participant'); + } +} diff --git a/database/migrations/Version20220921200425CreateProjectTable.php b/database/migrations/Version20220921200425CreateProjectTable.php new file mode 100644 index 00000000..4bfbe382 --- /dev/null +++ b/database/migrations/Version20220921200425CreateProjectTable.php @@ -0,0 +1,40 @@ +createTable('Project'); + + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); + $table->addColumn('uuid', Types::STRING, ['length' => 32,]); + $table->addColumn('participantId', Types::INTEGER, ['unsigned' => true,]); + $table->addColumn('title', Types::STRING, ['length' => 512]); + $table->addColumn('description', Types::TEXT); + $table->addColumn('createdAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); + $table->addColumn('gitRepoUri', Types::STRING, ['length' => 2083, 'default' => '',]); + $table->addColumn('demoPageUri', Types::STRING, ['length' => 2083, 'default' => '',]); + + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['title'], 'project_title_UNIQUE'); + + $schema->getTable('Project')->addForeignKeyConstraint( + 'Participant', + ['participantId'], + ['id'], + name: 'fk_Project_Participant_idx' + ); + } + + public function down(Schema $schema): void + { + $schema->getTable('Project')->dropIndex('fk_Project_Participant_idx'); + $schema->dropTable('Project'); + } +} diff --git a/database/migrations/Version20220921210101CreateGarbageTableTable.php b/database/migrations/Version20220921210101CreateGarbageTableTable.php new file mode 100644 index 00000000..bb4f59b9 --- /dev/null +++ b/database/migrations/Version20220921210101CreateGarbageTableTable.php @@ -0,0 +1,24 @@ +createTable('GarbageTable'); + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); + $table->addColumn('name', Types::STRING, ['length' => 50,]); + + $table->setPrimaryKey(['id']); + } + + public function down(Schema $schema): void + { + $schema->dropTable('GarbageTable'); + } +} diff --git a/database/migrations/Version20220921210102CreateGarbageReasonTypesTable.php b/database/migrations/Version20220921210102CreateGarbageReasonTypesTable.php new file mode 100644 index 00000000..db57cc6f --- /dev/null +++ b/database/migrations/Version20220921210102CreateGarbageReasonTypesTable.php @@ -0,0 +1,24 @@ +createTable('GarbageReasonType'); + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); + $table->addColumn('name', Types::STRING, ['length' => 50,]); + + $table->setPrimaryKey(['id']); + } + + public function down(Schema $schema): void + { + $schema->dropTable('GarbageReasonType'); + } +} diff --git a/database/migrations/Version20220921210259CreateGarbageContainerTable.php b/database/migrations/Version20220921210259CreateGarbageContainerTable.php new file mode 100644 index 00000000..ecd31e38 --- /dev/null +++ b/database/migrations/Version20220921210259CreateGarbageContainerTable.php @@ -0,0 +1,48 @@ +createTable('GarbageContainer'); + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); + $table->addColumn('userId', Types::INTEGER, ['unsigned' => true,]); + $table->addColumn('tableId', Types::INTEGER, ['unsigned' => true,]); + $table->addColumn('referenceId', Types::INTEGER, ['unsigned' => true,]); + $table->addColumn('reasonId', Types::INTEGER, ['unsigned' => true,]); + $table->addColumn('userDefinedReason', Types::STRING, ['length' => 255, 'default' => '',]); + $table->addColumn('deletedAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); + + $table->setPrimaryKey(['id']); + + $schema->getTable('GarbageContainer')->addForeignKeyConstraint( + 'Role', + ['userId'], + ['id'], + name: 'fk_GarbageContainer_User_idx' + ); + $schema->getTable('GarbageContainer')->addForeignKeyConstraint( + 'GarbageTable', + ['tableId'], + ['id'], + name: 'fk_GarbageContainer_GarbageTable_idx' + ); + $schema->getTable('GarbageContainer')->addForeignKeyConstraint( + 'GarbageReasonType', + ['reasonId'], + ['id'], + name: 'fk_GarbageContainer_GarbageReasonType_idx' + ); + } + + public function down(Schema $schema): void + { + $schema->dropTable('GarbageContainer'); + } +} diff --git a/database/migrations/Version20231103224046_CreateAccountActivationTable.php b/database/migrations/Version20220921220101CreateUserActivationTable.php similarity index 51% rename from database/migrations/Version20231103224046_CreateAccountActivationTable.php rename to database/migrations/Version20220921220101CreateUserActivationTable.php index a2ef6c01..b523058b 100644 --- a/database/migrations/Version20231103224046_CreateAccountActivationTable.php +++ b/database/migrations/Version20220921220101CreateUserActivationTable.php @@ -6,23 +6,22 @@ use Doctrine\DBAL\Types\Types; use Doctrine\Migrations\AbstractMigration; -final class Version20231103224046_CreateAccountActivationTable extends AbstractMigration +final class Version20220921220101CreateUserActivationTable extends AbstractMigration { public function up(Schema $schema): void { - $table = $schema->createTable('AccountActivation'); - + $table = $schema->createTable('UserActivation'); $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); - $table->addColumn('email', Types::STRING, ['length' => 512,]); + $table->addColumn('userId', Types::INTEGER, ['unsigned' => true,]); $table->addColumn('token', Types::STRING, ['length' => 32,]); - $table->addColumn('createdAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); + $table->addColumn('activationRequestTime', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); $table->setPrimaryKey(['id']); - $table->addUniqueIndex(['token'], 'account_activation_token_UNIQUE'); + $table->addUniqueIndex(['userId'], 'user_activation_userid_UNIQUE'); } public function down(Schema $schema): void { - $schema->dropTable('AccountActivation'); + $schema->dropTable('UserActivation'); } } diff --git a/database/migrations/Version20220921220156InsertDefaultsToRoleTable.php b/database/migrations/Version20220921220156InsertDefaultsToRoleTable.php new file mode 100644 index 00000000..27b32ac9 --- /dev/null +++ b/database/migrations/Version20220921220156InsertDefaultsToRoleTable.php @@ -0,0 +1,35 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $sql = <<addSql($sql); + } +} diff --git a/database/migrations/Version20231103223745_CreateAccountTable.php b/database/migrations/Version20231103223745_CreateAccountTable.php deleted file mode 100644 index 8b04c336..00000000 --- a/database/migrations/Version20231103223745_CreateAccountTable.php +++ /dev/null @@ -1,33 +0,0 @@ -createTable('Account'); - - $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); - $table->addColumn('uuid', Types::STRING, ['length' => 32,]); - $table->addColumn('name', Types::STRING, ['length' => 64, 'notnull' => false,]); - $table->addColumn('password', Types::STRING, ['length' => 255, 'notnull' => false,]); - $table->addColumn('email', Types::STRING, ['length' => 512,]); - $table->addColumn('registeredAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); - $table->addColumn('lastActionAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); - - $table->setPrimaryKey(['id']); - $table->addUniqueIndex(['uuid'], 'account_uuid_UNIQUE'); - $table->addUniqueIndex(['name'], 'account_name_UNIQUE'); - $table->addUniqueIndex(['email'], 'account_email_UNIQUE'); - } - - public function down(Schema $schema): void - { - $schema->dropTable('Account'); - } -} diff --git a/database/migrations/Version20231103224045_CreateAccountAccessAuthTable.php b/database/migrations/Version20231103224045_CreateAccountAccessAuthTable.php deleted file mode 100644 index 48576a70..00000000 --- a/database/migrations/Version20231103224045_CreateAccountAccessAuthTable.php +++ /dev/null @@ -1,32 +0,0 @@ -createTable('AccountAccessAuth'); - - $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); - $table->addColumn('accountId', Types::INTEGER, ['unsigned' => true,]); - $table->addColumn('label', Types::STRING, ['length' => 64, 'default' => 'default']); - $table->addColumn('refreshToken', Types::STRING, ['length' => 512,]); - $table->addColumn('userAgent', Types::STRING, ['length' => 255, 'default' => 'unknown']); - $table->addColumn('clientIdentHash', Types::STRING, ['length' => 128,]); - $table->addColumn('createdAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); - - $table->setPrimaryKey(['id']); - $table->addUniqueIndex(['refreshToken'], 'account_access_auth_refresh_token_UNIQUE'); - $table->addUniqueIndex(['clientIdentHash'], 'account_access_auth_client_ident_hash_UNIQUE'); - } - - public function down(Schema $schema): void - { - $schema->dropTable('AccountAccessAuth'); - } -} diff --git a/database/migrations/Version20231103224047_CreateTokenTable.php b/database/migrations/Version20231103224047_CreateTokenTable.php deleted file mode 100644 index 026030d9..00000000 --- a/database/migrations/Version20231103224047_CreateTokenTable.php +++ /dev/null @@ -1,29 +0,0 @@ -createTable('Token'); - - $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true,]); - $table->addColumn('accountId', Types::INTEGER, ['unsigned' => true,]); - $table->addColumn('token', Types::STRING, ['length' => 32,]); - $table->addColumn('tokenType', Types::SMALLINT, ['unsigned' => true, 'length' => 2,]); - $table->addColumn('createdAt', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP',]); - - $table->setPrimaryKey(['id']); - $table->addUniqueIndex(['token'], 'token_token_UNIQUE'); - } - - public function down(Schema $schema): void - { - $schema->dropTable('Token'); - } -} diff --git a/docker-compose.yml b/docker-compose.yml index 13d8053d..1496ee78 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,6 @@ services: container_name: hackathon-php user: "${USERMAP_UID:-1000}:${USERMAP_GID:-1000}" image: "ghcr.io/ownhackathon/hackathon-api-php:latest" - env_file: ".env" volumes: - ./:/var/www/html - ./docker/php/php-ini-overrides.ini:/usr/local/etc/php/conf.d/extra.ini @@ -34,28 +33,14 @@ services: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_general_ci - database-testing: - image: mariadb:${MARIADB_VERSION:-latest} - container_name: hackathon-mariadb-testing - ports: - - "${MYSQL_TESTING_PORT:-3307}:3306" - environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root} - MYSQL_DATABASE: ${MYSQL_DATABASE:-db} - MYSQL_USER: ${MYSQL_USER:-dev} - MYSQL_PASSWORD: ${MYSQL_PASSWORD:-dev} - volumes: - - db-testing:/var/lib/mysql - mailhog: image: mailhog/mailhog container_name: hackathon-mailhog logging: driver: 'none' # disable saving logs ports: - - "${MAILHOG_SMTP_PORT:-1025}:1025" # smtp server - - "${MAILHOG_WEBUI_PORT:-8025}:8025" # web ui + - "1025:1025" # smtp server + - "8025:8025" # web ui volumes: db: - db-testing: diff --git a/docker/php/php-ini-overrides.ini b/docker/php/php-ini-overrides.ini index 92aa2697..68c41dad 100644 --- a/docker/php/php-ini-overrides.ini +++ b/docker/php/php-ini-overrides.ini @@ -5,4 +5,3 @@ xdebug.discover_client_host=On error_reporting=E_ALL error_log=/var/log/php.log sendmail_path = /usr/local/bin/mhsendmail -memory_limit = 1024M diff --git a/phpcs.xml b/phpcs.xml index 09590230..a8ea11aa 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,43 +1,19 @@ - - - PSR-12 coding standard with Slevomat enhancements and custom declare-line formatting - - - - + + - - - - - - src/ - - - */vendor/* - */config/* - */database/* - */ConfigProvider.php - - - + - - + + - - - - - + + @@ -45,30 +21,22 @@ - - - 0 - + - - - - - - - - + - + @@ -76,13 +44,14 @@ - - 0 - + src - - + */vendor/* + */config/* + */database/* + + @@ -116,10 +85,16 @@ - + + + - + + + + + @@ -132,8 +107,6 @@ - - @@ -160,7 +133,6 @@ - @@ -179,4 +151,5 @@ + diff --git a/phpstan.neon b/phpstan.neon index e417e175..96cea727 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,18 +1,11 @@ parameters: level: 6 - phpVersion: 80400 paths: - src ignoreErrors: - identifier: missingType.iterableValue - identifier: missingType.generics bootstrapFiles: - - constants.php - checkUninitializedProperties: true - tips: - treatPhpDocTypesAsCertain: false -rules: - - PHPStan\Rules\Functions\ReturnNullsafeByRefRule + - constants.php includes: - vendor/phpstan/phpstan-deprecation-rules/rules.neon - - vendor/phpstan/phpstan/conf/bleedingEdge.neon diff --git a/phpunit_functionaltest.xml b/phpunit_functionaltest.xml index c6c36a42..d624e515 100644 --- a/phpunit_functionaltest.xml +++ b/phpunit_functionaltest.xml @@ -1,32 +1,20 @@ - - tests/FunctionalTest + + tests/Functional - - - - - - - - - src - - diff --git a/phpunit_unittest.xml b/phpunit_unittest.xml index 2bf52408..927c3dfd 100644 --- a/phpunit_unittest.xml +++ b/phpunit_unittest.xml @@ -1,7 +1,7 @@ - - tests/UnitTest/AppTest - - - tests/UnitTest/CoreTest - - - tests/UnitTest/GameTest + + tests/Unit - + diff --git a/public/api/docs/index.html b/public/api/doc/index.html similarity index 62% rename from public/api/docs/index.html rename to public/api/doc/index.html index da994d3e..39bbd0fb 100644 --- a/public/api/docs/index.html +++ b/public/api/doc/index.html @@ -7,18 +7,13 @@ name="description" content="SwaggerUI" /> - ownHackathon - SwaggerUI - - + Hackathon API Overview +
- - + + diff --git a/public/api/docs/swagger.json b/public/api/docs/swagger.json deleted file mode 100644 index 328503d2..00000000 --- a/public/api/docs/swagger.json +++ /dev/null @@ -1,466 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "ownHackathon API Overview", - "version": "0.1.0" - }, - "servers": [ - { - "url": "/api" - } - ], - "paths": { - "/token/refresh": { - "get": { - "tags": [ - "Account" - ], - "summary": "Return of a new access token", - "operationId": "1ee7f36021506322a0e202b1fb32f278", - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AccessToken" - } - } - } - }, - "401": { - "description": "Unauthorized access", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HttpResponseMessage" - } - } - } - } - } - } - }, - "/account/activation/{token}": { - "post": { - "tags": [ - "Account" - ], - "summary": "Activate the account", - "operationId": "db81d65a9e026816e8f2ea40f73a394b", - "parameters": [ - { - "name": "token", - "in": "path", - "description": "Token", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "E-Mail for register Account", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AccountRegistration" - } - } - } - }, - "responses": { - "200": { - "description": "Success" - }, - "400": { - "description": "Bad request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HttpResponseMessage" - } - } - } - } - } - } - }, - "/account/password/forgotten": { - "post": { - "tags": [ - "Account" - ], - "summary": "Creates a token for password reset. Sending via E-Mail", - "operationId": "faec81c539d68fbab3b442b162ad7c84", - "requestBody": { - "description": "Set Password for a Account", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EMail" - } - } - } - }, - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/account/password/{token}": { - "patch": { - "tags": [ - "Account" - ], - "summary": "Set Password to the account", - "operationId": "c5f1c414d69d6f318ab519d1746570e4", - "parameters": [ - { - "name": "token", - "in": "path", - "description": "Token", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "Set Password for a Account", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AccountPassword" - } - } - } - }, - "responses": { - "200": { - "description": "Success" - }, - "400": { - "description": "Bad request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HttpResponseMessage" - } - } - } - } - } - } - }, - "/account": { - "post": { - "tags": [ - "Account" - ], - "summary": "Endpoint to register a new user account.", - "description": "Create Account", - "operationId": "8a212b9a005ff2dd5aeea1756d25bd05", - "requestBody": { - "description": "The email address for the new account", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EMail" - } - } - } - }, - "responses": { - "200": { - "description": "Success" - }, - "400": { - "description": "Bad request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HttpResponseMessage" - } - } - } - } - } - } - }, - "/account/authentication": { - "post": { - "tags": [ - "Account" - ], - "summary": "Attempts to log in an account using transferred data", - "operationId": "4573a8211c6a27b677250eb46ca168ee", - "requestBody": { - "description": "Account data for authentication", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AccountAuthenticationData" - } - } - } - }, - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuthenticationResponse" - } - } - } - }, - "401": { - "description": "Unauthorized access", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HttpResponseMessage" - } - } - } - }, - "403": { - "description": "Access denied", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HttpResponseMessage" - } - } - } - } - } - } - }, - "/account/logout": { - "get": { - "tags": [ - "Account" - ], - "summary": "Attempts to log out an account", - "operationId": "1154528e1da5f90aa3b356c63502b533", - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuthenticationResponse" - } - } - } - }, - "401": { - "description": "Unauthorized access", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HttpResponseMessage" - } - } - } - } - } - } - }, - "/ping": { - "get": { - "tags": [ - "System Information" - ], - "summary": "Returns the current time in Unix format", - "operationId": "ffea4e020f3a4868f9f018e280509330", - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "properties": { - "ack": { - "description": "actually request time", - "type": "string" - } - }, - "type": "object" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "AccountAuthenticationData": { - "required": [ - "email", - "password" - ], - "properties": { - "email": { - "description": "The E-Mail from Account", - "type": "string" - }, - "password": { - "description": "The Password from Account", - "type": "string" - } - }, - "type": "object" - }, - "AccountPassword": { - "properties": { - "password": { - "description": "The Password", - "type": "string" - } - }, - "type": "object" - }, - "AccountRegistration": { - "required": [ - "accountName", - "password" - ], - "properties": { - "accountName": { - "description": "The Display Name for the Account", - "type": "string" - }, - "password": { - "description": "The Password", - "type": "string" - } - }, - "type": "object" - }, - "EMail": { - "properties": { - "email": { - "description": "The E-Mail", - "type": "string" - } - }, - "type": "object" - }, - "AuthenticationResponse": { - "properties": { - "accessToken": { - "description": "The access token after a valid log-in", - "type": "string" - }, - "refreshToken": { - "description": "The refresh token after a valid log-in", - "type": "string" - } - }, - "type": "object" - }, - "HttpResponseMessage": { - "properties": { - "statusCode": { - "description": "The Http Status Code", - "type": "integer", - "example": 400 - }, - "message": { - "description": "The Message", - "type": "string", - "example": "Bad request" - } - }, - "type": "object" - }, - "AccessToken": { - "properties": { - "accessToken": { - "description": "The Token for authorized access", - "type": "string" - } - }, - "type": "object" - }, - "AccountPasswordToken": { - "properties": { - "accountPasswordToken": { - "description": "Token to set a new password", - "type": "string" - } - }, - "type": "object" - }, - "RefreshToken": { - "properties": { - "refreshToken": { - "description": "The token after a valid log-in", - "type": "string" - } - }, - "type": "object" - }, - "Token": { - "properties": { - "token": { - "description": "The Token", - "type": "string" - } - }, - "type": "object" - } - }, - "securitySchemes": { - "accessToken": { - "type": "apiKey", - "name": "Authorization", - "in": "header" - }, - "refreshToken": { - "type": "apiKey", - "name": "Authentication", - "in": "header" - }, - "Client-Identification-String": { - "type": "apiKey", - "name": "x-ident", - "in": "header" - } - } - }, - "security": [ - { - "accessToken": [] - }, - { - "Client-Identification-String": [] - }, - { - "refreshToken": [] - } - ], - "tags": [ - { - "name": "Account", - "description": "Account" - }, - { - "name": "System Information", - "description": "System Information" - } - ] -} \ No newline at end of file diff --git a/public/api/index.php b/public/api/index.php index 2421c2e6..af523c13 100644 --- a/public/api/index.php +++ b/public/api/index.php @@ -12,7 +12,7 @@ chdir(dirname(__DIR__)); require './../vendor/autoload.php'; -if (file_exists('./../.env')) { +if (file_exists('./../.env')){ putenv('APP_ENV=develope'); } diff --git a/src/App/ConfigProvider.php b/src/App/ConfigProvider.php index 407a94f0..68db57e7 100644 --- a/src/App/ConfigProvider.php +++ b/src/App/ConfigProvider.php @@ -2,39 +2,42 @@ namespace App; +use App\Middleware\Event\EventCreateMiddlewareFactory; +use App\Repository\EventRepository; +use App\Repository\ParticipantRepository; +use App\Repository\ProjectRepository; +use App\Repository\TopicPoolRepository; +use App\Service\EMail\TopicCreateEMailService; +use App\Service\EMail\TopicCreateEMailServiceFactory; +use App\Service\Event\EventService; +use App\Service\Event\EventServiceFactory; +use App\Service\Participant\ParticipantService; +use App\Service\Participant\ParticipantServiceFactory; +use App\Service\Project\ProjectService; +use App\Service\Project\ProjectServiceFactory; +use App\Service\Topic\TopicPoolService; +use App\Service\Topic\TopicPoolServiceFactory; +use App\Service\User\UserService; +use App\Service\User\UserServiceFactory; +use App\Table\EventTable; +use App\Table\ParticipantTable; +use App\Table\ProjectTable; +use App\Table\TopicPoolTable; +use App\Validator\EventCreateValidator; +use App\Validator\Input\Event\EventDescriptionInput; +use App\Validator\Input\Event\EventDurationInput; +use App\Validator\Input\Event\EventStartTimeInput; +use App\Validator\Input\Event\EventTextInput; +use App\Validator\Input\Event\EventTitleInput; +use App\Validator\Input\Topic\TopicDescriptionInput; +use App\Validator\Input\Topic\TopicInput; +use App\Validator\TopicCreateValidator; +use Core\Hydrator\ReflectionHydrator; +use Core\Service\LoginAuthenticationService; use Envms\FluentPDO\Query; use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory; -use Laminas\ServiceManager\Factory\InvokableFactory; -use App\Hydrator\AccountAccessAuthHydratorInterface; -use App\Hydrator\AccountActivationHydratorInterface; -use App\Hydrator\AccountHydratorInterface; -use App\Hydrator\TokenHydratorInterface; -use App\Service\Account\AccountService; -use App\Service\Authentication\AuthenticationService; -use App\Service\ClientIdentification\ClientIdentificationService; -use App\Service\Token\AccessTokenService; -use App\Service\Token\ActivationTokenService; -use App\Service\Token\PasswordTokenService; -use App\Service\Token\RefreshTokenService; -use App\Table\AccountAccessAuthTable; -use App\Table\AccountActivationTable; -use App\Table\AccountTable; -use App\Table\TokenTable; -use App\Validator\AccountActivationValidator; -use App\Validator\AuthenticationValidator; -use App\Validator\EMailValidator; -use App\Validator\Input\AccountNameInput; -use App\Validator\Input\EmailInput; -use App\Validator\Input\PasswordInput; -use App\Validator\PasswordValidator; -use Core\Repository\AccountAccessAuthRepositoryInterface; -use Core\Repository\AccountActivationRepositoryInterface; -use Core\Repository\AccountRepositoryInterface; -use Core\Repository\TokenRepositoryInterface; -use Core\Store; -use Core\Utils\UuidFactoryInterface; -use Psr\Log\LoggerInterface; -use Symfony\Component\Mailer\MailerInterface; +use Ramsey\Uuid\Uuid; +use Symfony\Component\Mailer\Mailer; class ConfigProvider { @@ -49,75 +52,58 @@ public function __invoke(): array public function getDependencies(): array { return [ - 'aliases' => [ - Hydrator\AccountAccessAuthHydratorInterface::class => Hydrator\AccountAccessAuthHydrator::class, - Hydrator\AccountActivationHydratorInterface::class => Hydrator\AccountActivationHydrator::class, - Hydrator\AccountHydratorInterface::class => Hydrator\AccountHydrator::class, - Hydrator\TokenHydratorInterface::class => Hydrator\TokenHydrator::class, - - AccountRepositoryInterface::class => Repository\AccountRepository::class, - AccountActivationRepositoryInterface::class => Repository\AccountActivationRepository::class, - AccountAccessAuthRepositoryInterface::class => Repository\AccountAccessAuthRepository::class, - TokenRepositoryInterface::class => Repository\TokenRepository::class, - - Store\AccountStoreInterface::class => AccountTable::class, - Store\AccountAccessAuthStoreInterface::class => AccountAccessAuthTable::class, - Store\AccountActivationStoreInterface::class => AccountActivationTable::class, - Store\TokenStoreInterface::class => TokenTable::class, - ], 'invokables' => [ + LoginAuthenticationService::class, + EventDescriptionInput::class, + EventDurationInput::class, + EventStartTimeInput::class, + EventTextInput::class, + EventTitleInput::class, + TopicDescriptionInput::class, + TopicInput::class, + ], + 'aliases' => [ + EventRepository::class => EventTable::class, + ParticipantRepository::class => ParticipantTable::class, + ProjectRepository::class => ProjectTable::class, + TopicPoolRepository::class => TopicPoolTable::class, ], 'factories' => [ - Hydrator\AccountAccessAuthHydrator::class => InvokableFactory::class, - Hydrator\AccountActivationHydrator::class => ConfigAbstractFactory::class, - Hydrator\AccountHydrator::class => ConfigAbstractFactory::class, - Hydrator\TokenHydrator::class => ConfigAbstractFactory::class, + Handler\Event\EventHandler::class => ConfigAbstractFactory::class, + Handler\Event\EventParticipantSubscribeHandler::class => ConfigAbstractFactory::class, + Handler\Topic\TopicCreateHandler::class => ConfigAbstractFactory::class, + Handler\System\TestMailHandler::class => ConfigAbstractFactory::class, + + Middleware\Event\EventCreateMiddleware::class => EventCreateMiddlewareFactory::class, + Middleware\Event\EventCreateValidationMiddleware::class => ConfigAbstractFactory::class, + Middleware\Event\EventParticipantSubscribeMiddleware::class => ConfigAbstractFactory::class, + Middleware\Event\EventParticipantUnsubscribeMiddleware::class => ConfigAbstractFactory::class, + Middleware\Event\EventMiddleware::class => ConfigAbstractFactory::class, + Middleware\Event\EventNameMiddleware::class => ConfigAbstractFactory::class, + Middleware\Event\EventListMiddleware::class => ConfigAbstractFactory::class, + Middleware\Project\ProjectMiddleware::class => ConfigAbstractFactory::class, + Middleware\Project\ProjectOwnerMiddleware::class => ConfigAbstractFactory::class, + Middleware\Project\ProjectParticipantMiddleware::class => ConfigAbstractFactory::class, + Middleware\Topic\TopicCreateValidationMiddleware::class => ConfigAbstractFactory::class, + Middleware\Topic\TopicEntryStatisticMiddleware::class => ConfigAbstractFactory::class, + Middleware\Topic\TopicListAvailableMiddleware::class => ConfigAbstractFactory::class, + Middleware\Topic\TopicListMiddleware::class => ConfigAbstractFactory::class, + Middleware\Topic\TopicCreateSubmitMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\LoginAuthentication\AuthenticationConditionsMiddleware::class => InvokableFactory::class, - Middleware\Account\LoginAuthentication\AuthenticationMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\LoginAuthentication\AuthenticationValidationMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\LoginAuthentication\PersistAuthenticationMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\Validation\ActivationInputValidatorMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\Validation\EmailInputValidatorMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\Validation\PasswordInputValidatorMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\ActivationMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\LastAktivityUpdaterMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\LogoutMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\PasswordChangeMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\PasswordForgottenMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\RegisterMiddleware::class => ConfigAbstractFactory::class, - Middleware\Account\RequestAuthenticationMiddleware::class => ConfigAbstractFactory::class, - Middleware\ClientIdentification\ClientIdentificationMiddleware::class => ConfigAbstractFactory::class, - Middleware\Token\AccessTokenValidationMiddleware::class => ConfigAbstractFactory::class, - Middleware\Token\GenerateAccessTokenMiddleware::class => ConfigAbstractFactory::class, - Middleware\Token\GenerateRefreshTokenMiddleware::class => ConfigAbstractFactory::class, - Middleware\Token\RefreshTokenAccountMiddleware::class => ConfigAbstractFactory::class, - Middleware\Token\RefreshTokenDatabaseExistenceMiddleware::class => ConfigAbstractFactory::class, - Middleware\Token\RefreshTokenMatchClientIdentificationMiddleware::class => InvokableFactory::class, - Middleware\Token\RefreshTokenValidationMiddleware::class => ConfigAbstractFactory::class, - Repository\AccountAccessAuthRepository::class => ConfigAbstractFactory::class, - Repository\AccountActivationRepository::class => ConfigAbstractFactory::class, - Repository\AccountRepository::class => ConfigAbstractFactory::class, - Repository\TokenRepository::class => ConfigAbstractFactory::class, - Service\Account\AccountService::class => ConfigAbstractFactory::class, - Service\Authentication\AuthenticationService::class => InvokableFactory::class, - Service\ClientIdentification\ClientIdentificationService::class => InvokableFactory::class, - Service\Token\AccessTokenService::class => Service\Token\AccessTokenServiceFactory::class, - Service\Token\ActivationTokenService::class => ConfigAbstractFactory::class, - Service\Token\PasswordTokenService::class => ConfigAbstractFactory::class, - Service\Token\RefreshTokenService::class => Service\Token\RefreshTokenServiceFactory::class, - Table\AccountAccessAuthTable::class => ConfigAbstractFactory::class, - Table\AccountActivationTable::class => ConfigAbstractFactory::class, - Table\AccountTable::class => ConfigAbstractFactory::class, - Table\TokenTable::class => ConfigAbstractFactory::class, + Service\EMail\TopicCreateEMailService::class => TopicCreateEMailServiceFactory::class, + Service\Event\EventService::class => EventServiceFactory::class, + Service\Participant\ParticipantService::class => ParticipantServiceFactory::class, + Service\Project\ProjectService::class => ProjectServiceFactory::class, + Service\Topic\TopicPoolService::class => TopicPoolServiceFactory::class, + Service\User\UserService::class => UserServiceFactory::class, - Validator\Input\AccountNameInput::class => InvokableFactory::class, - Validator\Input\EmailInput::class => InvokableFactory::class, - Validator\Input\PasswordInput::class => InvokableFactory::class, - Validator\AccountActivationValidator::class => ConfigAbstractFactory::class, - Validator\AuthenticationValidator::class => ConfigAbstractFactory::class, - Validator\EMailValidator::class => ConfigAbstractFactory::class, - Validator\PasswordValidator::class => ConfigAbstractFactory::class, + Table\EventTable::class => ConfigAbstractFactory::class, + Table\ParticipantTable::class => ConfigAbstractFactory::class, + Table\ProjectTable::class => ConfigAbstractFactory::class, + Table\TopicPoolTable::class => ConfigAbstractFactory::class, + + EventCreateValidator::class => ConfigAbstractFactory::class, + TopicCreateValidator::class => ConfigAbstractFactory::class, ], ]; } @@ -125,143 +111,92 @@ public function getDependencies(): array public function getAbstractFactoryConfig(): array { return [ - Hydrator\AccountActivationHydrator::class => [ - UuidFactoryInterface::class, - ], - Hydrator\AccountHydrator::class => [ - UuidFactoryInterface::class, - ], - Hydrator\TokenHydrator::class => [ - UuidFactoryInterface::class, - ], - - Middleware\Account\LoginAuthentication\AuthenticationMiddleware::class => [ - AuthenticationService::class, - AccountRepositoryInterface::class, - ], - Middleware\Account\LoginAuthentication\AuthenticationValidationMiddleware::class => [ - AuthenticationValidator::class, - ], - Middleware\Account\LoginAuthentication\PersistAuthenticationMiddleware::class => [ - AccountAccessAuthRepositoryInterface::class, + Handler\Event\EventHandler::class => [ + UserService::class, + ParticipantService::class, + ProjectService::class, + TopicPoolService::class, ], - Middleware\Account\Validation\ActivationInputValidatorMiddleware::class => [ - AccountActivationValidator::class, + Handler\Event\EventParticipantSubscribeHandler::class => [ + ParticipantService::class, + ProjectService::class, ], - Middleware\Account\Validation\EmailInputValidatorMiddleware::class => [ - EMailValidator::class, + Handler\Topic\TopicCreateHandler::class => [ + TopicCreateEMailService::class, ], - Middleware\Account\Validation\PasswordInputValidatorMiddleware::class => [ - PasswordValidator::class, + Handler\System\TestMailHandler::class => [ + Mailer::class, ], - Middleware\Account\ActivationMiddleware::class => [ - AccountActivationRepositoryInterface::class, - AccountRepositoryInterface::class, - UuidFactoryInterface::class, + Middleware\Event\EventCreateValidationMiddleware::class => [ + EventCreateValidator::class, ], - Middleware\Account\LastAktivityUpdaterMiddleware::class => [ - AccountRepositoryInterface::class, + Middleware\Event\EventListMiddleware::class => [ + EventService::class, + UserService::class, ], - Middleware\Account\LogoutMiddleware::class => [ - AccountAccessAuthRepositoryInterface::class, + Middleware\Event\EventMiddleware::class => [ + EventService::class, ], - Middleware\Account\PasswordChangeMiddleware::class => [ - AccountRepositoryInterface::class, - TokenRepositoryInterface::class, - AccountService::class, + Middleware\Event\EventNameMiddleware::class => [ + EventService::class, ], - Middleware\Account\PasswordForgottenMiddleware::class => [ - AccountService::class, + Middleware\Event\EventParticipantSubscribeMiddleware::class => [ + ParticipantService::class, + EventService::class, ], - Middleware\Account\RegisterMiddleware::class => [ - AccountService::class, - AccountActivationRepositoryInterface::class, - ActivationTokenService::class, - UuidFactoryInterface::class, - LoggerInterface::class, + Middleware\Event\EventParticipantUnsubscribeMiddleware::class => [ + ParticipantService::class, + EventService::class, ], - Middleware\Account\RequestAuthenticationMiddleware::class => [ - AccessTokenService::class, - AccountRepositoryInterface::class, - UuidFactoryInterface::class, - LoggerInterface::class, + Middleware\Project\ProjectMiddleware::class => [ + ProjectService::class, ], - Middleware\ClientIdentification\ClientIdentificationMiddleware::class => [ - ClientIdentificationService::class, + Middleware\Project\ProjectOwnerMiddleware::class => [ + UserService::class, ], - Middleware\Token\AccessTokenValidationMiddleware::class => [ - AccessTokenService::class, + Middleware\Project\ProjectParticipantMiddleware::class => [ + ParticipantService::class, ], - Middleware\Token\GenerateAccessTokenMiddleware::class => [ - AccessTokenService::class, + Middleware\Topic\TopicCreateValidationMiddleware::class => [ + TopicCreateValidator::class, ], - Middleware\Token\GenerateRefreshTokenMiddleware::class => [ - RefreshTokenService::class, + Middleware\Topic\TopicEntryStatisticMiddleware::class => [ + TopicPoolService::class, ], - Middleware\Token\RefreshTokenAccountMiddleware::class => [ - AccountRepositoryInterface::class, + Middleware\Topic\TopicListAvailableMiddleware::class => [ + TopicPoolService::class, ], - Middleware\Token\RefreshTokenDatabaseExistenceMiddleware::class => [ - AccountAccessAuthRepositoryInterface::class, + Middleware\Topic\TopicListMiddleware::class => [ + TopicPoolService::class, ], - Middleware\Token\RefreshTokenValidationMiddleware::class => [ - RefreshTokenService::class, + Middleware\Topic\TopicCreateSubmitMiddleware::class => [ + TopicPoolService::class, + ReflectionHydrator::class, + Uuid::class, ], - - Repository\AccountAccessAuthRepository::class => [ - Store\AccountAccessAuthStoreInterface::class, - ], - Repository\AccountActivationRepository::class => [ - Store\AccountActivationStoreInterface::class, - ], - Repository\AccountRepository::class => [ - Store\AccountStoreInterface::class, - ], - Repository\TokenRepository::class => [ - Store\TokenStoreInterface::class, - ], - - Service\Account\AccountService::class => [ - AccountRepositoryInterface::class, - TokenRepositoryInterface::class, - PasswordTokenService::class, - UuidFactoryInterface::class, - ], - Service\Token\ActivationTokenService::class => [ - MailerInterface::class, - ], - Service\Token\PasswordTokenService::class => [ - MailerInterface::class, - ], - Table\AccountAccessAuthTable::class => [ + Table\EventTable::class => [ Query::class, - AccountAccessAuthHydratorInterface::class, ], - Table\AccountActivationTable::class => [ + Table\ParticipantTable::class => [ Query::class, - AccountActivationHydratorInterface::class, ], - Table\AccountTable::class => [ + Table\ProjectTable::class => [ Query::class, - AccountHydratorInterface::class, ], - Table\TokenTable::class => [ + Table\TopicPoolTable::class => [ Query::class, - TokenHydratorInterface::class, - ], - Validator\AccountActivationValidator::class => [ - AccountNameInput::class, - PasswordInput::class, ], - Validator\AuthenticationValidator::class => [ - EmailInput::class, - PasswordInput::class, - ], - Validator\EMailValidator::class => [ - EmailInput::class, - ], - Validator\PasswordValidator::class => [ - PasswordInput::class, + + EventCreateValidator::class => [ + EventTitleInput::class, + EventDescriptionInput::class, + EventTextInput::class, + EventStartTimeInput::class, + EventDurationInput::class, + ], + TopicCreateValidator::class => [ + TopicInput::class, + TopicDescriptionInput::class, ], ]; } diff --git a/src/App/DTO/Account/AccountAuthenticationData.php b/src/App/DTO/Account/AccountAuthenticationData.php deleted file mode 100644 index 7c28a769..00000000 --- a/src/App/DTO/Account/AccountAuthenticationData.php +++ /dev/null @@ -1,28 +0,0 @@ -value, - )] - public string $email; - - #[OA\Property( - description: 'The Password from Account', - type: DataType::STRING->value, - )] - public string $password; - - public function __construct(string $email, string $password) - { - $this->email = $email; - $this->password = $password; - } -} diff --git a/src/App/DTO/Account/AccountPassword.php b/src/App/DTO/Account/AccountPassword.php deleted file mode 100644 index 51ddce68..00000000 --- a/src/App/DTO/Account/AccountPassword.php +++ /dev/null @@ -1,24 +0,0 @@ -value, - )] - public string $password, - ) { - } - - public static function fromString(string $password): self - { - return new self($password); - } -} diff --git a/src/App/DTO/Account/AccountRegistration.php b/src/App/DTO/Account/AccountRegistration.php deleted file mode 100644 index bb01d5ad..00000000 --- a/src/App/DTO/Account/AccountRegistration.php +++ /dev/null @@ -1,29 +0,0 @@ -value, - )] - public string $accountName, - #[OA\Property( - description: 'The Password', - type: DataType::STRING->value, - )] - public string $password, - ) { - } - - public static function fromString(string $accountName, string $password): self - { - return new self($accountName, $password); - } -} diff --git a/src/App/DTO/Client/ClientIdentification.php b/src/App/DTO/Client/ClientIdentification.php deleted file mode 100644 index 4a3f86e3..00000000 --- a/src/App/DTO/Client/ClientIdentification.php +++ /dev/null @@ -1,17 +0,0 @@ -value, - )] - public string $email, - ) { - } - - public static function fromString(string $email): self - { - return new self($email); - } -} diff --git a/src/App/DTO/Response/AuthenticationResponse.php b/src/App/DTO/Response/AuthenticationResponse.php deleted file mode 100644 index e3b5a4ff..00000000 --- a/src/App/DTO/Response/AuthenticationResponse.php +++ /dev/null @@ -1,31 +0,0 @@ -value, - )] - public string $accessToken, - #[OA\Property( - description: 'The refresh token after a valid log-in', - type: DataType::STRING->value, - )] - public string $refreshToken, - ) { - } - - public static function from(AccessToken $accessToken, RefreshToken $refreshToken): self - { - return new self($accessToken->accessToken, $refreshToken->refreshToken); - } -} diff --git a/src/App/DTO/Response/HttpResponseMessage.php b/src/App/DTO/Response/HttpResponseMessage.php deleted file mode 100644 index 67e91d96..00000000 --- a/src/App/DTO/Response/HttpResponseMessage.php +++ /dev/null @@ -1,33 +0,0 @@ -value, - example: HTTP::STATUS_BAD_REQUEST - )] - public int $statusCode, - #[OA\Property( - description: 'The Message', - type: DataType::STRING->value, - example: StatusMessage::BAD_REQUEST->value - )] - public StatusMessage $message, - ) { - } - - public static function create(int $statusCode, StatusMessage $message): self - { - return new self($statusCode, $message); - } -} diff --git a/src/App/DTO/Token/AccessToken.php b/src/App/DTO/Token/AccessToken.php deleted file mode 100644 index edb501bf..00000000 --- a/src/App/DTO/Token/AccessToken.php +++ /dev/null @@ -1,24 +0,0 @@ -value, - )] - public string $accessToken, - ) { - } - - public static function fromString(string $token): self - { - return new self($token); - } -} diff --git a/src/App/DTO/Token/AccountPasswordToken.php b/src/App/DTO/Token/AccountPasswordToken.php deleted file mode 100644 index ba94635e..00000000 --- a/src/App/DTO/Token/AccountPasswordToken.php +++ /dev/null @@ -1,24 +0,0 @@ -value, - )] - public string $accountPasswordToken, - ) { - } - - public static function fromString(string $accountPasswordToken): self - { - return new self($accountPasswordToken); - } -} diff --git a/src/App/DTO/Token/JwtTokenConfig.php b/src/App/DTO/Token/JwtTokenConfig.php deleted file mode 100644 index b31c5f6f..00000000 --- a/src/App/DTO/Token/JwtTokenConfig.php +++ /dev/null @@ -1,26 +0,0 @@ -value, - )] - public string $refreshToken, - ) { - } - - public static function fromString(string $token): self - { - return new self($token); - } -} diff --git a/src/App/DTO/Token/Token.php b/src/App/DTO/Token/Token.php deleted file mode 100644 index 92a75603..00000000 --- a/src/App/DTO/Token/Token.php +++ /dev/null @@ -1,24 +0,0 @@ -value, - )] - public string $token, - ) { - } - - public static function fromString(string $token): self - { - return new self($token); - } -} diff --git a/src/App/Dto/Event/EventDto.php b/src/App/Dto/Event/EventDto.php new file mode 100644 index 00000000..a7223d28 --- /dev/null +++ b/src/App/Dto/Event/EventDto.php @@ -0,0 +1,61 @@ + + 1 = coming soon
+ 2 = in preparation
+ 3 = running
+ 4 = in evaluation
+ 5 = completed/finalized
+ 6 = closed
+ 7 = aborted
+ 8 = hidden', + type: 'integer', + enum: EventStatus::class, + )] + public int $status; + + public function __construct( + int $id, + string $owner, + string $title, + ?string $description, + int $duration, + string $startedAt, + EventStatus $status + ) { + $this->id = $id; + $this->owner = $owner; + $this->title = $title; + $this->description = $description; + $this->duration = $duration; + $this->startedAt = $startedAt; + $this->status = $status->value; + } +} diff --git a/src/App/Dto/Event/EventListDto.php b/src/App/Dto/Event/EventListDto.php new file mode 100644 index 00000000..02108010 --- /dev/null +++ b/src/App/Dto/Event/EventListDto.php @@ -0,0 +1,23 @@ + $events + */ + public function __construct(array $events) + { + $this->events = $events; + } +} diff --git a/src/App/Dto/Topic/TopicCreateFailureMessageDto.php b/src/App/Dto/Topic/TopicCreateFailureMessageDto.php new file mode 100644 index 00000000..bcb99bb8 --- /dev/null +++ b/src/App/Dto/Topic/TopicCreateFailureMessageDto.php @@ -0,0 +1,22 @@ +topic = $topic; + } +} diff --git a/src/App/Dto/Topic/TopicCreateRequestDto.php b/src/App/Dto/Topic/TopicCreateRequestDto.php new file mode 100644 index 00000000..d71ab93d --- /dev/null +++ b/src/App/Dto/Topic/TopicCreateRequestDto.php @@ -0,0 +1,29 @@ +topic = $topic->topic; + $this->description = $topic->description; + } +} diff --git a/src/App/Dto/Topic/TopicCreateResponseDto.php b/src/App/Dto/Topic/TopicCreateResponseDto.php new file mode 100644 index 00000000..b482d6f6 --- /dev/null +++ b/src/App/Dto/Topic/TopicCreateResponseDto.php @@ -0,0 +1,22 @@ +uuid = $topic->uuid->getHex()->toString(); + parent::__construct($topic); + } +} diff --git a/src/App/Dto/Topic/TopicListDto.php b/src/App/Dto/Topic/TopicListDto.php new file mode 100644 index 00000000..6e21fe23 --- /dev/null +++ b/src/App/Dto/Topic/TopicListDto.php @@ -0,0 +1,30 @@ + $topics + */ + public function __construct(array $topics) + { + $topicList = []; + + foreach ($topics as $topic) { + $topicList[] = new TopicCreateResponseDto($topic); + } + + $this->topics = $topicList; + } +} diff --git a/src/App/Entity/Account/Account.php b/src/App/Entity/Account/Account.php deleted file mode 100644 index c9679757..00000000 --- a/src/App/Entity/Account/Account.php +++ /dev/null @@ -1,26 +0,0 @@ - 'soon', + EventStatus::PREPARE => 'prepare', + EventStatus::RUNNING => 'running', + EventStatus::EVALUATION => 'evaluation', + EventStatus::COMPLETE => 'complete', + EventStatus::CLOSED => 'closed', + EventStatus::ABORTED => 'aborted', + EventStatus::HIDDEN => 'hidden', + }; + } +} diff --git a/src/App/Enum/UserRole.php b/src/App/Enum/UserRole.php new file mode 100644 index 00000000..43ac88c7 --- /dev/null +++ b/src/App/Enum/UserRole.php @@ -0,0 +1,23 @@ + 'Owner', + UserRole::ADMINISTRATOR => 'Administrator', + UserRole::MODERATOR => 'Moderator', + UserRole::USER => 'User', + UserRole::GUEST => 'Guest' + }; + } +} diff --git a/src/App/Handler/Account/AccessTokenHandler.php b/src/App/Handler/Account/AccessTokenHandler.php deleted file mode 100644 index 9fe2a366..00000000 --- a/src/App/Handler/Account/AccessTokenHandler.php +++ /dev/null @@ -1,38 +0,0 @@ -value, - content: [new OA\JsonContent(ref: AccessToken::class)] - )] - #[OA\Response( - response: HTTP::STATUS_UNAUTHORIZED, - description: StatusMessage::UNAUTHORIZED_ACCESS->value, - content: [new OA\JsonContent(ref: HttpResponseMessage::class)] - )] - public function handle(ServerRequestInterface $request): ResponseInterface - { - $accessToken = $request->getAttribute(AccessToken::class); - - return new JsonResponse($accessToken, HTTP::STATUS_OK); - } -} diff --git a/src/App/Handler/Account/AccountActivationHandler.php b/src/App/Handler/Account/AccountActivationHandler.php deleted file mode 100644 index 61c78675..00000000 --- a/src/App/Handler/Account/AccountActivationHandler.php +++ /dev/null @@ -1,49 +0,0 @@ -value, - )] - #[OA\Response( - response: HTTP::STATUS_BAD_REQUEST, - description: StatusMessage::BAD_REQUEST->value, - content: [new OA\JsonContent(ref: HttpResponseMessage::class)] - )] - public function handle(ServerRequestInterface $request): ResponseInterface - { - return new JsonResponse([], HTTP::STATUS_OK); - } -} diff --git a/src/App/Handler/Account/AccountPasswordForgottenHandler.php b/src/App/Handler/Account/AccountPasswordForgottenHandler.php deleted file mode 100644 index 11afdf9d..00000000 --- a/src/App/Handler/Account/AccountPasswordForgottenHandler.php +++ /dev/null @@ -1,34 +0,0 @@ -value, - )] - public function handle(ServerRequestInterface $request): ResponseInterface - { - return new JsonResponse([], HTTP::STATUS_OK); - } -} diff --git a/src/App/Handler/Account/AccountPasswordHandler.php b/src/App/Handler/Account/AccountPasswordHandler.php deleted file mode 100644 index e95f749f..00000000 --- a/src/App/Handler/Account/AccountPasswordHandler.php +++ /dev/null @@ -1,49 +0,0 @@ -value, - )] - #[OA\Response( - response: HTTP::STATUS_BAD_REQUEST, - description: StatusMessage::BAD_REQUEST->value, - content: [new OA\JsonContent(ref: HttpResponseMessage::class)] - )] - public function handle(ServerRequestInterface $request): ResponseInterface - { - return new JsonResponse([], HTTP::STATUS_OK); - } -} diff --git a/src/App/Handler/Account/AccountRegisterHandler.php b/src/App/Handler/Account/AccountRegisterHandler.php deleted file mode 100644 index 7a1fd10d..00000000 --- a/src/App/Handler/Account/AccountRegisterHandler.php +++ /dev/null @@ -1,41 +0,0 @@ -value, - )] - #[OA\Response( - response: HTTP::STATUS_BAD_REQUEST, - description: StatusMessage::BAD_REQUEST->value, - content: [new OA\JsonContent(ref: HttpResponseMessage::class)] - )] - public function handle(ServerRequestInterface $request): ResponseInterface - { - return new JsonResponse([], HTTP::STATUS_OK); - } -} diff --git a/src/App/Handler/Account/AuthenticationHandler.php b/src/App/Handler/Account/AuthenticationHandler.php deleted file mode 100644 index dab59953..00000000 --- a/src/App/Handler/Account/AuthenticationHandler.php +++ /dev/null @@ -1,54 +0,0 @@ -value, - content: [new OA\JsonContent(ref: AuthenticationResponse::class)] - )] - #[OA\Response( - response: HTTP::STATUS_UNAUTHORIZED, - description: StatusMessage::UNAUTHORIZED_ACCESS->value, - content: [new OA\JsonContent(ref: HttpResponseMessage::class)] - )] - #[OA\Response( - response: HTTP::STATUS_FORBIDDEN, - description: StatusMessage::FORBIDDEN->value, - content: [new OA\JsonContent(ref: HttpResponseMessage::class)] - )] - public function handle(ServerRequestInterface $request): ResponseInterface - { - $accessToken = $request->getAttribute(AccessToken::class); - $refreshToken = $request->getAttribute(RefreshToken::class); - - $response = AuthenticationResponse::from($accessToken, $refreshToken); - - return new JsonResponse($response, HTTP::STATUS_OK); - } -} diff --git a/src/App/Handler/Account/LogoutHandler.php b/src/App/Handler/Account/LogoutHandler.php deleted file mode 100644 index e67a91ab..00000000 --- a/src/App/Handler/Account/LogoutHandler.php +++ /dev/null @@ -1,38 +0,0 @@ -value, - content: [new OA\JsonContent(ref: AuthenticationResponse::class)] - )] - #[OA\Response( - response: HTTP::STATUS_UNAUTHORIZED, - description: StatusMessage::UNAUTHORIZED_ACCESS->value, - content: [new OA\JsonContent(ref: HttpResponseMessage::class)] - )] - public function handle(ServerRequestInterface $request): ResponseInterface - { - $response = HttpResponseMessage::create(HTTP::STATUS_OK, StatusMessage::SUCCESS); - - return new JsonResponse($response, $response->statusCode); - } -} diff --git a/src/App/Handler/Event/EventCreateHandler.php b/src/App/Handler/Event/EventCreateHandler.php new file mode 100644 index 00000000..c4f4c73c --- /dev/null +++ b/src/App/Handler/Event/EventCreateHandler.php @@ -0,0 +1,19 @@ +getParsedBody(); + + return new JsonResponse($data, HTTP::STATUS_CREATED); + } +} diff --git a/src/App/Handler/Event/EventHandler.php b/src/App/Handler/Event/EventHandler.php new file mode 100644 index 00000000..998f1856 --- /dev/null +++ b/src/App/Handler/Event/EventHandler.php @@ -0,0 +1,92 @@ +getAttribute(User::AUTHENTICATED_USER); + + /** + * @var Event $event + */ + $event = $request->getAttribute(Event::class); + + /** + * @var array $participants + */ + $participants = $this->participantService->findActiveParticipantByEvent($event->id); + + $data = [ + 'id' => $event->id, + 'owner' => $this->userService->findById($event->userId)->name, + 'title' => $event->title, + 'description' => $event->description, + 'eventText' => $event->eventText, + 'createdAt' => $event->createdAt->format('Y-m-d H:i'), + 'startedAt' => $event->startedAt->format('Y-m-d H:i'), + 'duration' => $event->duration, + 'status' => $event->status, + 'ratingCompleted' => $event->ratingCompleted, + ]; + + if ($user instanceof User) { + $participantData = []; + foreach ($participants as $participant) { + $user = $this->userService->findById($participant->userId); + $project = $this->projectService->findByParticipantId($participant->id); + $entry = [ + 'id' => $participant->id, + 'username' => $user->name, + 'userUuid' => $user->uuid, + 'requestedAt' => $participant->requestedAt->format('Y-m-d H:i'), + 'projectId' => $project?->id, + 'projectTitle' => $project?->title, + ]; + + $participantData[] = $entry; + } + + $data['participants'] = $participantData; + } + + $topic = $this->topicPoolService->findByEventId($event->id); + + if ($topic instanceof Topic) { + $topicData = [ + 'title' => $topic->topic, + 'description' => $topic->description, + ]; + + $data['topic'] = $topicData; + } + + return new JsonResponse($data, HTTP::STATUS_OK); + } +} diff --git a/src/App/Handler/Event/EventListHandler.php b/src/App/Handler/Event/EventListHandler.php new file mode 100644 index 00000000..a75c54c1 --- /dev/null +++ b/src/App/Handler/Event/EventListHandler.php @@ -0,0 +1,47 @@ + + Options: ID|OWNER|TITLE|DESCRIPTION|DURATION|STARTEDAT|STATUS', + in: 'query', + required: false, + schema: new OA\Schema(type: 'string'), + example: 'startedAt', + )] + #[OA\QueryParameter( + name: 'sort', + description: 'determines the display order of the events
+ Options: ASC|DESC', + in: 'query', + required: false, + schema: new OA\Schema(type: 'string'), + example: 'DESC', + )] + #[OA\Response( + response: HTTP::STATUS_OK, + description: 'Success', + content: new OA\JsonContent(type: 'array', items: new OA\Items(ref: EventDto::class)), + )] + public function handle(ServerRequestInterface $request): ResponseInterface + { + /** @var EventListDto $events */ + $events = $request->getAttribute(EventListDto::class); + + return new JsonResponse($events, HTTP::STATUS_OK); + } +} diff --git a/src/App/Handler/Event/EventNameHandler.php b/src/App/Handler/Event/EventNameHandler.php new file mode 100644 index 00000000..8730357c --- /dev/null +++ b/src/App/Handler/Event/EventNameHandler.php @@ -0,0 +1,27 @@ +getAttribute(Event::class); + + $data = [ + 'eventId' => $event->id, + ]; + + return new JsonResponse($data, HTTP::STATUS_OK); + } +} diff --git a/src/App/Handler/Event/EventParticipantSubscribeHandler.php b/src/App/Handler/Event/EventParticipantSubscribeHandler.php new file mode 100644 index 00000000..e1da6b66 --- /dev/null +++ b/src/App/Handler/Event/EventParticipantSubscribeHandler.php @@ -0,0 +1,53 @@ +getAttribute('participantCreateStatus'); + + if (!$participantCreateStatus) { + return new JsonResponse( + ['Status' => 'Benutzer konnte der Teilnehmerliste nicht hinzugefügt werden'], + HTTP::STATUS_METHOD_NOT_ALLOWED + ); + } + + /** + * @var User $user + */ + $user = $request->getAttribute(User::AUTHENTICATED_USER); + $eventId = (int)$request->getAttribute('eventId'); + + $participant = $this->participantService->findByUserIdAndEventId($user->id, $eventId); + $project = $this->projectService->findByParticipantId($participant->id); + + $participantData = [ + 'id' => $participant->id, + 'username' => $user->name, + 'userUuid' => $user->uuid, + 'requestedAt' => $participant->requestedAt->format('Y-m-d H:i'), + 'projectId' => $project?->id, + 'projectTitle' => $project?->title, + ]; + + return new JsonResponse($participantData, HTTP::STATUS_OK); + } +} diff --git a/src/App/Handler/Event/EventParticipantUnsubscribeHandler.php b/src/App/Handler/Event/EventParticipantUnsubscribeHandler.php new file mode 100644 index 00000000..d0747b0c --- /dev/null +++ b/src/App/Handler/Event/EventParticipantUnsubscribeHandler.php @@ -0,0 +1,22 @@ +getAttribute('participantRemoveStatus'); + + if (!$participantRemoveStatus) { + return new JsonResponse(['Status' => 'Benutzer konnte der Teilnehmerliste nicht entfernt werden'], HTTP::STATUS_METHOD_NOT_ALLOWED); + } + return new JsonResponse(['Status' => 'OK'], HTTP::STATUS_OK); + } +} diff --git a/src/App/Handler/SwaggerUIHandler.php b/src/App/Handler/SwaggerUIHandler.php deleted file mode 100644 index f114ab17..00000000 --- a/src/App/Handler/SwaggerUIHandler.php +++ /dev/null @@ -1,57 +0,0 @@ - []], ['Client-Identification-String' => []], ['refreshToken' => []]] -)] -readonly class SwaggerUIHandler implements RequestHandlerInterface -{ - public function handle(ServerRequestInterface $request): ResponseInterface - { - $indexFile = ROOT_DIR . 'public/docs/index.html'; - - if (file_exists($indexFile)) { - return new HtmlResponse(file_get_contents($indexFile)); - } - - return new JsonResponse([], HTTP::STATUS_NO_CONTENT); - } -} diff --git a/src/App/Handler/System/ApiMeHandler.php b/src/App/Handler/System/ApiMeHandler.php new file mode 100644 index 00000000..60378705 --- /dev/null +++ b/src/App/Handler/System/ApiMeHandler.php @@ -0,0 +1,57 @@ + []]], +)] +readonly class ApiMeHandler implements RequestHandlerInterface +{ + #[OA\Get( + path: '/api/user/me', + summary: 'Returns minimal information for a logged-in user or empty', + tags: ['User Control'], + deprecated: true, + )] + #[OA\Response( + response: HTTP::STATUS_OK, + description: 'Success', + content: new OA\JsonContent(ref: ApiMeDto::class) + )] + #[OA\Response( + response: HTTP::STATUS_UNAUTHORIZED, + description: 'Incorrect authorization or expired', + content: new OA\JsonContent(ref: SimpleMessageDto::class) + )] + public function handle(ServerRequestInterface $request): ResponseInterface + { + $user = $request->getAttribute(User::AUTHENTICATED_USER); + + if (!($user instanceof User)) { + return new JsonResponse([], HTTP::STATUS_OK); + } + + return new JsonResponse(new ApiMeDto($user), HTTP::STATUS_OK); + } +} diff --git a/src/App/Handler/PingHandler.php b/src/App/Handler/System/PingHandler.php similarity index 72% rename from src/App/Handler/PingHandler.php rename to src/App/Handler/System/PingHandler.php index be50fa47..74b9ec0a 100644 --- a/src/App/Handler/PingHandler.php +++ b/src/App/Handler/System/PingHandler.php @@ -1,12 +1,10 @@ value, + description: 'Success', content: [ new OA\JsonContent( properties: [ new OA\Property( property: 'ack', - description: 'actually request time', - type: DataType::STRING->value, + description: 'actually time', + type: 'string' ), ] ), ] ), - ] + ], + deprecated: true )] public function handle(ServerRequestInterface $request): ResponseInterface { diff --git a/src/App/Handler/System/TestMailHandler.php b/src/App/Handler/System/TestMailHandler.php new file mode 100644 index 00000000..97ff1d0a --- /dev/null +++ b/src/App/Handler/System/TestMailHandler.php @@ -0,0 +1,32 @@ +from('hello@example.com') + ->to('you@example.com') + ->subject('Time for Symfony Mailer!') + ->text('Sending emails is fun again!') + ->html('

See Twig integration for better HTML integration!

'); + + $this->mailer->send($email); + return new JsonResponse(['message' => 'You have a Mail'], HTTP::STATUS_CREATED); + } +} diff --git a/src/App/Handler/Topic/TopicCreateHandler.php b/src/App/Handler/Topic/TopicCreateHandler.php new file mode 100644 index 00000000..012d432e --- /dev/null +++ b/src/App/Handler/Topic/TopicCreateHandler.php @@ -0,0 +1,59 @@ +getAttribute(Topic::class); + + $this->mailService->send($topic); + + $topic = new TopicCreateResponseDto($topic); + + return new JsonResponse($topic, HTTP::STATUS_CREATED); + } +} diff --git a/src/App/Handler/Topic/TopicListAvailableHandler.php b/src/App/Handler/Topic/TopicListAvailableHandler.php new file mode 100644 index 00000000..c1ceba35 --- /dev/null +++ b/src/App/Handler/Topic/TopicListAvailableHandler.php @@ -0,0 +1,38 @@ + $data */ + $data = $request->getAttribute(TopicListDto::class); + + return new JsonResponse($data, HTTP::STATUS_OK); + } +} diff --git a/src/App/Hydrator/AccountAccessAuthHydrator.php b/src/App/Hydrator/AccountAccessAuthHydrator.php deleted file mode 100644 index 882507c4..00000000 --- a/src/App/Hydrator/AccountAccessAuthHydrator.php +++ /dev/null @@ -1,68 +0,0 @@ -hydrate($entity); - } - - return $collection; - } - - public function extract(AccountAccessAuthInterface $object): array - { - return [ - 'id' => $object->id, - 'accountId' => $object->accountId, - 'label' => $object->label, - 'refreshToken' => $object->refreshToken, - 'userAgent' => $object->userAgent, - 'clientIdentHash' => $object->clientIdentHash, - 'createdAt' => $object->createdAt->format(DateTimeFormat::DEFAULT->value), - ]; - } - - public function extractCollection(AccountAccessAuthCollectionInterface $collection): array - { - $data = []; - - foreach ($collection as $entity) { - $data[] = $this->extract($entity); - } - - return $data; - } -} diff --git a/src/App/Hydrator/AccountAccessAuthHydratorInterface.php b/src/App/Hydrator/AccountAccessAuthHydratorInterface.php deleted file mode 100644 index 1b16b3f7..00000000 --- a/src/App/Hydrator/AccountAccessAuthHydratorInterface.php +++ /dev/null @@ -1,14 +0,0 @@ -uuid->fromString($data['token']), - createdAt: new DateTimeImmutable($data['createdAt']), - ); - } - - public function hydrateCollection(array $data): AccountActivationCollectionInterface - { - $collection = new AccountActivationCollection(); - - foreach ($data as $entity) { - $collection[] = $this->hydrate($entity); - } - - return $collection; - } - - public function extract(AccountActivationInterface $object): array - { - return [ - 'id' => $object->id, - 'email' => $object->email->toString(), - 'token' => $object->token->getHex(), - 'createdAt' => $object->createdAt->format(DateTimeFormat::DEFAULT->value), - ]; - } - - public function extractCollection(AccountActivationCollectionInterface $collection): array - { - $data = []; - - foreach ($collection as $entity) { - $data[] = $this->extract($entity); - } - - return $data; - } -} diff --git a/src/App/Hydrator/AccountActivationHydratorInterface.php b/src/App/Hydrator/AccountActivationHydratorInterface.php deleted file mode 100644 index aad28f48..00000000 --- a/src/App/Hydrator/AccountActivationHydratorInterface.php +++ /dev/null @@ -1,14 +0,0 @@ -uuid->fromString($data['uuid']), - name: $data['name'], - password: $data['password'], - email: new Email($data['email']), - registeredAt: new DateTimeImmutable($data['registeredAt']), - lastActionAt: new DateTimeImmutable($data['lastActionAt']), - ); - } - - /** - * @throws Exception - */ - public function hydrateCollection(array $data): AccountCollectionInterface - { - $collection = new AccountCollection(); - - foreach ($data as $entity) { - $collection[] = $this->hydrate($entity); - } - - return $collection; - } - - public function extract(AccountInterface $object): array - { - return [ - 'id' => $object->id, - 'uuid' => $object->uuid->getHex()->toString(), - 'name' => $object->name, - 'password' => $object->password, - 'email' => $object->email->toString(), - 'registeredAt' => $object->registeredAt->format(DateTimeFormat::DEFAULT->value), - 'lastActionAt' => $object->lastActionAt->format(DateTimeFormat::DEFAULT->value), - ]; - } - - public function extractCollection(AccountCollectionInterface $collection): array - { - $data = []; - - foreach ($collection as $entity) { - $data[] = $this->extract($entity); - } - - return $data; - } -} diff --git a/src/App/Hydrator/AccountHydratorInterface.php b/src/App/Hydrator/AccountHydratorInterface.php deleted file mode 100644 index 3379238b..00000000 --- a/src/App/Hydrator/AccountHydratorInterface.php +++ /dev/null @@ -1,14 +0,0 @@ -uuid->fromString($data['token']), - createdAt: new DateTimeImmutable($data['createdAt']), - ); - } - - public function hydrateCollection(array $data): TokenCollectionInterface - { - $collection = new TokenCollection(); - - foreach ($data as $entity) { - $collection[] = $this->hydrate($entity); - } - - return $collection; - } - - public function extract(TokenInterface $object): array - { - return [ - 'id' => $object->id, - 'accountId' => $object->accountId, - 'tokenType' => $object->tokenType->value, - 'token' => $object->token->getHex(), - 'createdAt' => $object->createdAt->format(DateTimeFormat::DEFAULT->value), - ]; - } - - public function extractCollection(TokenCollectionInterface $collection): array - { - $data = []; - - foreach ($collection as $entity) { - $data[] = $this->extract($entity); - } - - return $data; - } -} diff --git a/src/App/Hydrator/TokenHydratorInterface.php b/src/App/Hydrator/TokenHydratorInterface.php deleted file mode 100644 index c1ac0358..00000000 --- a/src/App/Hydrator/TokenHydratorInterface.php +++ /dev/null @@ -1,14 +0,0 @@ -getAttribute('token'); - - /** @var AccountRegistration $accountData */ - $accountData = $request->getAttribute(AccountRegistration::class); - - if ($activationToken === null) { - throw new HttpInvalidArgumentException( - LogMessage::ACTIVATION_TOKEN_MISSING, - StatusMessage::TOKEN_INVALID, - [ - 'Token:' => $activationToken, - ] - ); - } - - /** @var null|AccountActivationInterface $persistActivationToken */ - $persistActivationToken = $this->accountActivationRepository->findByToken($activationToken); - - if ($persistActivationToken === null) { - throw new HttpInvalidArgumentException( - LogMessage::ACTIVATION_TOKEN_MISSING, - StatusMessage::TOKEN_INVALID, - [ - 'Invalid activation token:' => $activationToken, - ] - ); - } - - $account = new Account( - id: null, - uuid: $this->uuid->uuid7(), - name: $accountData->accountName, - password: password_hash($accountData->password, PASSWORD_BCRYPT), - email: $persistActivationToken->email, - registeredAt: new DateTimeImmutable(), - lastActionAt: new DateTimeImmutable() - ); - - try { - $this->accountRepository->insert($account); - } catch (DuplicateEntryException $e) { - throw new HttpDuplicateEntryException( - LogMessage::ACCOUNT_ALREADY_EXISTS, - StatusMessage::INVALID_DATA, - [ - 'E-Mail' => $account->email->toString(), - 'Exception Message:' => $e->getMessage(), - ] - ); - } - - $this->accountActivationRepository->deleteById($persistActivationToken->id); - - return $handler->handle($request); - } -} diff --git a/src/App/Middleware/Account/LastAktivityUpdaterMiddleware.php b/src/App/Middleware/Account/LastAktivityUpdaterMiddleware.php deleted file mode 100644 index 9a533b97..00000000 --- a/src/App/Middleware/Account/LastAktivityUpdaterMiddleware.php +++ /dev/null @@ -1,35 +0,0 @@ -getAttribute(AccountInterface::AUTHENTICATED); - - if (!($account instanceof AccountInterface)) { - return $handler->handle($request); - } - - $account = $account->with(lastActionAt: new DateTimeImmutable()); - - $this->accountRepository->update($account); - - return $handler->handle($request->withAttribute(AccountInterface::AUTHENTICATED, $account)); - } -} diff --git a/src/App/Middleware/Account/LoginAuthentication/AuthenticationConditionsMiddleware.php b/src/App/Middleware/Account/LoginAuthentication/AuthenticationConditionsMiddleware.php deleted file mode 100644 index c804b372..00000000 --- a/src/App/Middleware/Account/LoginAuthentication/AuthenticationConditionsMiddleware.php +++ /dev/null @@ -1,30 +0,0 @@ -hasHeader('Authentication') || $request->hasHeader('Authorization')) { - throw new HttpUnauthorizedException( - LogMessage::LOGIN_DENIED_AUTH_HEADER_ALREADY_PRESENT, - StatusMessage::ACCOUNT_ALREADY_AUTHENTICATED, - [ - 'uri' => (string)$request->getUri(), - 'ip' => $request->getServerParams()['REMOTE_ADDR'] ?? 'unknown', - ] - ); - } - - return $handler->handle($request); - } -} diff --git a/src/App/Middleware/Account/LoginAuthentication/AuthenticationMiddleware.php b/src/App/Middleware/Account/LoginAuthentication/AuthenticationMiddleware.php deleted file mode 100644 index bfc41663..00000000 --- a/src/App/Middleware/Account/LoginAuthentication/AuthenticationMiddleware.php +++ /dev/null @@ -1,72 +0,0 @@ -getParsedBody(); - - if (!array_key_exists('email', $data)) { - throw new HttpUnauthorizedException( - LogMessage::REQUIRED_EMAIL_MISSING, - StatusMessage::INVALID_DATA - ); - } - - $email = new Email($data['email']); - - $account = $this->accountRepository->findByEmail($email); - - if (!($account instanceof AccountInterface)) { - throw new HttpUnauthorizedException( - LogMessage::ACCOUNT_NOT_FOUND, - StatusMessage::INVALID_DATA, - [ - 'E-Mail:' => $email->toString(), - ], - Level::Warning - ); - } - - if (!$this->service->isPasswordMatch($data['password'], $account->password)) { - throw new HttpUnauthorizedException( - LogMessage::PASSWORD_INCORRECT, - StatusMessage::INVALID_DATA, - [ - 'E-Mail:' => $email->toString(), - ], - Level::Warning - ); - } - - $account = $account->with(lastActionAt: new DateTimeImmutable()); - - $this->accountRepository->update($account); - - return $handler->handle($request->withAttribute(AccountInterface::AUTHENTICATED, $account)); - } -} diff --git a/src/App/Middleware/Account/LoginAuthentication/AuthenticationValidationMiddleware.php b/src/App/Middleware/Account/LoginAuthentication/AuthenticationValidationMiddleware.php deleted file mode 100644 index 9291538e..00000000 --- a/src/App/Middleware/Account/LoginAuthentication/AuthenticationValidationMiddleware.php +++ /dev/null @@ -1,40 +0,0 @@ -getParsedBody(); - - $this->validator->setData($data); - - if (!$this->validator->isValid()) { - throw new HttpUnauthorizedException( - LogMessage::EMAIL_INVALID, - StatusMessage::INVALID_DATA, - [ - 'E-Mail:' => $data['email'] ?? null, - 'Validator-Message:' => $this->validator->getMessages(), - ] - ); - } - - return $handler->handle($request->withParsedBody($this->validator->getValues())); - } -} diff --git a/src/App/Middleware/Account/LoginAuthentication/PersistAuthenticationMiddleware.php b/src/App/Middleware/Account/LoginAuthentication/PersistAuthenticationMiddleware.php deleted file mode 100644 index 25155700..00000000 --- a/src/App/Middleware/Account/LoginAuthentication/PersistAuthenticationMiddleware.php +++ /dev/null @@ -1,79 +0,0 @@ -getAttribute(AccountInterface::AUTHENTICATED); - - /** @var ClientIdentification $clientIdent */ - $clientIdent = $request->getAttribute(ClientIdentification::class); - - /** @var RefreshToken $refreshToken */ - $refreshToken = $request->getAttribute(RefreshToken::class); - - // @phpstan-ignore-next-line - if ($account === null || $clientIdent === null || $refreshToken === null) { - throw new HttpUnauthorizedException( - LogMessage::AUTHENTICATION_PERSISTENCE_ERROR, - StatusMessage::INVALID_DATA, - [ - // @phpstan-ignore-next-line - 'Account:' => $account?->email, - // @phpstan-ignore-next-line - 'Client ID:' => $clientIdent?->identificationHash, - 'Refresh Token:' => $refreshToken ? 'placed' : null, - ] - ); - } - - $accountAccessAuth = new AccountAccessAuth( - 1, - $account->id, - 'default', - $refreshToken->refreshToken, - $clientIdent->clientIdentificationData->userAgent, - $clientIdent->identificationHash, - new DateTimeImmutable() - ); - try { - $this->repository->insert($accountAccessAuth); - } catch (DuplicateEntryException $e) { - throw new HttpDuplicateEntryException( - LogMessage::DUPLICATE_SOURCE_LOGIN, - StatusMessage::INVALID_DATA, - [ - 'Account' => $account->name, - 'ClientID' => $clientIdent->identificationHash, - 'ErrorMessage' => $e->getMessage(), - ], - ); - } - - return $handler->handle($request); - } -} diff --git a/src/App/Middleware/Account/LogoutMiddleware.php b/src/App/Middleware/Account/LogoutMiddleware.php deleted file mode 100644 index ad9d9d46..00000000 --- a/src/App/Middleware/Account/LogoutMiddleware.php +++ /dev/null @@ -1,63 +0,0 @@ -getAttribute(AccountInterface::AUTHENTICATED); - - if (!($account instanceof AccountInterface)) { - throw new HttpUnauthorizedException( - LogMessage::LOGOUT_REQUIRES_AUTHENTICATION, - StatusMessage::UNAUTHORIZED_ACCESS, - [], - Level::Warning - ); - } - - /** @var ClientIdentification $clientId */ - $clientId = $request->getAttribute(ClientIdentification::class); - - $accountAccessAuth = $this->authRepository->findByAccountIdAndClientIdHash( - $account->id, - $clientId->identificationHash - ); - - if (!($accountAccessAuth instanceof AccountAccessAuthInterface)) { - throw new HttpUnauthorizedException( - LogMessage::LOGOUT_CLIENT_IDENTITY_MISMATCH, - StatusMessage::UNAUTHORIZED_ACCESS, - [ - 'accountId' => $account->id, - 'clientIdentificationHash' => $clientId->identificationHash, - ], - Level::Warning - ); - } - - $this->authRepository->deleteById($accountAccessAuth->id); - - return $handler->handle($request); - } -} diff --git a/src/App/Middleware/Account/PasswordChangeMiddleware.php b/src/App/Middleware/Account/PasswordChangeMiddleware.php deleted file mode 100644 index 31013d8d..00000000 --- a/src/App/Middleware/Account/PasswordChangeMiddleware.php +++ /dev/null @@ -1,68 +0,0 @@ -getAttribute('token'); - $password = $request->getParsedBody()['password']; - - if ($token === null) { - return $this->errorResponse(LogMessage::PASSWORD_CHANGE_TOKEN_MISSING, $token); - } - - $persistedToken = $this->tokenRepository->findByToken($token); - - if (!($persistedToken instanceof TokenInterface) || $persistedToken->tokenType !== TokenType::EMail) { - return $this->errorResponse(LogMessage::PASSWORD_CHANGE_TOKEN_INVALID, $token); - } - - $account = $this->accountRepository->findById($persistedToken->accountId); - - if (!($account instanceof Account)) { - return $this->errorResponse(LogMessage::PASSWORD_CHANGE_TOKEN_ACCOUNT_NOT_FOUND, $token); - } - - $hashedPassword = $this->accountService->cryptPassword($password); - $account = $account->with(password: $hashedPassword); - - $this->accountRepository->update($account); - $this->tokenRepository->deleteById($persistedToken->id); - - return $handler->handle($request); - } - - private function errorResponse(LogMessage $logMessage, ?string $token): ResponseInterface - { - throw new HttpInvalidArgumentException( - $logMessage, - StatusMessage::TOKEN_INVALID, - [ - 'Token:' => $token, - ] - ); - } -} diff --git a/src/App/Middleware/Account/PasswordForgottenMiddleware.php b/src/App/Middleware/Account/PasswordForgottenMiddleware.php deleted file mode 100644 index f72db6be..00000000 --- a/src/App/Middleware/Account/PasswordForgottenMiddleware.php +++ /dev/null @@ -1,42 +0,0 @@ -getAttribute(Email::class); - - if (!$this->accountService->isEmailAvailable($email)) { - $this->accountService->sendTokenForPasswordChange($email); - return $handler->handle($request); - } - - throw new HttpHandledInvalidArgumentAsSuccessException( - LogMessage::PASSWORD_REQUEST_MISSING_ACCOUNT, - StatusMessage::INVALID_DATA, - [ - 'email:' => $email->toString(), - ], - Level::Alert - ); - } -} diff --git a/src/App/Middleware/Account/RegisterMiddleware.php b/src/App/Middleware/Account/RegisterMiddleware.php deleted file mode 100644 index 3a9e16b9..00000000 --- a/src/App/Middleware/Account/RegisterMiddleware.php +++ /dev/null @@ -1,58 +0,0 @@ -getAttribute(Email::class); - - if (!$this->accountService->isEmailAvailable($email)) { - $this->logger->warning(LogMessage::ACCOUNT_ALREADY_EXISTS->value, [ - 'email:' => $email->toString(), - ]); - - $this->accountService->sendTokenForPasswordChange($email); - - return $handler->handle($request); - } - - $activation = new AccountActivation( - id: null, - email: $email, - token: $this->uuid->uuid7(), - createdAt: new DateTimeImmutable() - ); - - $this->accountActivationRepository->insert($activation); - - $this->activationTokenService->sendEmail($activation); - - return $handler->handle($request); - } -} diff --git a/src/App/Middleware/Account/RequestAuthenticationMiddleware.php b/src/App/Middleware/Account/RequestAuthenticationMiddleware.php deleted file mode 100644 index 6330b90f..00000000 --- a/src/App/Middleware/Account/RequestAuthenticationMiddleware.php +++ /dev/null @@ -1,76 +0,0 @@ -getHeaderLine('Authorization'); - - if (strlen($authorization) === 0) { - $this->logger->info('Guest call', [ - 'uri' => (string)$request->getUri(), - ]); - - return $handler->handle($request); - } - - if (!$this->accessTokenService->isValid($authorization)) { - throw new HttpUnauthorizedException( - LogMessage::ACCESS_TOKEN_EXPIRED, - StatusMessage::TOKEN_EXPIRED, - [ - 'uri' => (string)$request->getUri(), - 'ip' => $request->getServerParams()['REMOTE_ADDR'] ?? 'unknown', - ] - ); - } - - $authorization = $this->accessTokenService->decode($authorization); - $uuid = $this->uuid->fromString($authorization->uuid); - $account = $this->accountRepository->findByUuid($uuid); - if (!($account instanceof AccountInterface)) { - throw new HttpUnauthorizedException( - LogMessage::ACCESS_TOKEN_ACCOUNT_NOT_FOUND, - StatusMessage::TOKEN_INVALID, - [ - 'uri' => (string)$request->getUri(), - 'uuid' => $authorization->uuid, - ], - Level::Warning - ); - } - - $this->logger->info('Authenticated user call.', [ - 'Account' => $account->name, - 'uri' => (string)$request->getUri(), - ]); - - return $handler->handle($request->withAttribute(AccountInterface::AUTHENTICATED, $account)); - } -} diff --git a/src/App/Middleware/Account/Validation/ActivationInputValidatorMiddleware.php b/src/App/Middleware/Account/Validation/ActivationInputValidatorMiddleware.php deleted file mode 100644 index 7c87bcd2..00000000 --- a/src/App/Middleware/Account/Validation/ActivationInputValidatorMiddleware.php +++ /dev/null @@ -1,45 +0,0 @@ -getParsedBody(); - - $this->validator->setData($data); - - if (!$this->validator->isValid()) { - throw new HttpInvalidArgumentException( - LogMessage::ACCOUNT_NAME_INVALID, - StatusMessage::INVALID_DATA, - [ - 'Account Name:' => $data['accountName'] ?? null, - 'Validator-Message:' => $this->validator->getMessages(), - ] - ); - } - - $data = $this->validator->getValues(); - - $response = AccountRegistration::fromString($data['accountName'], $data['password']); - - return $handler->handle($request->withAttribute(AccountRegistration::class, $response)); - } -} diff --git a/src/App/Middleware/Account/Validation/EmailInputValidatorMiddleware.php b/src/App/Middleware/Account/Validation/EmailInputValidatorMiddleware.php deleted file mode 100644 index 0913cf04..00000000 --- a/src/App/Middleware/Account/Validation/EmailInputValidatorMiddleware.php +++ /dev/null @@ -1,47 +0,0 @@ -getParsedBody(); - - $this->mailValidator->setData($data); - - if (!$this->mailValidator->isValid()) { - throw new HttpInvalidArgumentException( - LogMessage::EMAIL_INVALID, - StatusMessage::INVALID_DATA, - [ - 'E-Mail:' => $data['email'] ?? null, - 'Validator Message:' => $this->mailValidator->getMessages(), - ] - ); - } - - $email = new Email($data['email']); - - return $handler->handle($request->withAttribute(Email::class, $email)); - } -} diff --git a/src/App/Middleware/Account/Validation/PasswordInputValidatorMiddleware.php b/src/App/Middleware/Account/Validation/PasswordInputValidatorMiddleware.php deleted file mode 100644 index 8cea3ba0..00000000 --- a/src/App/Middleware/Account/Validation/PasswordInputValidatorMiddleware.php +++ /dev/null @@ -1,39 +0,0 @@ -getParsedBody(); - - $this->validator->setData($data); - - if (!$this->validator->isValid()) { - throw new HttpInvalidArgumentException( - LogMessage::PASSWORD_INVALID, - StatusMessage::INVALID_DATA, - [ - 'Validator Message:' => $this->validator->getMessages(), - ] - ); - } - - return $handler->handle($request); - } -} diff --git a/src/App/Middleware/ClientIdentification/ClientIdentificationMiddleware.php b/src/App/Middleware/ClientIdentification/ClientIdentificationMiddleware.php deleted file mode 100644 index a251414a..00000000 --- a/src/App/Middleware/ClientIdentification/ClientIdentificationMiddleware.php +++ /dev/null @@ -1,31 +0,0 @@ -getHeaderLine('x-ident'); - $userAgent = $request->getHeaderLine('user-agent'); - - $clientIdentificationData = ClientIdentificationData::create($clientIdent, $userAgent); - $identificationHash = $this->clientIdentification->getClientIdentificationHash($clientIdentificationData); - $clientIdentification = ClientIdentification::create($clientIdentificationData, $identificationHash); - - return $handler->handle($request->withAttribute(ClientIdentification::class, $clientIdentification)); - } -} diff --git a/src/App/Middleware/Event/EventCreateMiddleware.php b/src/App/Middleware/Event/EventCreateMiddleware.php new file mode 100644 index 00000000..a5bfb842 --- /dev/null +++ b/src/App/Middleware/Event/EventCreateMiddleware.php @@ -0,0 +1,44 @@ +getAttribute(User::AUTHENTICATED_USER); + + $data = $request->getParsedBody(); + $data['userId'] = $user->id; + + $event = $this->hydrator->hydrate($data, Event::class); + + if (!$this->eventService->create($event)) { + return new JsonResponse([ + 'message' => 'Event already exists', + ], HTTP::STATUS_NOT_FOUND); + } + + return $handler->handle($request); + } +} diff --git a/src/App/Middleware/Event/EventCreateMiddlewareFactory.php b/src/App/Middleware/Event/EventCreateMiddlewareFactory.php new file mode 100644 index 00000000..96d8247a --- /dev/null +++ b/src/App/Middleware/Event/EventCreateMiddlewareFactory.php @@ -0,0 +1,42 @@ +get(EventService::class); + + /** @var ReflectionHydrator $hydrator */ + $hydrator = clone $container->get(ReflectionHydrator::class); + + /** @var DateTimeFormatterStrategy $strategy */ + $strategy = $container->get(DateTimeFormatterStrategy::class); + + $hydrator->addStrategy( + 'createdAt', + $strategy, + ); + + $hydrator->addStrategy( + 'startedAt', + $strategy, + ); + + $hydrator->addStrategy( + 'event', + new HydratorStrategy($container->get(ReflectionHydrator::class), Event::class) + ); + + return new EventCreateMiddleware($service, $hydrator); + } +} diff --git a/src/App/Middleware/Event/EventCreateValidationMiddleware.php b/src/App/Middleware/Event/EventCreateValidationMiddleware.php new file mode 100644 index 00000000..3d136eb6 --- /dev/null +++ b/src/App/Middleware/Event/EventCreateValidationMiddleware.php @@ -0,0 +1,35 @@ +getParsedBody(); + + $this->validator->setData($data); + + if (!$this->validator->isValid()) { + return new JsonResponse([ + 'message' => 'Validation fault', + 'data' => $this->validator->getMessages(), + ], HTTP::STATUS_NOT_FOUND); + } + + return $handler->handle($request->withParsedBody($this->validator->getValues())); + } +} diff --git a/src/App/Middleware/Event/EventListMiddleware.php b/src/App/Middleware/Event/EventListMiddleware.php new file mode 100644 index 00000000..466b77fb --- /dev/null +++ b/src/App/Middleware/Event/EventListMiddleware.php @@ -0,0 +1,67 @@ +getQueryParams(); + + $sort = match (strtoupper($params['sort'] ?? '')) { + 'ASC' => 'ASC', + default => 'DESC', + }; + + $order = match (strtoupper($params['order'] ?? '')) { + 'ID' => 'id', + 'OWNER' => 'owner', + 'TITLE' => 'title', + 'DESCRIPTION' => 'description', + 'DURATION' => 'duration', + 'STATUS' => 'status', + default => 'startedAt', + }; + + /** @var array $events */ + $events = $this->eventService->findAll($order, $sort); + + $eventList = []; + + foreach ($events as $event) { + $entry = new EventDto( + $event->id, + $this->userService->findById($event->userId)->name, + $event->title, + $event->description, + $event->duration, + $event->createdAt->format('Y-m-d H:i'), + $event->status, + ); + + $eventList[] = $entry; + } + + $eventList = new EventListDto($eventList); + + return $handler->handle($request->withAttribute(EventListDto::class, $eventList)); + } +} diff --git a/src/App/Middleware/Event/EventMiddleware.php b/src/App/Middleware/Event/EventMiddleware.php new file mode 100644 index 00000000..4dfc01ef --- /dev/null +++ b/src/App/Middleware/Event/EventMiddleware.php @@ -0,0 +1,27 @@ +getAttribute('eventId'); + + $event = $this->eventService->findById($eventId); + + return $handler->handle($request->withAttribute(Event::class, $event)); + } +} diff --git a/src/App/Middleware/Event/EventNameMiddleware.php b/src/App/Middleware/Event/EventNameMiddleware.php new file mode 100644 index 00000000..a9d3258c --- /dev/null +++ b/src/App/Middleware/Event/EventNameMiddleware.php @@ -0,0 +1,32 @@ +getAttribute('eventName'); + + $event = $this->eventService->findByTitle($eventName); + + if (!$event instanceof Event) { + throw new InvalidArgumentException('Could not find Event', 400); + } + + return $handler->handle($request->withAttribute(Event::class, $event)); + } +} diff --git a/src/App/Middleware/Event/EventParticipantSubscribeMiddleware.php b/src/App/Middleware/Event/EventParticipantSubscribeMiddleware.php new file mode 100644 index 00000000..0d76d5a2 --- /dev/null +++ b/src/App/Middleware/Event/EventParticipantSubscribeMiddleware.php @@ -0,0 +1,50 @@ +getAttribute('eventId'); + $event = $this->eventService->findById($eventId); + + if ($event->status->value >= EventStatus::RUNNING->value) { + return $handler->handle($request->withAttribute('participantCreateStatus', false)); + } + + /** + * @var User $user + */ + $user = $request->getAttribute(User::AUTHENTICATED_USER); + + $participant = new Participant( + 1, + $user->id, + $eventId, + new DateTimeImmutable(), + true, + false + ); + $participantCreateStatus = $this->participantService->create($participant); + + return $handler->handle($request->withAttribute('participantCreateStatus', $participantCreateStatus)); + } +} diff --git a/src/App/Middleware/Event/EventParticipantUnsubscribeMiddleware.php b/src/App/Middleware/Event/EventParticipantUnsubscribeMiddleware.php new file mode 100644 index 00000000..6072ffb4 --- /dev/null +++ b/src/App/Middleware/Event/EventParticipantUnsubscribeMiddleware.php @@ -0,0 +1,42 @@ +getAttribute(User::AUTHENTICATED_USER); + $eventId = (int)$request->getAttribute('eventId'); + + $event = $this->eventService->findById($eventId); + + if ($event->status->value >= EventStatus::RUNNING->value) { + return $handler->handle($request->withAttribute('participantRemoveStatus', false)); + } + + $participant = $this->participantService->findByUserIdAndEventId($user->id, $eventId); + + $participantRemoveStatus = $this->participantService->remove($participant); + + return $handler->handle($request->withAttribute('participantRemoveStatus', $participantRemoveStatus)); + } +} diff --git a/src/App/Middleware/Project/ProjectMiddleware.php b/src/App/Middleware/Project/ProjectMiddleware.php new file mode 100644 index 00000000..39bddb68 --- /dev/null +++ b/src/App/Middleware/Project/ProjectMiddleware.php @@ -0,0 +1,27 @@ +getAttribute('projectId'); + + $project = $this->projectService->findById($projectId); + + return $handler->handle($request->withAttribute(Project::class, $project)); + } +} diff --git a/src/App/Middleware/Project/ProjectOwnerMiddleware.php b/src/App/Middleware/Project/ProjectOwnerMiddleware.php new file mode 100644 index 00000000..16779bb4 --- /dev/null +++ b/src/App/Middleware/Project/ProjectOwnerMiddleware.php @@ -0,0 +1,30 @@ +getAttribute(Participant::class); + + $projectOwner = $this->userService->findById($participant->userId); + + return $handler->handle($request->withAttribute('projectOwner', $projectOwner)); + } +} diff --git a/src/App/Middleware/Project/ProjectParticipantMiddleware.php b/src/App/Middleware/Project/ProjectParticipantMiddleware.php new file mode 100644 index 00000000..9182cd33 --- /dev/null +++ b/src/App/Middleware/Project/ProjectParticipantMiddleware.php @@ -0,0 +1,31 @@ +getAttribute(Project::class); + + $participant = $this->participantService->findById($project->participantId); + + return $handler->handle($request->withAttribute(Participant::class, $participant)); + } +} diff --git a/src/App/Middleware/Token/AccessTokenValidationMiddleware.php b/src/App/Middleware/Token/AccessTokenValidationMiddleware.php deleted file mode 100644 index 747eb58d..00000000 --- a/src/App/Middleware/Token/AccessTokenValidationMiddleware.php +++ /dev/null @@ -1,47 +0,0 @@ -getHeaderLine('Authorization'); - - if (empty($accessToken)) { - throw new HttpUnauthorizedException( - LogMessage::ACCESS_TOKEN_MISSING, - StatusMessage::ACCOUNT_UNAUTHORIZED, - [], - Level::Warning - ); - } - - if (!$this->tokenService->isValid($accessToken)) { - throw new HttpUnauthorizedException( - LogMessage::ACCESS_TOKEN_EXPIRED, - StatusMessage::TOKEN_EXPIRED, - [ - 'Access Token:' => $accessToken, - ], - ); - } - - return $handler->handle($request); - } -} diff --git a/src/App/Middleware/Token/GenerateAccessTokenMiddleware.php b/src/App/Middleware/Token/GenerateAccessTokenMiddleware.php deleted file mode 100644 index 3c94050c..00000000 --- a/src/App/Middleware/Token/GenerateAccessTokenMiddleware.php +++ /dev/null @@ -1,31 +0,0 @@ -getAttribute(AccountInterface::AUTHENTICATED); - - $accessToken = $this->accessTokenService->generate($account->uuid); - - $accessToken = AccessToken::fromString($accessToken); - - return $handler->handle($request->withAttribute(AccessToken::class, $accessToken)); - } -} diff --git a/src/App/Middleware/Token/GenerateRefreshTokenMiddleware.php b/src/App/Middleware/Token/GenerateRefreshTokenMiddleware.php deleted file mode 100644 index bbbc10cc..00000000 --- a/src/App/Middleware/Token/GenerateRefreshTokenMiddleware.php +++ /dev/null @@ -1,30 +0,0 @@ -getAttribute(ClientIdentification::class); - - $refreshToken = $this->tokenService->generate($clientIdentification); - - $refreshToken = RefreshToken::fromString($refreshToken); - - return $handler->handle($request->withAttribute(RefreshToken::class, $refreshToken)); - } -} diff --git a/src/App/Middleware/Token/RefreshTokenAccountMiddleware.php b/src/App/Middleware/Token/RefreshTokenAccountMiddleware.php deleted file mode 100644 index 428ea70c..00000000 --- a/src/App/Middleware/Token/RefreshTokenAccountMiddleware.php +++ /dev/null @@ -1,46 +0,0 @@ -getAttribute(AccountAccessAuthInterface::class); - - /** @var null|AccountInterface $account */ - $account = $this->accountRepository->findById($accountAccessAuth->accountId); - - if ($account === null) { - throw new HttpUnauthorizedException( - LogMessage::REFRESH_TOKEN_ACCOUNT_NOT_FOUND, - StatusMessage::TOKEN_INVALID, - [ - 'AccessAuth ID:' => $accountAccessAuth->id, - 'Account ID:' => $accountAccessAuth->accountId, - ], - Level::Warning - ); - } - return $handler->handle($request->withAttribute(AccountInterface::AUTHENTICATED, $account)); - } -} diff --git a/src/App/Middleware/Token/RefreshTokenDatabaseExistenceMiddleware.php b/src/App/Middleware/Token/RefreshTokenDatabaseExistenceMiddleware.php deleted file mode 100644 index b2883911..00000000 --- a/src/App/Middleware/Token/RefreshTokenDatabaseExistenceMiddleware.php +++ /dev/null @@ -1,43 +0,0 @@ -getAttribute(RefreshToken::class); - - $persistToken = $this->accessAuthRepository->findByRefreshToken($refreshToken->refreshToken); - if (!($persistToken instanceof AccountAccessAuthInterface)) { - throw new HttpUnauthorizedException( - LogMessage::REFRESH_TOKEN_NOT_FOUND, - StatusMessage::TOKEN_NOT_PERSISTENT, - [ - 'Refresh Token:' => $refreshToken, - ], - Level::Warning - ); - } - - return $handler->handle($request->withAttribute(AccountAccessAuthInterface::class, $persistToken)); - } -} diff --git a/src/App/Middleware/Token/RefreshTokenMatchClientIdentificationMiddleware.php b/src/App/Middleware/Token/RefreshTokenMatchClientIdentificationMiddleware.php deleted file mode 100644 index 265b1a93..00000000 --- a/src/App/Middleware/Token/RefreshTokenMatchClientIdentificationMiddleware.php +++ /dev/null @@ -1,43 +0,0 @@ -getAttribute(AccountAccessAuthInterface::class); - - /** @var ClientIdentification $clientIdentification */ - $clientIdentification = $request->getAttribute(ClientIdentification::class); - - if ($accountAccessAuth->clientIdentHash !== $clientIdentification->identificationHash) { - throw new HttpUnauthorizedException( - LogMessage::REFRESH_TOKEN_CLIENT_MISMATCH, - StatusMessage::CLIENT_UNEXPECTED, - [ - 'expected:' => $accountAccessAuth->clientIdentHash, - 'expected UserAgent' => $accountAccessAuth->userAgent, - 'current:' => $clientIdentification->identificationHash, - 'current UserAgent:' => $clientIdentification->clientIdentificationData->userAgent, - ], - Level::Warning - ); - } - - return $handler->handle($request); - } -} diff --git a/src/App/Middleware/Token/RefreshTokenValidationMiddleware.php b/src/App/Middleware/Token/RefreshTokenValidationMiddleware.php deleted file mode 100644 index 0ccdc0cf..00000000 --- a/src/App/Middleware/Token/RefreshTokenValidationMiddleware.php +++ /dev/null @@ -1,40 +0,0 @@ -getHeaderLine('Authentication'); - - if (!$this->tokenService->isValid($refreshToken)) { - throw new HttpUnauthorizedException( - LogMessage::REFRESH_TOKEN_INVALID, - StatusMessage::TOKEN_INVALID, - [ - 'Refresh Token:' => $refreshToken, - ], - ); - } - - $refreshToken = RefreshToken::fromString($refreshToken); - - return $handler->handle($request->withAttribute(RefreshToken::class, $refreshToken)); - } -} diff --git a/src/App/Middleware/Topic/TopicCreateSubmitMiddleware.php b/src/App/Middleware/Topic/TopicCreateSubmitMiddleware.php new file mode 100644 index 00000000..3d1589ce --- /dev/null +++ b/src/App/Middleware/Topic/TopicCreateSubmitMiddleware.php @@ -0,0 +1,46 @@ +getParsedBody(); + + $topic = $this->hydrator->hydrate($data, Topic::class); + + $existTopic = $this->topicPoolService->findByTopic($topic->topic); + + if ($existTopic instanceof Topic) { + throw new DuplicateNameHttpException(['topic' => ['topic' => 'The Topic is already present']]); + } + + $topic = $topic->with(uuid: $this->uuid); + + $this->topicPoolService->insert($topic); + + return $handler->handle($request->withAttribute(Topic::class, $topic)); + } +} diff --git a/src/App/Middleware/Topic/TopicCreateValidationMiddleware.php b/src/App/Middleware/Topic/TopicCreateValidationMiddleware.php new file mode 100644 index 00000000..9d2ac799 --- /dev/null +++ b/src/App/Middleware/Topic/TopicCreateValidationMiddleware.php @@ -0,0 +1,34 @@ +getParsedBody(); + + $this->validator->setData($data); + + if (!$this->validator->isValid()) { + throw new InvalidArgumentHttpException($this->validator->getMessages()); + } + + return $handler->handle($request->withParsedBody($this->validator->getValues())); + } +} diff --git a/src/App/Middleware/Topic/TopicEntryStatisticMiddleware.php b/src/App/Middleware/Topic/TopicEntryStatisticMiddleware.php new file mode 100644 index 00000000..3008b7f0 --- /dev/null +++ b/src/App/Middleware/Topic/TopicEntryStatisticMiddleware.php @@ -0,0 +1,24 @@ +topicPoolService->getEntriesStatistic(); + + return $handler->handle($request->withAttribute('topicEntriesStatistic', $data)); + } +} diff --git a/src/App/Middleware/Topic/TopicListAvailableMiddleware.php b/src/App/Middleware/Topic/TopicListAvailableMiddleware.php new file mode 100644 index 00000000..f5249469 --- /dev/null +++ b/src/App/Middleware/Topic/TopicListAvailableMiddleware.php @@ -0,0 +1,26 @@ +topicPoolService->findAvailable(); + $topics = new TopicListDto($topics); + + return $handler->handle($request->withAttribute(TopicListDto::class, $topics)); + } +} diff --git a/src/Core/Middleware/RouteNotFoundMiddleware.php b/src/App/Middleware/Topic/TopicListMiddleware.php similarity index 54% rename from src/Core/Middleware/RouteNotFoundMiddleware.php rename to src/App/Middleware/Topic/TopicListMiddleware.php index aa37efe6..3d647d38 100644 --- a/src/Core/Middleware/RouteNotFoundMiddleware.php +++ b/src/App/Middleware/Topic/TopicListMiddleware.php @@ -1,24 +1,24 @@ logger->notice('Route not found'); + $topics = $this->topicPoolService->findAll(); - return $handler->handle($request); + return $handler->handle($request->withAttribute('topics', $topics)); } } diff --git a/src/App/Repository/AccountAccessAuthRepository.php b/src/App/Repository/AccountAccessAuthRepository.php deleted file mode 100644 index ae69bc9c..00000000 --- a/src/App/Repository/AccountAccessAuthRepository.php +++ /dev/null @@ -1,71 +0,0 @@ -store->insert($accountAccessAuth); - } - - public function update(AccountAccessAuthInterface $accountAccessAuth): true - { - return $this->store->update($accountAccessAuth); - } - - public function deleteById(int $id): true - { - return $this->store->deleteById($id); - } - - public function findById(int $id): ?AccountAccessAuthInterface - { - return $this->store->findById($id); - } - - public function findByAccountId(int $accountId): AccountAccessAuthCollectionInterface - { - return $this->store->findByAccountId($accountId); - } - - public function findByAccountIdAndClientIdHash(int $accountId, string $clientHash): ?AccountAccessAuthInterface - { - return $this->store->findByAccountIdAndClientIdHash($accountId, $clientHash); - } - - public function findByLabel(string $label): AccountAccessAuthCollectionInterface - { - return $this->store->findByLabel($label); - } - - public function findByRefreshToken(string $refreshToken): ?AccountAccessAuthInterface - { - return $this->store->findByRefreshToken($refreshToken); - } - - public function findByUserAgent(string $userAgent): AccountAccessAuthCollectionInterface - { - return $this->store->findByUserAgent($userAgent); - } - - public function findByClientIdentHash(string $clientIdentHash): ?AccountAccessAuthInterface - { - return $this->store->findByClientIdentHash($clientIdentHash); - } - - public function findAll(): AccountAccessAuthCollectionInterface - { - return $this->store->findAll(); - } -} diff --git a/src/App/Repository/AccountActivationRepository.php b/src/App/Repository/AccountActivationRepository.php deleted file mode 100644 index a87a19e0..00000000 --- a/src/App/Repository/AccountActivationRepository.php +++ /dev/null @@ -1,57 +0,0 @@ -store->insert($data); - } - - public function update(AccountActivationInterface $data): true - { - return $this->store->update($data); - } - - public function findById(int $id): ?AccountActivationInterface - { - return $this->store->findById($id); - } - - public function findEmail(Email $email): AccountActivationCollectionInterface - { - return $this->store->findByEmail($email); - } - - public function findByToken(string $token): ?AccountActivationInterface - { - return $this->store->findByToken($token); - } - - public function findAll(): AccountActivationCollectionInterface - { - return $this->store->findAll(); - } - - public function deleteById(int $id): true - { - return $this->store->deleteById($id); - } - - public function deleteByEmail(Email $email): true - { - return $this->store->deleteByEmail($email); - } -} diff --git a/src/App/Repository/AccountRepository.php b/src/App/Repository/AccountRepository.php deleted file mode 100644 index 1fbe69e4..00000000 --- a/src/App/Repository/AccountRepository.php +++ /dev/null @@ -1,58 +0,0 @@ -store->insert($data); - } - - public function update(AccountInterface $data): true - { - return $this->store->update($data); - } - - public function deleteById(int $id): true - { - return $this->store->deleteById($id); - } - - public function findById(int $id): ?AccountInterface - { - return $this->store->findById($id); - } - - public function findByUuid(UuidInterface $uuid): ?AccountInterface - { - return $this->store->findByUuid($uuid); - } - - public function findByName(string $name): ?AccountInterface - { - return $this->store->findByName($name); - } - - public function findByEmail(Email $email): ?AccountInterface - { - return $this->store->findByEmail($email); - } - - public function findAll(): AccountCollectionInterface - { - return $this->store->findAll(); - } -} diff --git a/src/App/Repository/EventRepository.php b/src/App/Repository/EventRepository.php new file mode 100644 index 00000000..2c0a69dc --- /dev/null +++ b/src/App/Repository/EventRepository.php @@ -0,0 +1,21 @@ +store->insert($data); - } - - public function update(TokenInterface $data): true - { - return $this->store->update($data); - } - - public function findById(int $id): ?TokenInterface - { - return $this->store->findById($id); - } - - public function findByAccountId(int $accountId): TokenCollectionInterface - { - return $this->store->findByAccountId($accountId); - } - - public function findByToken(string $token): ?TokenInterface - { - return $this->store->findByToken($token); - } - - public function findAll(): TokenCollectionInterface - { - return $this->store->findAll(); - } - - public function deleteById(int $id): true - { - return $this->store->deleteById($id); - } - - public function deleteByAccountId(int $accountId): true - { - return $this->store->deleteByAccountId($accountId); - } -} diff --git a/src/App/Repository/TopicPoolRepository.php b/src/App/Repository/TopicPoolRepository.php new file mode 100644 index 00000000..e5e19714 --- /dev/null +++ b/src/App/Repository/TopicPoolRepository.php @@ -0,0 +1,27 @@ +accountRepository->findByEmail($email); - $token = $this->createPasswordChangeTokenForUserId($account->id); - $this->tokenRepository->insert($token); - $this->tokenService->sendEmail($email, $token); - } - - public function isEmailAvailable(Email $email): bool - { - $account = $this->accountRepository->findByEmail($email); - - return $account === null; - } - - public function createPasswordChangeTokenForUserId(int $userId): TokenInterface - { - return new Token( - id: null, - accountId: $userId, - tokenType: TokenType::EMail, - token: $this->uuid->uuid7(), - createdAt: new DateTimeImmutable() - ); - } - - public function cryptPassword(string $password): string - { - return password_hash($password, PASSWORD_BCRYPT); - } -} diff --git a/src/App/Service/Authentication/AuthenticationService.php b/src/App/Service/Authentication/AuthenticationService.php deleted file mode 100644 index bf48bac5..00000000 --- a/src/App/Service/Authentication/AuthenticationService.php +++ /dev/null @@ -1,11 +0,0 @@ -getIdentificationHash($clientIdentificationData); - } - - private function getIdentificationHash(ClientIdentificationData $clientIdentificationData): string - { - return hash('sha512', serialize($clientIdentificationData)); - } -} diff --git a/src/App/Service/EMail/EMailServiceInterface.php b/src/App/Service/EMail/EMailServiceInterface.php new file mode 100644 index 00000000..1ae4f642 --- /dev/null +++ b/src/App/Service/EMail/EMailServiceInterface.php @@ -0,0 +1,10 @@ +topic); + $text = sprintf( + "Check the new topic and approve it if necessary\r\n\r\nTitle:\r\n%s\r\n\r\nDescription:\r\n%s\r\n\r\nLink:\r\n%s/topic/%s", + $topic->topic, + $topic->description, + $this->projectUri, + $topic->uuid, + ); + + $email = (new Email()) + ->from($this->mailSender) + ->to('hackathon@exdrals.de') + ->subject($subject) + ->text($text); + + $this->mailer->send($email); + } +} diff --git a/src/App/Service/EMail/TopicCreateEMailServiceFactory.php b/src/App/Service/EMail/TopicCreateEMailServiceFactory.php new file mode 100644 index 00000000..b231a896 --- /dev/null +++ b/src/App/Service/EMail/TopicCreateEMailServiceFactory.php @@ -0,0 +1,20 @@ +get(Mailer::class); + + $mailSender = $container->get('config')['mailer']['from']; + $projectUri = $container->get('config')['project']['uri']; + + return new TopicCreateEMailService($mailer, $mailSender, $projectUri); + } +} diff --git a/src/App/Service/Event/EventService.php b/src/App/Service/Event/EventService.php new file mode 100644 index 00000000..cd685c3c --- /dev/null +++ b/src/App/Service/Event/EventService.php @@ -0,0 +1,94 @@ +isEventExist($event->title)) { + return false; + } + + $this->repository->insert($event); + + return true; + } + + public function findById(int $id): Event + { + $event = $this->repository->findById($id); + + if ($event === []) { + throw new InvalidArgumentException( + sprintf('Could not find Event with id %d', $id), + HTTP::STATUS_NOT_FOUND + ); + } + + return $this->hydrator->hydrate($event, Event::class); + } + + public function findByTitle(string $topic): ?Event + { + $event = $this->repository->findByTitle($topic); + + return $this->hydrator->hydrate($event, Event::class); + } + + /** + * @return array + */ + public function findAll(string $order = 'startedAt', string $sort = 'DESC'): array + { + $events = $this->repository->findAll($order, $sort); + + return $this->hydrator->hydrateList($events, Event::class); + } + + /** + * @return array|null + */ + public function findAllActive(): ?array + { + $events = $this->repository->findAllActive(); + + return $this->hydrator->hydrateList($events, Event::class); + } + + /** + * @return array|null + */ + public function findAllNotActive(): ?array + { + $events = $this->repository->findAllInactive(); + + return $this->hydrator->hydrateList($events, Event::class); + } + + public function isRatingCompleted(int $id): bool + { + $event = $this->findById($id); + + return $event->ratingCompleted; + } + + public function isEventExist(string $topic): bool + { + $event = $this->findByTitle($topic); + + return $event instanceof Event; + } +} diff --git a/src/App/Service/Event/EventServiceFactory.php b/src/App/Service/Event/EventServiceFactory.php new file mode 100644 index 00000000..2efb2f32 --- /dev/null +++ b/src/App/Service/Event/EventServiceFactory.php @@ -0,0 +1,48 @@ +get(EventRepository::class); + + /** @var ReflectionHydrator $hydrator */ + $hydrator = clone $container->get(ReflectionHydrator::class); + + /** @var DateTimeFormatterStrategy $strategy */ + $strategy = $container->get(DateTimeFormatterStrategy::class); + + $hydrator->addStrategy( + 'createdAt', + $strategy, + ); + + $hydrator->addStrategy( + 'startedAt', + $strategy, + ); + + $hydrator->addStrategy( + 'status', + new BackedEnumStrategy(EventStatus::class) + ); + + $hydrator->addStrategy( + 'uuid', + new UuidStrategy() + ); + + return new EventService($repository, $hydrator); + } +} diff --git a/src/App/Service/Participant/ParticipantService.php b/src/App/Service/Participant/ParticipantService.php new file mode 100644 index 00000000..f1ce23d1 --- /dev/null +++ b/src/App/Service/Participant/ParticipantService.php @@ -0,0 +1,77 @@ +isParticipantInEventExist($participant->userId, $participant->eventId)) { + return false; + } + + return $this->repository->insert($participant) !== 0; + } + + public function remove(Participant $participant): bool + { + return (int)$this->repository->remove($participant) !== 0; + } + + public function findById(int $id): Participant + { + $participant = $this->repository->findById($id); + + if ($participant === []) { + throw new InvalidArgumentException( + sprintf('Could not find Participant with id %d', $id), + HTTP::STATUS_NOT_FOUND + ); + } + + return $this->hydrator->hydrate($participant, Participant::class); + } + + public function findByUserId(int $userId): ?Participant + { + $participant = $this->repository->findByUserId($userId); + + return $this->hydrator->hydrate($participant, Participant::class); + } + + public function findByUserIdAndEventId(int $userId, int $eventId): ?Participant + { + $participant = $this->repository->findUserForAnEvent($userId, $eventId); + + return $this->hydrator->hydrate($participant, Participant::class); + } + + /** + * @return array|null + */ + public function findActiveParticipantByEvent(int $eventId): ?array + { + $participants = $this->repository->findActiveParticipantsByEvent($eventId); + + return $this->hydrator->hydrateList($participants, Participant::class); + } + + private function isParticipantInEventExist(int $userId, int $eventId): bool + { + $participant = $this->findByUserIdAndEventId($userId, $eventId); + + return $participant instanceof Participant; + } +} diff --git a/src/App/Service/Participant/ParticipantServiceFactory.php b/src/App/Service/Participant/ParticipantServiceFactory.php new file mode 100644 index 00000000..eb8e157a --- /dev/null +++ b/src/App/Service/Participant/ParticipantServiceFactory.php @@ -0,0 +1,30 @@ +get(ParticipantRepository::class); + + /** @var ReflectionHydrator $hydrator */ + $hydrator = clone $container->get(ReflectionHydrator::class); + + /** @var DateTimeFormatterStrategy $strategy */ + $strategy = $container->get(DateTimeFormatterStrategy::class); + + $hydrator->addStrategy( + 'requestTime', + $strategy, + ); + + return new ParticipantService($repository, $hydrator); + } +} diff --git a/src/App/Service/Project/ProjectService.php b/src/App/Service/Project/ProjectService.php new file mode 100644 index 00000000..f340cb6d --- /dev/null +++ b/src/App/Service/Project/ProjectService.php @@ -0,0 +1,41 @@ +repository->findById($id); + + if ($project === []) { + throw new InvalidArgumentException( + sprintf('Project with id %d not found', $id), + HTTP::STATUS_NOT_FOUND + ); + } + + return $this->hydrator->hydrate($project, Project::class); + } + + public function findByParticipantId(int $id): ?Project + { + $project = $this->repository->findByParticipantId($id); + + return $this->hydrator->hydrate($project, Project::class); + } +} diff --git a/src/App/Service/Project/ProjectServiceFactory.php b/src/App/Service/Project/ProjectServiceFactory.php new file mode 100644 index 00000000..784ded97 --- /dev/null +++ b/src/App/Service/Project/ProjectServiceFactory.php @@ -0,0 +1,36 @@ +get(ProjectRepository::class); + + /** @var ReflectionHydrator $hydrator */ + $hydrator = clone $container->get(ReflectionHydrator::class); + + /** @var DateTimeFormatterStrategy $strategy */ + $strategy = $container->get(DateTimeFormatterStrategy::class); + + $hydrator->addStrategy( + 'createdAt', + $strategy, + ); + + $hydrator->addStrategy( + 'uuid', + new UuidStrategy() + ); + + return new ProjectService($repository, $hydrator); + } +} diff --git a/src/App/Service/Token/AccessTokenService.php b/src/App/Service/Token/AccessTokenService.php deleted file mode 100644 index 016a53aa..00000000 --- a/src/App/Service/Token/AccessTokenService.php +++ /dev/null @@ -1,34 +0,0 @@ - $this->config->iss, - 'aud' => $this->config->aud, - 'iat' => $now, - 'exp' => $now + $this->config->duration, - 'uuid' => $uuid->getHex()->toString(), - ]; - - return JWT::encode($payload, $this->config->key, $this->config->algorithmus); - } -} diff --git a/src/App/Service/Token/AccessTokenServiceFactory.php b/src/App/Service/Token/AccessTokenServiceFactory.php deleted file mode 100644 index ba0b2d36..00000000 --- a/src/App/Service/Token/AccessTokenServiceFactory.php +++ /dev/null @@ -1,17 +0,0 @@ -get('config')['jwt_token']['access']; - $jwtTokenConfig = JwtTokenConfig::createFromArray($jwtTokenConfig); - - return new AccessTokenService($jwtTokenConfig); - } -} diff --git a/src/App/Service/Token/ActivationTokenService.php b/src/App/Service/Token/ActivationTokenService.php deleted file mode 100644 index c7444652..00000000 --- a/src/App/Service/Token/ActivationTokenService.php +++ /dev/null @@ -1,30 +0,0 @@ -token->getHex()->toString()); - - $email = new Email() - ->from('no-reply@stormannsgal.de') - ->to($activation->email->toString()) - ->subject('Account Activation Code') - ->text($text); - - $this->mailer->send($email); - } -} diff --git a/src/App/Service/Token/JwtTokenTrait.php b/src/App/Service/Token/JwtTokenTrait.php deleted file mode 100644 index db9d14a9..00000000 --- a/src/App/Service/Token/JwtTokenTrait.php +++ /dev/null @@ -1,42 +0,0 @@ -config->key, $this->config->algorithmus)); - } catch ( - InvalidArgumentException - | DomainException - | UnexpectedValueException - | SignatureInvalidException - | BeforeValidException - | ExpiredException $e - ) { - return false; - } - - return true; - } - - public function decode(string $token): object - { - if (!$this->isValid($token)) { - return throw new InvalidArgumentException(); - } - - return JWT::decode($token, new Key($this->config->key, $this->config->algorithmus)); - } -} diff --git a/src/App/Service/Token/PasswordTokenService.php b/src/App/Service/Token/PasswordTokenService.php deleted file mode 100644 index 962fc064..00000000 --- a/src/App/Service/Token/PasswordTokenService.php +++ /dev/null @@ -1,31 +0,0 @@ -token->getHex()->toString()); - - $email = new Email() - ->from('no-reply@stormannsgal.de') - ->to($email->toString()) - ->subject('Password Forgotten Code') - ->text($text); - - $this->mailer->send($email); - } -} diff --git a/src/App/Service/Token/RefreshTokenService.php b/src/App/Service/Token/RefreshTokenService.php deleted file mode 100644 index 3863e2bb..00000000 --- a/src/App/Service/Token/RefreshTokenService.php +++ /dev/null @@ -1,34 +0,0 @@ - $this->config->iss, - 'aud' => $this->config->aud, - 'iat' => $now, - 'exp' => $now + $this->config->duration, - 'ident' => $clientIdentification->identificationHash, - ]; - - return JWT::encode($payload, $this->config->key, $this->config->algorithmus); - } -} diff --git a/src/App/Service/Token/RefreshTokenServiceFactory.php b/src/App/Service/Token/RefreshTokenServiceFactory.php deleted file mode 100644 index 9f0877f8..00000000 --- a/src/App/Service/Token/RefreshTokenServiceFactory.php +++ /dev/null @@ -1,17 +0,0 @@ -get('config')['jwt_token']['refresh']; - $jwtTokenConfig = JwtTokenConfig::createFromArray($jwtTokenConfig); - - return new RefreshTokenService($jwtTokenConfig); - } -} diff --git a/src/App/Service/Topic/TopicPoolService.php b/src/App/Service/Topic/TopicPoolService.php new file mode 100644 index 00000000..37f907e0 --- /dev/null +++ b/src/App/Service/Topic/TopicPoolService.php @@ -0,0 +1,112 @@ +repository->insert($topic); + } catch (PDOException $e) { + /** TODO: Change to Logger */ + throw new HttpException(['PDO' => $e->getMessage()], HTTP::STATUS_INTERNAL_SERVER_ERROR); + } + + return $this; + } + + public function updateEventId(Topic $topic): self + { + $this->repository->assignAnEvent($topic->id, $topic->eventId); + + return $this; + } + + public function findById(int $id): Topic + { + $event = $this->repository->findById($id); + + if ($event === []) { + throw new InvalidArgumentException( + sprintf('Could not find Event with id %d', $id), + HTTP::STATUS_NOT_FOUND + ); + } + + return $this->hydrator->hydrate($event, Topic::class); + } + + public function findByEventId(int $id): ?Topic + { + $topic = $this->repository->findByEventId($id); + + return $this->hydrator->hydrate($topic, Topic::class); + } + + /** + * @return array|null + */ + public function findAvailable(): ?array + { + $topics = $this->repository->findAvailable(); + + return $this->hydrator->hydrateList($topics, Topic::class); + } + + /** + * @return array|null + */ + public function findAll(): ?array + { + $topics = $this->repository->findAll(); + + return $this->hydrator->hydrateList($topics, Topic::class); + } + + public function isTopic(string $topic): bool + { + $topic = $this->findByTopic($topic); + + return $topic instanceof Topic; + } + + public function findByTopic(string $topic): ?Topic + { + $topic = $this->repository->findByTopic($topic); + + return $this->hydrator->hydrate($topic, Topic::class); + } + + #[ArrayShape([ + 'allTopic' => 'int', + 'allAcceptedTopic' => 'int', + 'allSelectionAvailableTopic' => 'int', + ])] + public function getEntriesStatistic(): array + { + return [ + 'allTopic' => $this->repository->getCountTopic(), + 'allAcceptedTopic' => $this->repository->getCountTopicAccepted(), + 'allSelectionAvailableTopic' => $this->repository->getCountTopicSelectionAvailable(), + ]; + } +} diff --git a/src/App/Service/Topic/TopicPoolServiceFactory.php b/src/App/Service/Topic/TopicPoolServiceFactory.php new file mode 100644 index 00000000..f1d579a2 --- /dev/null +++ b/src/App/Service/Topic/TopicPoolServiceFactory.php @@ -0,0 +1,27 @@ +get(TopicPoolRepository::class); + + /** @var ReflectionHydrator $hydrator */ + $hydrator = clone $container->get(ReflectionHydrator::class); + + $hydrator->addStrategy( + 'uuid', + new UuidStrategy() + ); + + return new TopicPoolService($repository, $hydrator); + } +} diff --git a/src/App/Service/User/UserService.php b/src/App/Service/User/UserService.php new file mode 100644 index 00000000..9cff7f00 --- /dev/null +++ b/src/App/Service/User/UserService.php @@ -0,0 +1,95 @@ +repository->updateLastUserActionTime($user->id, new DateTime()); + + return $user; + } + + public function create(User $user, UserRole $role = UserRole::USER): int + { + if ($this->isEmailExist($user->email)) { + return throw new DuplicateEntryException('User', $user->uuid->getHex()->toString()); + } + + $hashedPassword = password_hash($user->password, PASSWORD_BCRYPT); + + $user = $user->with( + password: $hashedPassword, + role: $role, + uuid: $this->uuid, + ); + + return $this->repository->insert($user); + } + + public function update(User $user): bool + { + return (bool)$this->repository->update($user); + } + + // @phpstan-ignore-next-line + private function isUserExist(string $userName): bool + { + $user = $this->findByName($userName); + + return $user instanceof User; + } + + private function isEmailExist(string $email): bool + { + $user = $this->findByEMail($email); + + return ($user instanceof User); + } + + public function findById(int $id): ?User + { + $user = $this->repository->findById($id); + + return $user !== [] ? $this->hydrator->hydrate($user, User::class) : null; + } + + public function findByUuid(string $uuid): ?User + { + $user = $this->repository->findByUuid($uuid); + + return $user !== [] ? $this->hydrator->hydrate($user, User::class) : null; + } + + public function findByName(string $name): ?User + { + $user = $this->repository->findByName($name); + + return $user !== [] ? $this->hydrator->hydrate($user, User::class) : null; + } + + public function findByEMail(string $email): ?User + { + $user = $this->repository->findByEMail($email); + + return $user !== [] ? $this->hydrator->hydrate($user, User::class) : null; + } +} diff --git a/src/App/Service/User/UserServiceFactory.php b/src/App/Service/User/UserServiceFactory.php new file mode 100644 index 00000000..21b3e213 --- /dev/null +++ b/src/App/Service/User/UserServiceFactory.php @@ -0,0 +1,52 @@ +get(UserRepository::class); + + /** @var ReflectionHydrator $hydrator */ + $hydrator = clone $container->get(ReflectionHydrator::class); + + /** @var DateTimeImmutableFormatterStrategy $dateTimeFormatterStrategy */ + $dateTimeFormatterStrategy = $container->get(DateTimeImmutableFormatterStrategy::class); + + /** @var Uuid $uuid */ + $uuid = $container->get(Uuid::class); + + $hydrator->addStrategy( + 'registrationAt', + $dateTimeFormatterStrategy, + ); + + $hydrator->addStrategy( + 'lastActionAt', + $dateTimeFormatterStrategy, + ); + + $hydrator->addStrategy( + 'role', + new BackedEnumStrategy(UserRole::class) + ); + + $hydrator->addStrategy( + 'uuid', + new UuidStrategy() + ); + + return new UserService($repository, $hydrator, $uuid); + } +} diff --git a/src/App/Table/AbstractTable.php b/src/App/Table/AbstractTable.php deleted file mode 100644 index 859829c5..00000000 --- a/src/App/Table/AbstractTable.php +++ /dev/null @@ -1,44 +0,0 @@ -table = substr(new ReflectionClass($this)->getShortName(), 0, -5); - $this->query = $query; - } - - public function getTableName(): string - { - return $this->table; - } - - /** - * @throws Exception - */ - public function deleteById(int $id): true - { - $result = $this->query->delete($this->table, $id)->execute(); - - if ($result === false) { - throw new InvalidArgumentException( - sprintf('Failed to delete %s table with id: `%s`', $this->getTableName(), $id) - ); - } - - return true; - } -} diff --git a/src/App/Table/AccountAccessAuthTable.php b/src/App/Table/AccountAccessAuthTable.php deleted file mode 100644 index f0aa363c..00000000 --- a/src/App/Table/AccountAccessAuthTable.php +++ /dev/null @@ -1,143 +0,0 @@ -hydrator->extract($data); - - unset($value['id']); - - try { - $lastInsertId = $this->query->insertInto($this->table, $value)->execute(); - } catch (Exception | PDOException $e) { - return throw new DuplicateEntryException($this->getTableName(), $data->id); - } - - return true; - } - - public function update(AccountAccessAuthInterface $data): true - { - $value = $this->hydrator->extract($data); - - $result = $this->query->update($this->table, $value, $data->id)->execute(); - - if ($result === false) { - throw new InvalidArgumentException( - sprintf('Unknown Error while updating %s with id: %s', $this->getTableName(), $data->id) - ); - } - - return true; - } - - public function findById(int $id): ?AccountAccessAuthInterface - { - $result = $this->query->from($this->table) - ->where('id', $id) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findByAccountId(int $accountId): AccountAccessAuthCollectionInterface - { - $result = $this->query->from($this->table) - ->where('userId', $accountId) - ->fetchAll(); - - return is_array($result) - ? $this->hydrator->hydrateCollection($result) - : $this->hydrator->hydrateCollection( - [] - ); - } - - public function findByAccountIdAndClientIdHash(int $accountId, string $clientHash): ?AccountAccessAuthInterface - { - $result = $this->query->from($this->table) - ->where('accountId', $accountId) - ->where('clientIdentHash', $clientHash) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findByLabel(string $label): AccountAccessAuthCollectionInterface - { - $result = $this->query->from($this->table) - ->where('label', $label) - ->fetchAll(); - - return is_array($result) - ? $this->hydrator->hydrateCollection($result) - : $this->hydrator->hydrateCollection( - [] - ); - } - - public function findByRefreshToken(string $refreshToken): ?AccountAccessAuthInterface - { - $result = $this->query->from($this->table) - ->where('refreshToken', $refreshToken) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findByUserAgent(string $userAgent): AccountAccessAuthCollectionInterface - { - $result = $this->query->from($this->table) - ->where('userAgent', $userAgent) - ->fetchAll(); - - return is_array($result) - ? $this->hydrator->hydrateCollection($result) - : $this->hydrator->hydrateCollection( - [] - ); - } - - public function findByClientIdentHash(string $clientIdentHash): ?AccountAccessAuthInterface - { - $result = $this->query->from($this->table) - ->where('clientIdentHash', $clientIdentHash) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findAll(): AccountAccessAuthCollectionInterface - { - $result = $this->query->from($this->table)->fetchAll(); - - return is_array($result) - ? $this->hydrator->hydrateCollection($result) - : $this->hydrator->hydrateCollection( - [] - ); - } -} diff --git a/src/App/Table/AccountActivationTable.php b/src/App/Table/AccountActivationTable.php deleted file mode 100644 index 137b1823..00000000 --- a/src/App/Table/AccountActivationTable.php +++ /dev/null @@ -1,105 +0,0 @@ -hydrator->extract($data); - - unset($value['id']); - - try { - $this->query->insertInto($this->table, $value)->execute(); - } catch (PDOException $e) { - throw new DuplicateEntryException($this->getTableName(), $data->id); - } - - return true; - } - - public function update(AccountActivationInterface $data): true - { - $value = $this->hydrator->extract($data); - - $result = $this->query->update($this->table, $value, $data->id)->execute(); - - if ($result === false) { - throw new InvalidArgumentException( - sprintf('Unknown Error while updating %s with id: %s', $this->getTableName(), $data->id) - ); - } - - return true; - } - - public function findById(int $id): ?AccountActivationInterface - { - $result = $this->query->from($this->table) - ->where('id', $id) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findByEmail(Email $email): AccountActivationCollectionInterface - { - $result = $this->query->from($this->table) - ->where('email', $email->toString()) - ->fetchAll(); - - return is_array($result) ? $this->hydrator->hydrateCollection($result) : $this->hydrator->hydrateCollection([]); - } - - public function findByToken(string $token): ?AccountActivationInterface - { - $result = $this->query->from($this->table) - ->where('token', $token) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findAll(): AccountActivationCollectionInterface - { - $result = $this->query->from($this->table)->fetchAll(); - - return is_array($result) ? $this->hydrator->hydrateCollection($result) : $this->hydrator->hydrateCollection([]); - } - - public function deleteByEmail(Email $email): true - { - $result = $this->query->delete($this->table) - ->where('email', $email->toString()) - ->execute(); - - if ($result === false) { - throw new InvalidArgumentException( - sprintf('Failed to delete %s table with email: `%s`', $this->getTableName(), $email->toString()) - ); - } - - return true; - } -} diff --git a/src/App/Table/AccountTable.php b/src/App/Table/AccountTable.php deleted file mode 100644 index 2bc5dca5..00000000 --- a/src/App/Table/AccountTable.php +++ /dev/null @@ -1,105 +0,0 @@ -hydrator->extract($data); - - unset($value['id']); - - try { - $this->query->insertInto($this->table, $value)->execute(); - } catch (PDOException $e) { - throw new DuplicateEntryException($this->getTableName(), $data->id); - } - - return true; - } - - public function update(AccountInterface $data): true - { - $value = $this->hydrator->extract($data); - - $result = $this->query->update($this->table, $value, $data->id)->execute(); - - if ($result === false) { - throw new InvalidArgumentException( - sprintf('Unknown Error while updating %s with id: %s', $this->getTableName(), $data->id) - ); - } - - return true; - } - - public function findById(int $id): ?AccountInterface - { - $result = $this->query->from($this->table) - ->where('id', $id) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findByUuid(UuidInterface $uuid): ?AccountInterface - { - $result = $this->query->from($this->table) - ->where('uuid', $uuid->getHex()->toString()) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findByName(string $name): ?AccountInterface - { - $result = $this->query->from($this->table) - ->where('name', $name) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findByEmail(Email $email): ?AccountInterface - { - $result = $this->query->from($this->table) - ->where('email', $email->toString()) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findAll(): AccountCollectionInterface - { - $result = $this->query->from($this->table)->fetchAll(); - - return is_array($result) ? $this->hydrator->hydrateCollection($result) : $this->hydrator->hydrateCollection([]); - } -} diff --git a/src/App/Table/EventTable.php b/src/App/Table/EventTable.php new file mode 100644 index 00000000..d789fec6 --- /dev/null +++ b/src/App/Table/EventTable.php @@ -0,0 +1,75 @@ + $event->uuid->getHex()->toString(), + 'userId' => $event->userId, + 'title' => $event->title, + 'description' => $event->description, + 'eventText' => $event->eventText, + 'startedAt' => $event->startedAt->format('Y-m-d H:i'), + 'duration' => $event->duration, + ]; + + $insertStatus = $this->query->insertInto($this->table, $values)->execute(); + + if (!$insertStatus) { + throw new DuplicateEntryException('Event', $event->uuid->getHex()->toString()); + } + + return (int)$insertStatus; + } + + public function findAll(string $order = 'startedAt', string $sort = 'DESC'): array + { + $result = $this->query->from($this->table)->orderBy($order . ' ' . $sort)->fetchAll(); + + return $result ?: []; + } + + public function findByTitle(string $title): array + { + $result = $this->query->from($this->table) + ->where('title', $title) + ->fetch(); + + return $result ?: []; + } + + public function findAllActive(): array + { + $result = $this->query->from($this->table) + ->where('active', 1) + ->orderBy('startedAt DESC') + ->fetchAll(); + + return $result ?: []; + } + + public function findAllInactive(): array + { + $result = $this->query->from($this->table) + ->where('active', 0) + ->orderBy('startedAt DESC') + ->fetchAll(); + + return $result ?: []; + } + + public function remove(Event $event): bool + { + return $this->query->deleteFrom($this->table) + ->where('id', $event->id) + ->execute(); + } +} diff --git a/src/App/Table/ParticipantTable.php b/src/App/Table/ParticipantTable.php new file mode 100644 index 00000000..65a8532f --- /dev/null +++ b/src/App/Table/ParticipantTable.php @@ -0,0 +1,69 @@ + $participant->userId, + 'eventId' => $participant->eventId, + ]; + + $insertStatus = $this->query->insertInto($this->table, $values) + ->onDuplicateKeyUpdate(['subscribed' => 1]) + ->execute(); + + if (!$insertStatus) { + throw new DuplicateEntryException('Participant', (string)$participant->id); + } + + return (int)$insertStatus; + } + + public function remove(Participant $participant): bool + { + return (bool)$this->query->update($this->table) + ->set(['subscribed' => 0]) + ->where('userId', $participant->userId) + ->where('eventId', $participant->eventId) + ->execute(); + } + + public function findByUserId(int $userId): array + { + $result = $this->query->from($this->table) + ->where('userId', $userId) + ->fetch(); + + return $result ?: []; + } + + public function findUserForAnEvent(int $userId, int $eventId): array + { + $result = $this->query->from($this->table) + ->where('userId', $userId) + ->where('eventId', $eventId) + ->where('subscribed', 1) + ->fetch(); + + return $result ?: []; + } + + public function findActiveParticipantsByEvent(int $eventId): array + { + $result = $this->query->from($this->table) + ->where('eventId', $eventId) + ->where('subscribed', 1) + ->where('disqualified', 0) + ->fetchAll(); + + return $result ?: []; + } +} diff --git a/src/App/Table/ProjectTable.php b/src/App/Table/ProjectTable.php new file mode 100644 index 00000000..f6ecdc73 --- /dev/null +++ b/src/App/Table/ProjectTable.php @@ -0,0 +1,16 @@ +query->from($this->table) + ->where('participantId', $id) + ->fetch(); + } +} diff --git a/src/App/Table/TokenTable.php b/src/App/Table/TokenTable.php deleted file mode 100644 index 63425c1e..00000000 --- a/src/App/Table/TokenTable.php +++ /dev/null @@ -1,104 +0,0 @@ -hydrator->extract($data); - - unset($value['id']); - - try { - $this->query->insertInto($this->table, $value)->execute(); - } catch (PDOException $e) { - throw new DuplicateEntryException($this->getTableName(), $data->id); - } - - return true; - } - - public function update(TokenInterface $data): true - { - $value = $this->hydrator->extract($data); - - $result = $this->query->update($this->table, $value, $data->id)->execute(); - - if ($result === false) { - throw new InvalidArgumentException( - sprintf('Unknown Error while updating %s with id: %s', $this->getTableName(), $data->id) - ); - } - - return true; - } - - public function findById(int $id): ?TokenInterface - { - $result = $this->query->from($this->table) - ->where('id', $id) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findByAccountId(int $accountId): TokenCollectionInterface - { - $result = $this->query->from($this->table) - ->where('accountId', $accountId) - ->fetchAll(); - - return is_array($result) ? $this->hydrator->hydrateCollection($result) : $this->hydrator->hydrateCollection([]); - } - - public function findByToken(string $token): ?TokenInterface - { - $result = $this->query->from($this->table) - ->where('token', $token) - ->fetch(); - - return is_array($result) ? $this->hydrator->hydrate($result) : null; - } - - public function findAll(): TokenCollectionInterface - { - $result = $this->query->from($this->table)->fetchAll(); - - return is_array($result) ? $this->hydrator->hydrateCollection($result) : $this->hydrator->hydrateCollection([]); - } - - public function deleteByAccountId(int $accountId): true - { - $result = $this->query->delete($this->table) - ->where('accountId', $accountId) - ->execute(); - - if ($result === false) { - throw new InvalidArgumentException( - sprintf('Failed to delete %s table with accountId: `%s`', $this->getTableName(), $accountId) - ); - } - - return true; - } -} diff --git a/src/App/Table/TopicPoolTable.php b/src/App/Table/TopicPoolTable.php new file mode 100644 index 00000000..4189adb1 --- /dev/null +++ b/src/App/Table/TopicPoolTable.php @@ -0,0 +1,89 @@ + $topic->uuid->getHex()->toString(), + 'topic' => $topic->topic, + 'description' => $topic->description, + ]; + + $this->query->insertInto($this->table, $values)->execute(); + + return $this; + } + + public function findByUuId(string $uuid): bool|array + { + return $this->query->from($this->table) + ->where('uuid', $uuid) + ->fetch(); + } + + public function assignAnEvent(int $topicId, int $eventId): self + { + $values = [ + 'eventId' => $eventId, + ]; + $this->query->update($this->table, $values, $topicId)->execute(); + + return $this; + } + + public function findByEventId(int $eventId): bool|array + { + return $this->query->from($this->table) + ->where('eventId', $eventId) + ->fetch(); + } + + public function findAvailable(): bool|array + { + return $this->query->from($this->table) + ->where('eventId', null) + ->where('accepted', 1) + ->fetchAll(); + } + + public function findByTopic(string $topic): bool|array + { + return $this->query->from($this->table) + ->where('topic', $topic) + ->fetch(); + } + + public function getCountTopic(): int + { + $data = $this->query->from($this->table) + ->select('COUNT(id) AS countTopic') + ->fetch(); + return $data['countTopic']; + } + + public function getCountTopicAccepted(): int + { + $data = $this->query->from($this->table) + ->select('COUNT(id) AS countTopic') + ->where('accepted', 1) + ->fetch(); + return $data['countTopic']; + } + + public function getCountTopicSelectionAvailable(): int + { + $data = $this->query->from($this->table) + ->select('COUNT(id) AS countTopic') + ->where('accepted', 1) + ->where('eventId', null) + ->fetch(); + return $data['countTopic']; + } +} diff --git a/src/App/Validator/AccountActivationValidator.php b/src/App/Validator/AccountActivationValidator.php deleted file mode 100644 index 9a9ad148..00000000 --- a/src/App/Validator/AccountActivationValidator.php +++ /dev/null @@ -1,18 +0,0 @@ -add($this->accountNameInput); - $this->add($this->passwordInput); - } -} diff --git a/src/App/Validator/AuthenticationValidator.php b/src/App/Validator/AuthenticationValidator.php deleted file mode 100644 index f01eca9c..00000000 --- a/src/App/Validator/AuthenticationValidator.php +++ /dev/null @@ -1,18 +0,0 @@ -add($this->emailInput); - $this->add($this->passwordInput); - } -} diff --git a/src/App/Validator/EventCreateValidator.php b/src/App/Validator/EventCreateValidator.php new file mode 100644 index 00000000..e375eb59 --- /dev/null +++ b/src/App/Validator/EventCreateValidator.php @@ -0,0 +1,27 @@ +add($this->eventTitleInput); + $this->add($this->descriptionInput); + $this->add($this->eventTextInput); + $this->add($this->startTimeInput); + $this->add($this->durationInput); + } +} diff --git a/src/App/Validator/Input/Event/EventDescriptionInput.php b/src/App/Validator/Input/Event/EventDescriptionInput.php new file mode 100644 index 00000000..1fa4114c --- /dev/null +++ b/src/App/Validator/Input/Event/EventDescriptionInput.php @@ -0,0 +1,27 @@ +setRequired(false); + + $this->getFilterChain()->attachByName('StringTrim'); + + $this->getValidatorChain()->attachByName( + 'StringLength', + [ + 'encoding' => 'UTF-8', + 'min' => 10, + 'max' => 255, + 'inclusive' => true, + ] + ); + } +} diff --git a/src/App/Validator/Input/Event/EventDurationInput.php b/src/App/Validator/Input/Event/EventDurationInput.php new file mode 100644 index 00000000..689f0fad --- /dev/null +++ b/src/App/Validator/Input/Event/EventDurationInput.php @@ -0,0 +1,26 @@ +setRequired(true); + + $this->getValidatorChain()->attachByName( + 'GreaterThan', + [ + 'min' => 1, + 'max' => 356, + 'inclusive' => true, + ], + ); + + $this->getFilterChain()->attachByName('ToInt'); + } +} diff --git a/src/App/Validator/Input/Event/EventStartTimeInput.php b/src/App/Validator/Input/Event/EventStartTimeInput.php new file mode 100644 index 00000000..5d7f8b52 --- /dev/null +++ b/src/App/Validator/Input/Event/EventStartTimeInput.php @@ -0,0 +1,27 @@ +setRequired(true); + $this->setBreakOnFailure(true); + + $this->getValidatorChain()->attach( + new Date([ + 'format' => 'Y-m-d H:i:s', + 'strict' => true, + ]), + ); + + $this->getValidatorChain()->attach(new DateLessNow()); + } +} diff --git a/src/App/Validator/Input/Event/EventTextInput.php b/src/App/Validator/Input/Event/EventTextInput.php new file mode 100644 index 00000000..d8f70f34 --- /dev/null +++ b/src/App/Validator/Input/Event/EventTextInput.php @@ -0,0 +1,27 @@ +setRequired(false); + + $this->getFilterChain()->attachByName('StringTrim'); + + $this->getValidatorChain()->attachByName( + 'StringLength', + [ + 'encoding' => 'UTF-8', + 'min' => 50, + 'max' => 8192, + 'inclusive' => true, + ] + ); + } +} diff --git a/src/App/Validator/Input/Event/EventTitleInput.php b/src/App/Validator/Input/Event/EventTitleInput.php new file mode 100644 index 00000000..5dbdeded --- /dev/null +++ b/src/App/Validator/Input/Event/EventTitleInput.php @@ -0,0 +1,27 @@ +setRequired(true); + + $this->getFilterChain()->attachByName('StringTrim'); + + $this->getValidatorChain()->attachByName( + 'StringLength', + [ + 'encoding' => 'UTF-8', + 'min' => 3, + 'max' => 50, + 'inclusive' => true, + ] + ); + } +} diff --git a/src/App/Validator/Input/Topic/TopicDescriptionInput.php b/src/App/Validator/Input/Topic/TopicDescriptionInput.php new file mode 100644 index 00000000..d7a16a79 --- /dev/null +++ b/src/App/Validator/Input/Topic/TopicDescriptionInput.php @@ -0,0 +1,26 @@ +setRequired(true); + + $this->getFilterChain()->attachByName('StringTrim'); + + $this->getValidatorChain()->attachByName( + 'StringLength', + [ + 'encoding' => 'UTF-8', + 'min' => 20, + 'max' => 8096, + ] + ); + } +} diff --git a/src/App/Validator/Input/AccountNameInput.php b/src/App/Validator/Input/Topic/TopicInput.php similarity index 73% rename from src/App/Validator/Input/AccountNameInput.php rename to src/App/Validator/Input/Topic/TopicInput.php index efedd759..e2591493 100644 --- a/src/App/Validator/Input/AccountNameInput.php +++ b/src/App/Validator/Input/Topic/TopicInput.php @@ -1,14 +1,14 @@ setRequired(true); @@ -19,7 +19,7 @@ public function __construct() [ 'encoding' => 'UTF-8', 'min' => 3, - 'max' => 64, + 'max' => 50, ] ); } diff --git a/src/App/Validator/TopicCreateValidator.php b/src/App/Validator/TopicCreateValidator.php new file mode 100644 index 00000000..1d8516fb --- /dev/null +++ b/src/App/Validator/TopicCreateValidator.php @@ -0,0 +1,18 @@ +add($this->topicInput); + $this->add($this->topicDescriptionInput); + } +} diff --git a/src/Core/ConfigProvider.php b/src/Core/ConfigProvider.php index 17e6d5ca..264b8db6 100644 --- a/src/Core/ConfigProvider.php +++ b/src/Core/ConfigProvider.php @@ -2,10 +2,29 @@ namespace Core; -use App\Validator\Input\EmailInput; -use App\Validator\Input\PasswordInput; +use App\Service\User\UserService; +use Core\Handler\LoginHandlerFactory; +use Core\Handler\UserPasswordForgottonHandlerFactory; +use Core\Hydrator\ClassMethodsHydratorFactory; +use Core\Hydrator\DateTimeFormatterStrategyFactory; +use Core\Hydrator\DateTimeImmutableFormatterStrategyFactory; +use Core\Hydrator\NullableStrategyFactory; +use Core\Hydrator\ReflectionHydrator; +use Core\Listener\LoggingErrorListener; +use Core\Listener\LoggingErrorListenerFactory; +use Core\Middleware\JwtAuthenticationMiddlewareFactory; +use Core\Repository\UserRepository; +use Core\Service\ApiAccessService; +use Core\Service\ApiAccessServiceFactory; +use Core\Service\LoginAuthenticationService; +use Core\Table\UserTable; +use Core\Token\TokenService; +use Envms\FluentPDO\Query; +use Laminas\Hydrator\ClassMethodsHydrator; +use Laminas\Hydrator\Strategy\DateTimeFormatterStrategy; +use Laminas\Hydrator\Strategy\DateTimeImmutableFormatterStrategy; +use Laminas\Hydrator\Strategy\NullableStrategy; use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory; -use Psr\Log\LoggerInterface; class ConfigProvider { @@ -21,16 +40,50 @@ public function getDependencies(): array { return [ 'invokables' => [ - EmailInput::class => EmailInput::class, - PasswordInput::class => PasswordInput::class, + ReflectionHydrator::class, + + Validator\Input\EmailInput::class, + Validator\Input\PasswordInput::class, + Validator\Input\UsernameInput::class, ], 'aliases' => [ + UserRepository::class => UserTable::class, ], 'factories' => [ - Factory\ErrorResponseFactory::class => ConfigAbstractFactory::class, - Middleware\ApiErrorHandlerMiddleware::class => ConfigAbstractFactory::class, - Middleware\RouteNotFoundMiddleware::class => ConfigAbstractFactory::class, + ClassMethodsHydrator::class => ClassMethodsHydratorFactory::class, + DateTimeFormatterStrategy::class => DateTimeFormatterStrategyFactory::class, + DateTimeImmutableFormatterStrategy::class => DateTimeImmutableFormatterStrategyFactory::class, + + Handler\LoginHandler::class => LoginHandlerFactory::class, + Handler\UserHandler::class => ConfigAbstractFactory::class, + Handler\UserPasswordForgottonHandler::class => UserPasswordForgottonHandlerFactory::class, + + LoggingErrorListener::class => LoggingErrorListenerFactory::class, + + Middleware\ApiAccessMiddleware::class => ConfigAbstractFactory::class, + Middleware\JwtAuthenticationMiddleware::class => JwtAuthenticationMiddlewareFactory::class, + Middleware\LoginAuthenticationMiddleware::class => ConfigAbstractFactory::class, + Middleware\LoginValidationMiddleware::class => ConfigAbstractFactory::class, + Middleware\UserPasswordChangeMiddleware::class => ConfigAbstractFactory::class, + Middleware\UserPasswordChangeValidatorMiddleware::class => ConfigAbstractFactory::class, + Middleware\UserPasswordForgottenMiddleware::class => ConfigAbstractFactory::class, + Middleware\UserPasswordForgottenValidator::class => ConfigAbstractFactory::class, + Middleware\UserPasswordVerifyTokenMiddleware::class => ConfigAbstractFactory::class, + Middleware\UserRegisterMiddleware::class => ConfigAbstractFactory::class, + Middleware\UserRegisterValidationMiddleware::class => ConfigAbstractFactory::class, + Middleware\UpdateLastUserActionTimeMiddleware::class => ConfigAbstractFactory::class, + Middleware\UserMiddleware::class => ConfigAbstractFactory::class, + + NullableStrategy::class => NullableStrategyFactory::class, + + Service\ApiAccessService::class => ApiAccessServiceFactory::class, + Table\UserTable::class => ConfigAbstractFactory::class, + + Validator\LoginValidator::class => ConfigAbstractFactory::class, + Validator\PasswordForgottenEmailValidator::class => ConfigAbstractFactory::class, + Validator\RegisterValidator::class => ConfigAbstractFactory::class, + Validator\UserPasswordChangeValidator::class => ConfigAbstractFactory::class, ], ]; } @@ -38,14 +91,65 @@ public function getDependencies(): array public function getAbstractFactoryConfig(): array { return [ - Factory\ErrorResponseFactory::class => [ - LoggerInterface::class, + Handler\UserHandler::class => [ + ClassMethodsHydrator::class, + ], + Middleware\UserRegisterMiddleware::class => [ + UserService::class, + ReflectionHydrator::class, + ], + Middleware\ApiAccessMiddleware::class => [ + ApiAccessService::class, + ], + Middleware\UserPasswordForgottenMiddleware::class => [ + UserService::class, + TokenService::class, + ], + Middleware\UserPasswordChangeMiddleware::class => [ + UserService::class, + ], + Middleware\UserPasswordChangeValidatorMiddleware::class => [ + Validator\UserPasswordChangeValidator::class, + ], + Middleware\UserPasswordForgottenValidator::class => [ + Validator\PasswordForgottenEmailValidator::class, + ], + Middleware\UserPasswordVerifyTokenMiddleware::class => [ + UserService::class, + ], + Middleware\UserRegisterValidationMiddleware::class => [ + Validator\RegisterValidator::class, + ], + Middleware\LoginAuthenticationMiddleware::class => [ + UserService::class, + LoginAuthenticationService::class, + ], + Middleware\LoginValidationMiddleware::class => [ + Validator\LoginValidator::class, + ], + Middleware\UpdateLastUserActionTimeMiddleware::class => [ + UserService::class, + ], + Middleware\UserMiddleware::class => [ + UserService::class, + ], + + Table\UserTable::class => [ + Query::class, + ], + + Validator\LoginValidator::class => [ + Validator\Input\UsernameInput::class, + Validator\Input\PasswordInput::class, + ], + Validator\PasswordForgottenEmailValidator::class => [ + Validator\Input\EmailInput::class, ], - Middleware\ApiErrorHandlerMiddleware::class => [ - Factory\ErrorResponseFactory::class, + Validator\RegisterValidator::class => [ + Validator\Input\EmailInput::class, ], - Middleware\RouteNotFoundMiddleware::class => [ - LoggerInterface::class, + Validator\UserPasswordChangeValidator::class => [ + Validator\Input\PasswordInput::class, ], ]; } diff --git a/src/Core/Dto/ApiMeDto.php b/src/Core/Dto/ApiMeDto.php new file mode 100644 index 00000000..21e1747f --- /dev/null +++ b/src/Core/Dto/ApiMeDto.php @@ -0,0 +1,38 @@ +uuid = $user->uuid->getHex()->toString(); + $this->name = $user->name; + $this->role = $user->role->getRoleName(); + } +} diff --git a/src/Core/Dto/HttpStatusCodeMessage.php b/src/Core/Dto/HttpStatusCodeMessage.php new file mode 100644 index 00000000..781c960f --- /dev/null +++ b/src/Core/Dto/HttpStatusCodeMessage.php @@ -0,0 +1,35 @@ +status = $status; + $this->message = $message; + $this->data = $data; + } +} diff --git a/src/Core/Dto/SimpleMessageDto.php b/src/Core/Dto/SimpleMessageDto.php new file mode 100644 index 00000000..0ff6ba79 --- /dev/null +++ b/src/Core/Dto/SimpleMessageDto.php @@ -0,0 +1,20 @@ +message = $message; + } +} diff --git a/src/Core/Dto/User/LoginTokenDto.php b/src/Core/Dto/User/LoginTokenDto.php new file mode 100644 index 00000000..76034da5 --- /dev/null +++ b/src/Core/Dto/User/LoginTokenDto.php @@ -0,0 +1,20 @@ +token = $token; + } +} diff --git a/src/Core/Dto/User/LoginValidationFailureMessageDto.php b/src/Core/Dto/User/LoginValidationFailureMessageDto.php new file mode 100644 index 00000000..5e2868c6 --- /dev/null +++ b/src/Core/Dto/User/LoginValidationFailureMessageDto.php @@ -0,0 +1,36 @@ +message = $message; + $this->username = $data['username'] ?? null; + $this->password = $data['password'] ?? null; + } +} diff --git a/src/Core/Dto/User/UserLogInDataDto.php b/src/Core/Dto/User/UserLogInDataDto.php new file mode 100644 index 00000000..e5c8b902 --- /dev/null +++ b/src/Core/Dto/User/UserLogInDataDto.php @@ -0,0 +1,27 @@ +username = $username; + $this->password = $password; + } +} diff --git a/src/Core/Entity/Account/AccountAccessAuthCollectionInterface.php b/src/Core/Entity/Account/AccountAccessAuthCollectionInterface.php deleted file mode 100644 index 173da23d..00000000 --- a/src/Core/Entity/Account/AccountAccessAuthCollectionInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - 'Eigentümer', - AccountRoles::Administrator => 'Administrator', - AccountRoles::Moderator => 'Moderator', - AccountRoles::User => 'Benutzer', - AccountRoles::Guest => 'Gast' - }; - } -} diff --git a/src/Core/Enum/AccountVisibleStatus.php b/src/Core/Enum/AccountVisibleStatus.php deleted file mode 100644 index f0d1095f..00000000 --- a/src/Core/Enum/AccountVisibleStatus.php +++ /dev/null @@ -1,23 +0,0 @@ - 'online', - AccountVisibleStatus::NOT_PRESENT => 'Abwesend', - AccountVisibleStatus::DO_NOT_DISTURB => 'Bitte nicht stören', - AccountVisibleStatus::GHOST => 'unsichtbar', - AccountVisibleStatus::PERSONALIZED => 'personalisiert' - }; - } -} diff --git a/src/Core/Enum/DataType.php b/src/Core/Enum/DataType.php deleted file mode 100644 index fca7e15b..00000000 --- a/src/Core/Enum/DataType.php +++ /dev/null @@ -1,19 +0,0 @@ -value, $this->getHttpStatusCode(), $previous); - $this->context = $context; - $this->responseMessage = $responseMessage; - $this->logLevel = $loglevel; + $this->jsonMessage = $jsonMessage; + parent::__construct('', $code, $previous); } - abstract public function getHttpStatusCode(): int; - - public function getContext(): array - { - return $this->context; - } - - public function getResponseMessage(): StatusMessage - { - return $this->responseMessage; - } - - public function getLogLevel(): Level + public function getJSonMessage(): array { - return $this->logLevel; + return $this->jsonMessage; } } diff --git a/src/Core/Middleware/ApiErrorHandlerMiddleware.php b/src/Core/Exception/HttpExceptionMiddleware.php similarity index 52% rename from src/Core/Middleware/ApiErrorHandlerMiddleware.php rename to src/Core/Exception/HttpExceptionMiddleware.php index a9371244..33f814df 100644 --- a/src/Core/Middleware/ApiErrorHandlerMiddleware.php +++ b/src/Core/Exception/HttpExceptionMiddleware.php @@ -1,27 +1,21 @@ handle($request); - } catch (Throwable $e) { - return $this->errorResponseFactory->createFromThrowable($e); + } catch (HttpException $e) { + return new JsonResponse($e->getJSonMessage(), $e->getCode()); } } } diff --git a/src/Core/Exception/HttpHandledInvalidArgumentAsSuccessException.php b/src/Core/Exception/HttpHandledInvalidArgumentAsSuccessException.php deleted file mode 100644 index 5ea924b6..00000000 --- a/src/Core/Exception/HttpHandledInvalidArgumentAsSuccessException.php +++ /dev/null @@ -1,27 +0,0 @@ -get('config')['database']; + $settings = $container->get('config'); + $settings = $settings['database']; $dsn = $settings['driver'] === 'mysql' ? 'mysql:dbname=' . $settings['dbname'] . ';host=' . $settings['host'] . ';port=' . $settings['port'] @@ -25,7 +20,7 @@ public function __invoke(ContainerInterface $container): PDO $password = $settings['password']; $options = [ PDO::ATTR_ERRMODE => $settings['error'], - PDO::ATTR_EMULATE_PREPARES => $settings['emulate_prepares'], + PDO::ATTR_EMULATE_PREPARES => false, ]; return new PDO($dsn, $user, $password, $options); diff --git a/src/Core/Factory/ErrorResponseFactory.php b/src/Core/Factory/ErrorResponseFactory.php deleted file mode 100644 index d0c2222a..00000000 --- a/src/Core/Factory/ErrorResponseFactory.php +++ /dev/null @@ -1,51 +0,0 @@ -getHttpStatusCode(); - $logLevel = $e->getLogLevel(); - $logContext = $e->getContext(); - $responseMessage = $e->getResponseMessage(); - - $this->logger->log( - $logLevel->value, - sprintf('[%d] %s', $statusCode, $e->getMessage()), - $logContext - ); - } else { - $this->logger->log( - Level::Critical, - sprintf('[%d] Unhandled exception %s', $statusCode, $e->getMessage()), - ['exception' => $e] - ); - } - - $message = HttpResponseMessage::create($statusCode, $responseMessage); - return new JsonResponse($message, $message->statusCode); - } -} diff --git a/src/Core/Factory/LoggerFactory.php b/src/Core/Factory/LoggerFactory.php new file mode 100644 index 00000000..35978e94 --- /dev/null +++ b/src/Core/Factory/LoggerFactory.php @@ -0,0 +1,53 @@ +get('config')['logger']['path']; + + $date = (new DateTime())->format('Y-m-d'); + $path = rtrim($path, '/') . '/' . $date . '/'; + + if (!is_dir($path)) { + mkdir($path, 0775); + } + + $formatter = new Simple(Simple::DEFAULT_FORMAT, 'Y-m-d H:i:s'); + + $defaultWriter = new Stream($path . 'default.log'); + $defaultWriter->setFormatter($formatter); + + $errorWriter = new Stream($path . 'error.log'); + $errorFilter = new Priority(Logger::ERR); + $errorWriter->addFilter($errorFilter); + $errorWriter->setFormatter($formatter); + + $logger = new Logger(); + + $logger->addWriter($defaultWriter); + $logger->addWriter($errorWriter); + + $logger->addProcessor(new BaseInformationProcessor()); + $logger->addProcessor(new PsrPlaceholder()); + + return new PsrLoggerAdapter($logger); + } +} diff --git a/src/Core/Factory/MailFactory.php b/src/Core/Factory/MailFactory.php index 140c2c3e..ce2fde8a 100644 --- a/src/Core/Factory/MailFactory.php +++ b/src/Core/Factory/MailFactory.php @@ -4,12 +4,11 @@ use Psr\Container\ContainerInterface; use Symfony\Component\Mailer\Mailer; -use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mailer\Transport; -class MailFactory +readonly class MailFactory { - public function __invoke(ContainerInterface $container): MailerInterface + public function __invoke(ContainerInterface $container): Mailer { $settings = $container->get('config'); diff --git a/src/Core/Factory/QueryFactory.php b/src/Core/Factory/QueryFactory.php index 063a6deb..4b88cd3b 100644 --- a/src/Core/Factory/QueryFactory.php +++ b/src/Core/Factory/QueryFactory.php @@ -4,16 +4,10 @@ use Envms\FluentPDO\Query; use PDO; -use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; -use Psr\Container\NotFoundExceptionInterface; -class QueryFactory +readonly class QueryFactory { - /** - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ public function __invoke(ContainerInterface $container): Query { return new Query($container->get(PDO::class)); diff --git a/src/Core/Factory/UuidFactory.php b/src/Core/Factory/UuidFactory.php index cf9e115e..6d926697 100644 --- a/src/Core/Factory/UuidFactory.php +++ b/src/Core/Factory/UuidFactory.php @@ -2,12 +2,14 @@ namespace Core\Factory; -use Core\Utils\UuidFactoryInterface; +use Psr\Container\ContainerInterface; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; -class UuidFactory +readonly class UuidFactory { - public function __invoke(): UuidFactoryInterface + public function __invoke(ContainerInterface $container): UuidInterface { - return new \Core\Utils\UuidFactory(); + return Uuid::uuid7(); } } diff --git a/src/Core/Handler/LoginHandler.php b/src/Core/Handler/LoginHandler.php new file mode 100644 index 00000000..31f3f544 --- /dev/null +++ b/src/Core/Handler/LoginHandler.php @@ -0,0 +1,64 @@ +getAttribute(User::AUTHENTICATED_USER); + + $token = $this->generateToken( + $user->uuid->getHex()->toString(), + $this->tokenSecret, + $this->tokenDuration, + ); + + return new JsonResponse(new LoginTokenDto($token), HTTP::STATUS_OK); + } +} diff --git a/src/Core/Handler/LoginHandlerFactory.php b/src/Core/Handler/LoginHandlerFactory.php new file mode 100644 index 00000000..05b90f7f --- /dev/null +++ b/src/Core/Handler/LoginHandlerFactory.php @@ -0,0 +1,15 @@ +get('config')['token']['auth']; + + return new LoginHandler($token['secret'], (int)$token['duration']); + } +} diff --git a/src/Core/Handler/LogoutHandler.php b/src/Core/Handler/LogoutHandler.php new file mode 100644 index 00000000..c2e37e24 --- /dev/null +++ b/src/Core/Handler/LogoutHandler.php @@ -0,0 +1,32 @@ +getAttribute(User::class); + + $data = $this->hydrator->extract($user); + unset($data['id'], $data['password'], $data['email']); + + return new JsonResponse($data, HTTP::STATUS_OK); + } +} diff --git a/src/Core/Handler/UserPasswordChangeHandler.php b/src/Core/Handler/UserPasswordChangeHandler.php new file mode 100644 index 00000000..47a8cf03 --- /dev/null +++ b/src/Core/Handler/UserPasswordChangeHandler.php @@ -0,0 +1,17 @@ + 'Password was changed'], HTTP::STATUS_OK); + } +} diff --git a/src/Core/Handler/UserPasswordForgottonHandler.php b/src/Core/Handler/UserPasswordForgottonHandler.php new file mode 100644 index 00000000..0b2d2e5d --- /dev/null +++ b/src/Core/Handler/UserPasswordForgottonHandler.php @@ -0,0 +1,48 @@ +getAttribute(User::class); + + $email = (new Email()) + ->from($this->mailSender) + ->to($user->email) + ->subject('Password forgotton') + ->text( + sprintf( + 'Follow the link to change your password: %s/user/password/%s', + $this->projectUri, + $user->name, /** ToDo implements Token support */ + ) + ); + + $this->mailer->send($email); + + return new JsonResponse(['message' => 'Email was created and sent'], HTTP::STATUS_OK); + } +} diff --git a/src/Core/Handler/UserPasswordForgottonHandlerFactory.php b/src/Core/Handler/UserPasswordForgottonHandlerFactory.php new file mode 100644 index 00000000..b19bb54f --- /dev/null +++ b/src/Core/Handler/UserPasswordForgottonHandlerFactory.php @@ -0,0 +1,18 @@ +get(Mailer::class); + $mailSender = $container->get('config')['mailer']['from']; + $projectUri = $container->get('config')['project']['uri']; + + return new UserPasswordForgottonHandler($mailer, $mailSender, $projectUri); + } +} diff --git a/src/Core/Handler/UserPasswordVerifyTokenHandler.php b/src/Core/Handler/UserPasswordVerifyTokenHandler.php new file mode 100644 index 00000000..a9c0b05f --- /dev/null +++ b/src/Core/Handler/UserPasswordVerifyTokenHandler.php @@ -0,0 +1,17 @@ + 'Token verification successful'], HTTP::STATUS_OK); + } +} diff --git a/src/Core/Handler/UserRegisterSubmitHandler.php b/src/Core/Handler/UserRegisterSubmitHandler.php new file mode 100644 index 00000000..148ad2ea --- /dev/null +++ b/src/Core/Handler/UserRegisterSubmitHandler.php @@ -0,0 +1,33 @@ + 'Account was created'], HTTP::STATUS_OK); + } +} diff --git a/src/Core/Hydrator/ClassMethodsHydratorFactory.php b/src/Core/Hydrator/ClassMethodsHydratorFactory.php new file mode 100644 index 00000000..b19b4cd7 --- /dev/null +++ b/src/Core/Hydrator/ClassMethodsHydratorFactory.php @@ -0,0 +1,14 @@ +get(DateTimeFormatterStrategy::class); + + return new NullableStrategy($dateTimeFormatterStrategy); + } +} diff --git a/src/Core/Hydrator/ReflectionHydrator.php b/src/Core/Hydrator/ReflectionHydrator.php new file mode 100644 index 00000000..db839053 --- /dev/null +++ b/src/Core/Hydrator/ReflectionHydrator.php @@ -0,0 +1,49 @@ +hydrate($data, $className); + } + } + + return $hydratedList; + } + + public function hydrate(bool|array $data, string|object $object): ?object + { + if (!$data) { + return null; + } + + if (!is_object($object)) { + $object = new ReflectionClass($object); + $object = $object->newInstanceWithoutConstructor(); + } + + return parent::hydrate($data, $object); + } + + public function extractList(array $data): array + { + $extractedList = []; + + foreach ($data as $value) { + if (is_object($value)) { + $extractedList[] = $this->extract($value); + } + } + return $extractedList; + } +} diff --git a/src/Core/Hydrator/Strategy/UuidStrategy.php b/src/Core/Hydrator/Strategy/UuidStrategy.php new file mode 100644 index 00000000..0bd5a9a9 --- /dev/null +++ b/src/Core/Hydrator/Strategy/UuidStrategy.php @@ -0,0 +1,47 @@ +getHex()->toString(); + } + + public function hydrate($value, ?array $data) + { + if ($value instanceof UuidInterface) { + return $value; + } + + if (!is_string($value)) { + throw new InvalidArgumentException( + sprintf( + 'Value must be string; %s provided', + get_debug_type($value) + ) + ); + } + + return Uuid::fromString($value); + } +} diff --git a/src/Core/Listener/LoggingErrorListener.php b/src/Core/Listener/LoggingErrorListener.php new file mode 100644 index 00000000..2c4d3148 --- /dev/null +++ b/src/Core/Listener/LoggingErrorListener.php @@ -0,0 +1,32 @@ +getServerParams(); + + $this->logger->error( + '{Host} Code: {Code} - Message: {Message}', + [ + 'user-agent' => $serverParams['HTTP_USER_AGENT'], + 'Code' => $error->getCode(), + 'Message' => $error->getMessage(), + ], + ); + } +} diff --git a/src/Core/Listener/LoggingErrorListenerDelegatorFactory.php b/src/Core/Listener/LoggingErrorListenerDelegatorFactory.php new file mode 100644 index 00000000..3fc7da4b --- /dev/null +++ b/src/Core/Listener/LoggingErrorListenerDelegatorFactory.php @@ -0,0 +1,17 @@ +get(LoggingErrorListener::class); + $errorHandler = $callback(); + $errorHandler->attachListener($listener); + return $errorHandler; + } +} diff --git a/src/Core/Listener/LoggingErrorListenerFactory.php b/src/Core/Listener/LoggingErrorListenerFactory.php new file mode 100644 index 00000000..2e56cf95 --- /dev/null +++ b/src/Core/Listener/LoggingErrorListenerFactory.php @@ -0,0 +1,16 @@ +get(LoggerInterface::class); + + return new LoggingErrorListener($logger); + } +} diff --git a/src/Core/Logger/BaseInformationProcessor.php b/src/Core/Logger/BaseInformationProcessor.php new file mode 100644 index 00000000..470baa38 --- /dev/null +++ b/src/Core/Logger/BaseInformationProcessor.php @@ -0,0 +1,29 @@ +get('config')['logger']['path']; - - $date = (new DateTime())->format('Y-m-d'); - $path = rtrim($path, '/') . '/' . $date . '/'; - - if (!is_dir($path)) { - mkdir($path, 0775); - } - - $dateFormat = 'Y-m-d H:i:s'; - $output = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; - $formatter = new LineFormatter($output, $dateFormat); - $stackTraceFormater = clone $formatter; - $stackTraceFormater->includeStacktraces(true); - - $logger = new Logger('log'); - - $logger->pushHandler(new StreamHandler($path . 'default.log')->setFormatter($formatter)); - - $errorHandler = new StreamHandler($path . 'error.log', Level::Error)->setFormatter($formatter); - $errorHandler = new FilterHandler($errorHandler, Level::Error, Level::Error); - - $logger->pushHandler($errorHandler); - - $errorHandler = new StreamHandler($path . 'warning.log', Level::Warning)->setFormatter($formatter); - $errorHandler = new FilterHandler($errorHandler, Level::Error, Level::Error); - - $logger->pushHandler($errorHandler); - - $logger->pushHandler( - new StreamHandler($path . 'critical.log', Level::Critical)->setFormatter($stackTraceFormater) - ); - $logger->pushProcessor(new PsrLogMessageProcessor()); - $logger->pushProcessor( - new MetaDataProcessor( - filter_input(INPUT_SERVER, 'REMOTE_ADDR'), - filter_input(INPUT_SERVER, 'REQUEST_URI'), - filter_input(INPUT_SERVER, 'REQUEST_METHOD'), - filter_input(INPUT_SERVER, 'REDIRECT_URL'), - filter_input_array(INPUT_GET) - ) - ); - return $logger; - } -} diff --git a/src/Core/Logger/MetaDataProcessor.php b/src/Core/Logger/MetaDataProcessor.php deleted file mode 100644 index ff02b90f..00000000 --- a/src/Core/Logger/MetaDataProcessor.php +++ /dev/null @@ -1,29 +0,0 @@ -extra['Remote'] = $this->remoteAddr; - $record->extra['URI'] = $this->uri; - $record->extra['Method'] = $this->method; - $record->extra['Redirect'] = $this->redirect; - $record->extra['Query'] = $this->query; - - return $record; - } -} diff --git a/src/Core/Middleware/ApiAccessMiddleware.php b/src/Core/Middleware/ApiAccessMiddleware.php new file mode 100644 index 00000000..7df0c0ab --- /dev/null +++ b/src/Core/Middleware/ApiAccessMiddleware.php @@ -0,0 +1,30 @@ +getHeader('Host')[0])[0]; + + if (!$this->apiAccessService->hasAccessRights($domain)) { + return new JsonResponse(['message' => 'No access authorization'], HTTP::STATUS_UNAUTHORIZED); + } + + return $handler->handle($request); + } +} diff --git a/src/Core/Middleware/IsLoggedInAuthenticationMiddleware.php b/src/Core/Middleware/IsLoggedInAuthenticationMiddleware.php new file mode 100644 index 00000000..96feafb5 --- /dev/null +++ b/src/Core/Middleware/IsLoggedInAuthenticationMiddleware.php @@ -0,0 +1,29 @@ +getAttribute(User::AUTHENTICATED_USER); + + if (!$user) { + return new JsonResponse(new SimpleMessageDto('Authentication is required'), HTTP::STATUS_UNAUTHORIZED); + } + + return $handler->handle($request); + } +} diff --git a/src/Core/Middleware/JwtAuthenticationMiddleware.php b/src/Core/Middleware/JwtAuthenticationMiddleware.php new file mode 100644 index 00000000..2cae4b3d --- /dev/null +++ b/src/Core/Middleware/JwtAuthenticationMiddleware.php @@ -0,0 +1,60 @@ +getHeaderLine('Authorization'); + $token = substr($token, 7); + + $user = null; + + if ($token) { + try { + $tokenData = JWT::decode($token, new Key($this->tokenSecret, $this->tokenAlgorithmus)); + } catch (ExpiredException $e) { + $this->logger->notice( + '{Host} has call {URI} with expired Token', + [ + 'Host' => $request->getServerParams()['HTTP_HOST'], + 'URI' => $request->getServerParams()['REQUEST_URI'], + ] + ); + return new JsonResponse(['message' => 'invalid Token'], HTTP::STATUS_UNAUTHORIZED); + } + + $user = $this->userService->findByUuid($tokenData->uuid); + } + + $this->logger->info('{Host} as {User} call -> {URI}', [ + 'User' => $user ? $user->name : 'Guest', + ]); + + return $handler->handle($request->withAttribute(User::AUTHENTICATED_USER, $user)); + } +} diff --git a/src/Core/Middleware/JwtAuthenticationMiddlewareFactory.php b/src/Core/Middleware/JwtAuthenticationMiddlewareFactory.php new file mode 100644 index 00000000..1d916e19 --- /dev/null +++ b/src/Core/Middleware/JwtAuthenticationMiddlewareFactory.php @@ -0,0 +1,24 @@ +get(UserService::class); + $token = $container->get('config')['token']['auth']; + $logger = $container->get(LoggerInterface::class); + + return new JwtAuthenticationMiddleware( + $userService, + $token['secret'], + $token['algorithmus'], + $logger, + ); + } +} diff --git a/src/Core/Middleware/LoginAuthenticationMiddleware.php b/src/Core/Middleware/LoginAuthenticationMiddleware.php new file mode 100644 index 00000000..ab4efbda --- /dev/null +++ b/src/Core/Middleware/LoginAuthenticationMiddleware.php @@ -0,0 +1,41 @@ +getParsedBody(); + + $name = $data['username']; + $password = $data['password']; + + $user = $this->userService->findByName($name); + + if (!($user instanceof User) || !$this->authService->isUserDataCorrect($user, $password)) { + return new JsonResponse(new SimpleMessageDto('Login failed'), HTTP::STATUS_UNAUTHORIZED); + } + + return $handler->handle( + $request->withAttribute(User::AUTHENTICATED_USER, $user) + ); + } +} diff --git a/src/Core/Middleware/LoginValidationMiddleware.php b/src/Core/Middleware/LoginValidationMiddleware.php new file mode 100644 index 00000000..5acb8896 --- /dev/null +++ b/src/Core/Middleware/LoginValidationMiddleware.php @@ -0,0 +1,37 @@ +getParsedBody(); + + $this->validator->setData($data); + + if (!$this->validator->isValid()) { + // ToDo $this->validator->getMessage() + return new JsonResponse( + new LoginValidationFailureMessageDto('Login failed', $data), + HTTP::STATUS_BAD_REQUEST + ); + } + + return $handler->handle($request->withParsedBody($this->validator->getValues())); + } +} diff --git a/src/Core/Middleware/UpdateLastUserActionTimeMiddleware.php b/src/Core/Middleware/UpdateLastUserActionTimeMiddleware.php new file mode 100644 index 00000000..368b5e9b --- /dev/null +++ b/src/Core/Middleware/UpdateLastUserActionTimeMiddleware.php @@ -0,0 +1,29 @@ +getAttribute(User::AUTHENTICATED_USER); + + if ($user instanceof User) { + $user = $this->userService->updateLastUserActionTime($user); + } + + return $handler->handle($request->withAttribute(User::AUTHENTICATED_USER, $user)); + } +} diff --git a/src/Core/Middleware/UserMiddleware.php b/src/Core/Middleware/UserMiddleware.php new file mode 100644 index 00000000..ef4aa663 --- /dev/null +++ b/src/Core/Middleware/UserMiddleware.php @@ -0,0 +1,33 @@ +getAttribute('userUuid'); + + $user = $this->userService->findByUuid($userUuid); + + if (!$user) { + return new JsonResponse(['message' => 'User could not be found'], HTTP::STATUS_NOT_FOUND); + } + + return $handler->handle($request->withAttribute(User::class, $user)); + } +} diff --git a/src/Core/Middleware/UserPasswordChangeMiddleware.php b/src/Core/Middleware/UserPasswordChangeMiddleware.php new file mode 100644 index 00000000..41e7e0b6 --- /dev/null +++ b/src/Core/Middleware/UserPasswordChangeMiddleware.php @@ -0,0 +1,43 @@ +getAttribute(User::class); + + $data = $request->getParsedBody(); + + /** ToDo implements Token support */ + $user = $user->with(['password' => password_hash($data['password'], PASSWORD_BCRYPT)]); + + if (!$this->userService->update($user)) { + return new JsonResponse(['Password could not be changed'], Http::STATUS_BAD_REQUEST); + } + + return $handler->handle($request); + } +} diff --git a/src/Core/Middleware/UserPasswordChangeValidatorMiddleware.php b/src/Core/Middleware/UserPasswordChangeValidatorMiddleware.php new file mode 100644 index 00000000..f0e7e4df --- /dev/null +++ b/src/Core/Middleware/UserPasswordChangeValidatorMiddleware.php @@ -0,0 +1,35 @@ +getParsedBody(); + + $this->validator->setData($data); + + if (!$this->validator->isValid()) { + return new JsonResponse([ + 'message' => 'Validation fault', + 'data' => $this->validator->getMessages(), + ], HTTP::STATUS_NOT_FOUND); + } + + return $handler->handle($request->withParsedBody($this->validator->getValues())); + } +} diff --git a/src/Core/Middleware/UserPasswordForgottenMiddleware.php b/src/Core/Middleware/UserPasswordForgottenMiddleware.php new file mode 100644 index 00000000..93e7e94f --- /dev/null +++ b/src/Core/Middleware/UserPasswordForgottenMiddleware.php @@ -0,0 +1,40 @@ +getParsedBody(); + + $user = $this->userService->findByEMail($data['email']); + + if (!$user) { + return new JsonResponse(['message' => 'invalid E-Mai'], HTTP::STATUS_BAD_REQUEST); + } + + /** ToDo implements Token support */ + $this->tokenService->generateToken(); + + $this->userService->update($user); + + return $handler->handle($request->withAttribute(User::class, $user)); + } +} diff --git a/src/Core/Middleware/UserPasswordForgottenValidator.php b/src/Core/Middleware/UserPasswordForgottenValidator.php new file mode 100644 index 00000000..73a07a27 --- /dev/null +++ b/src/Core/Middleware/UserPasswordForgottenValidator.php @@ -0,0 +1,35 @@ +getParsedBody(); + + $this->validator->setData($data); + + if (!$this->validator->isValid()) { + return new JsonResponse([ + 'message' => 'Validation fault', + 'data' => $this->validator->getMessages(), + ], HTTP::STATUS_NOT_FOUND); + } + + return $handler->handle($request->withParsedBody($this->validator->getValues())); + } +} diff --git a/src/Core/Middleware/UserPasswordVerifyTokenMiddleware.php b/src/Core/Middleware/UserPasswordVerifyTokenMiddleware.php new file mode 100644 index 00000000..e28ece53 --- /dev/null +++ b/src/Core/Middleware/UserPasswordVerifyTokenMiddleware.php @@ -0,0 +1,37 @@ +getAttribute('token'); + + /** ToDo implements Token */ + $user = $this->userService->findById($token); + + if (!$user instanceof User) { + return new JsonResponse( + ['message' => 'Password cannot be changed due to invalid token'], + HTTP::STATUS_BAD_REQUEST + ); + } + + return $handler->handle($request->withAttribute(User::class, $user)); + } +} diff --git a/src/Core/Middleware/UserRegisterMiddleware.php b/src/Core/Middleware/UserRegisterMiddleware.php new file mode 100644 index 00000000..a70aa2ba --- /dev/null +++ b/src/Core/Middleware/UserRegisterMiddleware.php @@ -0,0 +1,64 @@ +getParsedBody(); + $newUser = [ + 'id' => 1, + 'uuid' => $this->uuid, + 'role' => UserRole::GUEST, + 'name' => '', + 'password' => '', + 'email' => $data['e-mail'], + 'registrationAt' => new DateTime(), + 'lastActionAt' => new DateTime(), + ]; + + $user = $this->hydrator->hydrate($newUser, User::class); + + try { + !$this->userService->create($user); + } catch (DuplicateEntryException $exception) { + $validationMessages = [ + 'email' => [ + 'message' => 'Invalid registration data', + ], + ]; + + return new JsonResponse( + new HttpStatusCodeMessage( + $exception->getCode(), + 'Registration failed', + $validationMessages + ), + $exception->getCode() + ); + } + return $handler->handle($request); + } +} diff --git a/src/Core/Middleware/UserRegisterValidationMiddleware.php b/src/Core/Middleware/UserRegisterValidationMiddleware.php new file mode 100644 index 00000000..ba4e1510 --- /dev/null +++ b/src/Core/Middleware/UserRegisterValidationMiddleware.php @@ -0,0 +1,39 @@ +getParsedBody(); + + $this->validator->setData($data); + + if (!$this->validator->isValid()) { + return new JsonResponse([ + new HttpStatusCodeMessage( + HTTP::STATUS_BAD_REQUEST, + 'Registration failed', + $this->validator->getMessages() + ), + ], HTTP::STATUS_BAD_REQUEST); + } + + return $handler->handle($request->withParsedBody($this->validator->getValues())); + } +} diff --git a/src/Core/Repository/AccountAccessAuthRepositoryInterface.php b/src/Core/Repository/AccountAccessAuthRepositoryInterface.php deleted file mode 100644 index 6258aa2e..00000000 --- a/src/Core/Repository/AccountAccessAuthRepositoryInterface.php +++ /dev/null @@ -1,29 +0,0 @@ -apiAccessConfig['domain']['whitelist'], true); + } +} diff --git a/src/Core/Service/ApiAccessServiceFactory.php b/src/Core/Service/ApiAccessServiceFactory.php new file mode 100644 index 00000000..32f7ecb9 --- /dev/null +++ b/src/Core/Service/ApiAccessServiceFactory.php @@ -0,0 +1,15 @@ +get('config')['api']['access']; + + return new ApiAccessService($apiAccessConfig); + } +} diff --git a/src/Core/Service/LoginAuthenticationService.php b/src/Core/Service/LoginAuthenticationService.php new file mode 100644 index 00000000..6c006da3 --- /dev/null +++ b/src/Core/Service/LoginAuthenticationService.php @@ -0,0 +1,19 @@ +password); + } +} diff --git a/src/Core/Store/AccountAccessAuthStoreInterface.php b/src/Core/Store/AccountAccessAuthStoreInterface.php deleted file mode 100644 index 73487b96..00000000 --- a/src/Core/Store/AccountAccessAuthStoreInterface.php +++ /dev/null @@ -1,29 +0,0 @@ -table = substr((new ReflectionClass($this))->getShortName(), 0, -5); + } + + public function getTableName(): string + { + return $this->table; + } + + public function findById(int $id): array + { + $result = $this->query->from($this->table) + ->where('id', $id) + ->fetch(); + + return $result ?: []; + } + + public function findAll(): array + { + $result = $this->query->from($this->table)->fetchAll(); + + return $result ?: []; + } +} diff --git a/src/Core/Table/UserTable.php b/src/Core/Table/UserTable.php new file mode 100644 index 00000000..fca4e705 --- /dev/null +++ b/src/Core/Table/UserTable.php @@ -0,0 +1,93 @@ + $user->uuid->getHex()->toString(), + 'roleId' => $user->role->value, + 'name' => $user->name, + 'password' => $user->password, + 'email' => $user->email, + ]; + + $lastInsertId = $this->query->insertInto($this->table, $values)->execute(); + + if (!$lastInsertId) { + return throw new DuplicateEntryException('User', $user->uuid->getHex()->toString()); + } + + return (int)$lastInsertId; + } + + public function update(User $user): int + { + $values = [ + 'uuid' => $user->uuid, + 'roleId' => $user->role->value, + 'name' => $user->name, + 'password' => $user->password, + 'email' => $user->email, + 'registrationAt' => $user->registrationAt->format('Y-m-d H:i:s'), + 'lastActionAt' => $user->lastActionAt->format('Y-m-d H:i:s'), + ]; + + $affectedRowCount = $this->query->update($this->table, $values, $user->id)->execute(); + + if (!$affectedRowCount) { + throw new InvalidArgumentException('User data could not be modified'); + } + + return (int)$affectedRowCount; + } + + public function updateLastUserActionTime(int $id, DateTime $actionTime): self + { + $result = $this->query->update($this->table) + ->set(['lastActionAt' => $actionTime->format('Y-m-d H:i:s')]) + ->where('id', $id) + ->execute(); + + if (!$result) { + throw new InvalidArgumentException('User data could not be modified'); + } + + return $this; + } + + public function findByUuid(string $uuid): array + { + $result = $this->query->from($this->table) + ->where('uuid', $uuid) + ->fetch(); + + return $result ?: []; + } + + public function findByName(string $name): array + { + $result = $this->query->from($this->table) + ->where('name', $name) + ->fetch(); + + return $result ?: []; + } + + public function findByEMail(string $email): array + { + $result = $this->query->from($this->table) + ->where('email', $email) + ->fetch(); + + return $result ?: []; + } +} diff --git a/src/Core/Token/JwtTokenGeneratorTrait.php b/src/Core/Token/JwtTokenGeneratorTrait.php new file mode 100644 index 00000000..fca83751 --- /dev/null +++ b/src/Core/Token/JwtTokenGeneratorTrait.php @@ -0,0 +1,26 @@ + $now, + 'exp' => $now + $timeout, + + 'uuid' => $uuid, + ], + $tokenSecret, + $alg + ); + } +} diff --git a/src/Core/Token/TokenService.php b/src/Core/Token/TokenService.php new file mode 100644 index 00000000..0d55c8cf --- /dev/null +++ b/src/Core/Token/TokenService.php @@ -0,0 +1,14 @@ +value = $value instanceof self ? (string)$value : $this->prepareValue($value); - } - - public function toString(): string - { - return $this->value; - } - - public function __toString(): string - { - return $this->toString(); - } - - public function serialize(): string - { - return $this->toString(); - } - - public function __serialize(): array - { - return ['string' => $this->toString()]; - } - - public function unserialize(string $data): void - { - $this->__construct($data); - } - - public function __unserialize(array $data): void - { - // @codeCoverageIgnoreStart - if (!isset($data['string'])) { - throw new ValueError(sprintf('%s(): Argument #1 ($data) is invalid', __METHOD__)); - } - // @codeCoverageIgnoreEnd - - $this->unserialize($data['string']); - } - - public function jsonSerialize(): string - { - return $this->toString(); - } - - private function prepareValue(string $value): string - { - if (!filter_var($value, FILTER_VALIDATE_EMAIL)) { - throw new HttpInvalidArgumentException( - LogMessage::EMAIL_FORMAT_REQUIRED, - StatusMessage::INVALID_DATA, - [ - 'email' => $value, - ] - ); - } - - return $value; - } -} diff --git a/src/Core/Type/TypeInterface.php b/src/Core/Type/TypeInterface.php deleted file mode 100644 index 7f7c9ae9..00000000 --- a/src/Core/Type/TypeInterface.php +++ /dev/null @@ -1,13 +0,0 @@ - $collection - */ - protected array $collection = []; - private int $position = 0; - - public function offsetExists(mixed $offset): bool - { - return isset($this->collection[$offset]); - } - - /** - * @throws UndefinedOffsetException - */ - public function offsetGet(mixed $offset): mixed - { - if (!$this->offsetExists($offset)) { - throw new UndefinedOffsetException( - sprintf('Undefined offset: %s in Collection %s on Line %s', $offset, __FILE__, __LINE__) - ); - } - - return $this->collection[$offset]; - } - - public function offsetSet(mixed $offset, mixed $value): void - { - is_null($offset) - ? $this->collection[] = $value - : $this->collection[$offset] = $value; - } - - public function offsetUnset(mixed $offset): void - { - unset($this->collection[$offset]); - } - - public function current(): mixed - { - return $this->collection[$this->position]; - } - - public function next(): void - { - $this->position++; - } - - public function key(): int - { - return $this->position; - } - - public function valid(): bool - { - return $this->offsetExists($this->position); - } - - public function rewind(): void - { - $this->position = 0; - } - - public function count(): int - { - return count($this->collection); - } - - public function first(): mixed - { - $collection = $this->collection; - return array_shift($collection); - } - - public function last(): mixed - { - $collection = $this->collection; - return array_pop($collection); - } - - /** - * @return array - */ - public function filter(Closure $function): array - { - return array_filter($this->collection, $function); - } - - public function getElements(): array - { - return $this->collection; - } - - public function jsonSerialize(): array - { - return $this->getElements(); - } -} diff --git a/src/Core/Utils/CollectionInterface.php b/src/Core/Utils/CollectionInterface.php deleted file mode 100644 index 9e838aa7..00000000 --- a/src/Core/Utils/CollectionInterface.php +++ /dev/null @@ -1,12 +0,0 @@ -setRequired(true); diff --git a/src/App/Validator/Input/PasswordInput.php b/src/Core/Validator/Input/PasswordInput.php similarity index 88% rename from src/App/Validator/Input/PasswordInput.php rename to src/Core/Validator/Input/PasswordInput.php index 02a447d2..0327d695 100644 --- a/src/App/Validator/Input/PasswordInput.php +++ b/src/Core/Validator/Input/PasswordInput.php @@ -1,6 +1,6 @@ 'UTF-8', 'min' => 6, - 'max' => 255, ] ); } diff --git a/src/Core/Validator/Input/UsernameInput.php b/src/Core/Validator/Input/UsernameInput.php new file mode 100644 index 00000000..fe2bff05 --- /dev/null +++ b/src/Core/Validator/Input/UsernameInput.php @@ -0,0 +1,26 @@ +setRequired(true); + + $this->getFilterChain()->attachByName('StringTrim'); + + $this->getValidatorChain()->attachByName( + 'StringLength', + [ + 'encoding' => 'UTF-8', + 'min' => 3, + 'max' => 50, + ] + ); + } +} diff --git a/src/Core/Validator/LoginValidator.php b/src/Core/Validator/LoginValidator.php new file mode 100644 index 00000000..f8584b4a --- /dev/null +++ b/src/Core/Validator/LoginValidator.php @@ -0,0 +1,18 @@ +add($this->usernameInput); + $this->add($this->passwordInput); + } +} diff --git a/src/Core/Validator/PasswordForgottenEmailValidator.php b/src/Core/Validator/PasswordForgottenEmailValidator.php new file mode 100644 index 00000000..d032a063 --- /dev/null +++ b/src/Core/Validator/PasswordForgottenEmailValidator.php @@ -0,0 +1,15 @@ +add($this->emailInput); + } +} diff --git a/src/App/Validator/EMailValidator.php b/src/Core/Validator/RegisterValidator.php similarity index 66% rename from src/App/Validator/EMailValidator.php rename to src/Core/Validator/RegisterValidator.php index a0b1e404..203ec41a 100644 --- a/src/App/Validator/EMailValidator.php +++ b/src/Core/Validator/RegisterValidator.php @@ -1,11 +1,11 @@ TestConstants::EVENT_ID, + 'uuid' => UuidV7::fromString(TestConstants::EVENT_UUID), + 'userId' => TestConstants::USER_ID, + 'title' => TestConstants::EVENT_TITLE, + 'description' => TestConstants::EVENT_DESCRIPTION, + 'eventText' => TestConstants::EVENT_TEXT, + 'createdAt' => new DateTimeImmutable(TestConstants::TIME), + 'startedAt' => new DateTimeImmutable(TestConstants::TIME), + 'duration' => TestConstants::EVENT_DURATION, + 'status' => EventStatus::SOON, + 'ratingCompleted' => false, + ]; + } +} diff --git a/tests/Data/Entity/ParticipantTestEntity.php b/tests/Data/Entity/ParticipantTestEntity.php new file mode 100644 index 00000000..f0bf7e23 --- /dev/null +++ b/tests/Data/Entity/ParticipantTestEntity.php @@ -0,0 +1,21 @@ + TestConstants::PARTICIPANT_ID, + 'userId' => TestConstants::USER_ID, + 'eventId' => TestConstants::EVENT_ID, + 'requestedAt' => new DateTimeImmutable(TestConstants::TIME), + 'subscribed' => true, + 'disqualified' => false, + ]; + } +} diff --git a/tests/Data/Entity/ProjectTestEntity.php b/tests/Data/Entity/ProjectTestEntity.php new file mode 100644 index 00000000..3490b844 --- /dev/null +++ b/tests/Data/Entity/ProjectTestEntity.php @@ -0,0 +1,24 @@ + TestConstants::PROJECT_ID, + 'uuid' => UuidV7::fromString(TestConstants::PROJECT_UUID), + 'participantId' => TestConstants::PARTICIPANT_ID, + 'title' => TestConstants::PROJECT_TITLE, + 'description' => TestConstants::PROJECT_DESCRIPTION, + 'createdAt' => new DateTimeImmutable(TestConstants::TIME), + 'gitRepoUri' => TestConstants::PROJECT_GIT_URL, + 'demoPageUri' => TestConstants::PROJECT_DEMO_URI, + ]; + } +} diff --git a/tests/Data/Entity/RoleTestEntity.php b/tests/Data/Entity/RoleTestEntity.php new file mode 100644 index 00000000..21f57716 --- /dev/null +++ b/tests/Data/Entity/RoleTestEntity.php @@ -0,0 +1,19 @@ + TestConstants::ROLE_ID, + 'uuid' => UuidV7::fromString(TestConstants::ROLE_UUID), + 'name' => TestConstants::ROLE_NAME, + 'description' => TestConstants::ROLE_DESCRIPTION, + ]; + } +} diff --git a/tests/Data/Entity/TopicTestEntity.php b/tests/Data/Entity/TopicTestEntity.php new file mode 100644 index 00000000..9ea733a0 --- /dev/null +++ b/tests/Data/Entity/TopicTestEntity.php @@ -0,0 +1,21 @@ + TestConstants::TOPIC_ID, + 'uuid' => UuidV7::fromString(TestConstants::TOPIC_UUID), + 'eventId' => TestConstants::EVENT_ID, + 'topic' => TestConstants::TOPIC_TITLE, + 'description' => TestConstants::TOPIC_DESCRIPTION, + 'accepted' => true, + ]; + } +} diff --git a/tests/Data/Entity/UserTestEntity.php b/tests/Data/Entity/UserTestEntity.php new file mode 100644 index 00000000..d5e79eda --- /dev/null +++ b/tests/Data/Entity/UserTestEntity.php @@ -0,0 +1,25 @@ + TestConstants::USER_ID, + 'uuid' => UuidV7::fromString(TestConstants::USER_UUID), + 'role' => UserRole::USER, + 'name' => TestConstants::USER_NAME, + 'password' => TestConstants::USER_PASSWORD, + 'email' => TestConstants::USER_EMAIL, + 'registrationAt' => new DateTimeImmutable(TestConstants::TIME), + 'lastActionAt' => new DateTimeImmutable(TestConstants::TIME), + ]; + } +} diff --git a/tests/Data/TestConstants.php b/tests/Data/TestConstants.php new file mode 100644 index 00000000..d2513be7 --- /dev/null +++ b/tests/Data/TestConstants.php @@ -0,0 +1,110 @@ +app = new MezzioTestEnvironment($basePath); + } +} diff --git a/tests/UnitTest/JsonRequestHelper.php b/tests/Functional/JsonRequestHelper.php similarity index 91% rename from tests/UnitTest/JsonRequestHelper.php rename to tests/Functional/JsonRequestHelper.php index 265a9fc0..0661e7c9 100644 --- a/tests/UnitTest/JsonRequestHelper.php +++ b/tests/Functional/JsonRequestHelper.php @@ -1,6 +1,6 @@ app->dispatchRequest($request); + + self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode()); + self::assertJsonValueMatches( + self::getContentAsJson($response), + '$.ack', + self::greaterThanOrEqual(time()) + ); + } +} diff --git a/tests/Functional/UserControl/UserMeTest.php b/tests/Functional/UserControl/UserMeTest.php new file mode 100644 index 00000000..ea361784 --- /dev/null +++ b/tests/Functional/UserControl/UserMeTest.php @@ -0,0 +1,54 @@ +app->dispatchRequest($request); + + self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode()); + self::assertEmpty(self::getContentAsJson($response)); + } + + /** + * ToDo - The Auth Token for verification is still missing here + */ + public function testMeReturnValidUserByAuthenticatedUser(): void + { + $token = $this->app->container()->get('config')['token']['auth']; + + $user = new User( + 1, + Uuid::uuid7(), + UserRole::USER, + 'TestingUser', + 'myworld', + 'testing@example.com', + new DateTimeImmutable(), + new DateTimeImmutable() + ); + + /** @var UserService $userService */ + $userService = $this->app->container()->get(UserService::class); + $userService->create($user); + + $request = new ServerRequest(uri: '/api/user/me', method: 'GET'); + + $response = $this->app->dispatchRequest($request); + + self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode()); + } +} diff --git a/tests/FunctionalTest/AbstractFunctional.php b/tests/FunctionalTest/AbstractFunctional.php deleted file mode 100644 index bdbd8b9b..00000000 --- a/tests/FunctionalTest/AbstractFunctional.php +++ /dev/null @@ -1,83 +0,0 @@ -initContainer(); - $this->initApp(); - $this->initPipeline(); - $this->initRoutes(); - } - - public static function tearDownAfterClass(): void - { - system('php ' . dirname(__FILE__) . '/bootstrap.php'); - } - - protected function initContainer(): void - { - $this->container = require __DIR__ . '/../../config/container.php'; - } - - protected function initApp(): void - { - $this->app = $this->container->get(Application::class); - } - - protected function initPipeline(): void - { - $factory = $this->container->get(MiddlewareFactory::class); - (require __DIR__ . '/../../config/pipeline.php')($this->app, $factory, $this->container); - } - - protected function initRoutes(): void - { - $factory = $this->container->get(MiddlewareFactory::class); - (require __DIR__ . '/../../config/routes.php')($this->app, $factory, $this->container); - } - - /** - * Override parent method's hard-coded regex - */ - public static function bodyMatchesJson(array $constraints): Constraint - { - return Assert::logicalAnd( - self::hasHeader( - 'content-type', - Assert::matchesRegularExpression( - ',^application/(.+\+)?json(;.+)?$,' - ) - ), - self::bodyMatches( - Assert::logicalAnd( - Assert::isJson(), - new JsonValueMatchesMany($constraints) - ) - ) - ); - } -} diff --git a/tests/FunctionalTest/Mock/NullMailerFactory.php b/tests/FunctionalTest/Mock/NullMailerFactory.php deleted file mode 100644 index d48d61ab..00000000 --- a/tests/FunctionalTest/Mock/NullMailerFactory.php +++ /dev/null @@ -1,14 +0,0 @@ -accountRepository = $this->container->get(AccountRepositoryInterface::class); - $this->accountAccessAuthRepository = $this->container->get(AccountAccessAuthRepository::class); - $this->clientIdentificationService = $this->container->get(ClientIdentificationService::class); - $this->clientIdentificationData = ClientIdentificationData::create( - self::CLIENT_IDENTIFICATION, - self::USER_AGENT - ); - $clientIdentifcationHash = $this->clientIdentificationService->getClientIdentificationHash( - $this->clientIdentificationData - ); - $this->clientIdentification = ClientIdentification::create( - $this->clientIdentificationData, - $clientIdentifcationHash - ); - $this->refreshTokenService = $this->container->get(RefreshTokenService::class); - $this->accessTokenService = $this->container->get(AccessTokenService::class); - $this->refreshToken = $this->refreshTokenService->generate($this->clientIdentification); - /** @var Query $query */ - $query = $this->container->get(Query::class); - $this->PDO = $query->getPdo(); - } - - public function testReturnANewAccessToken(): void - { - $userAccount = $this->accountRepository->findByName('User'); - $accountAccessAuth = new AccountAccessAuth( - null, - $userAccount->id, - 'Testing', - $this->refreshToken, - self::USER_AGENT, - $this->clientIdentificationService->getClientIdentificationHash($this->clientIdentificationData), - new DateTimeImmutable() - ); - $this->accountAccessAuthRepository->insert($accountAccessAuth); - $accountAccessAuthId = (int)$this->PDO->lastInsertId(); - - $request = new ServerRequest( - uri: '/api/token/refresh', - method: 'GET', - headers: [ - 'x-ident' => self::CLIENT_IDENTIFICATION, - 'Authentication' => $this->refreshToken, - 'User-Agent' => self::USER_AGENT, - ] - ); - - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - $this->assertThat($response, $this->bodyMatchesJson([ - 'accessToken' => Assert::isType('string'), - ])); - - $isAccessToken = $this->accessTokenService->isValid($content['accessToken']); - - $this->assertTrue($isAccessToken); - - $this->accountAccessAuthRepository->deleteById($accountAccessAuthId); - } - - public function testGivenRefreshTokenIsInvalid(): void - { - $userAccount = $this->accountRepository->findByName('User'); - $accountAccessAuth = new AccountAccessAuth( - null, - $userAccount->id, - 'Testing', - $this->refreshToken, - self::USER_AGENT, - $this->clientIdentificationService->getClientIdentificationHash($this->clientIdentificationData), - new DateTimeImmutable() - ); - $this->accountAccessAuthRepository->insert($accountAccessAuth); - $accountAccessAuthId = (int)$this->PDO->lastInsertId(); - - $request = new ServerRequest( - uri: '/api/token/refresh', - method: 'GET', - headers: [ - 'x-ident' => self::CLIENT_IDENTIFICATION, - 'Authentication' => self::INVALID_REFRESH_TOKEN, - 'User-Agent' => self::USER_AGENT, - ] - ); - - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_UNAUTHORIZED, $response->getStatusCode()); - $this->assertSame(StatusMessage::TOKEN_INVALID->value, $content['message']); - - $this->accountAccessAuthRepository->deleteById($accountAccessAuthId); - } - - public function testGivenRefreshTokenIsExpired(): void - { - $userAccount = $this->accountRepository->findByName('User'); - $accountAccessAuth = new AccountAccessAuth( - 1, - $userAccount->id, - 'Testing', - $this->refreshToken, - self::USER_AGENT, - $this->clientIdentificationService->getClientIdentificationHash($this->clientIdentificationData), - new DateTimeImmutable() - ); - $this->accountAccessAuthRepository->insert($accountAccessAuth); - $accountAccessAuthId = (int)$this->PDO->lastInsertId(); - - $request = new ServerRequest( - uri: '/api/token/refresh', - method: 'GET', - headers: [ - 'x-ident' => self::CLIENT_IDENTIFICATION, - 'Authentication' => self::EXPIRED_REFRESH_TOKEN, - 'User-Agent' => self::USER_AGENT, - ] - ); - - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_UNAUTHORIZED, $response->getStatusCode()); - $this->assertSame(StatusMessage::TOKEN_INVALID->value, $content['message']); - - $this->accountAccessAuthRepository->deleteById($accountAccessAuthId); - } - - public function testGivenRefreshTokenIsNotPersistenInDatabase(): void - { - $request = new ServerRequest( - uri: '/api/token/refresh', - method: 'GET', - headers: [ - 'x-ident' => self::CLIENT_IDENTIFICATION, - 'Authentication' => $this->refreshToken, - 'User-Agent' => self::USER_AGENT, - ] - ); - - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_UNAUTHORIZED, $response->getStatusCode()); - $this->assertSame(StatusMessage::TOKEN_NOT_PERSISTENT->value, $content['message']); - } - - public function testUnexpectedClientIdentification(): void - { - $userAccount = $this->accountRepository->findByName('User'); - $accountAccessAuth = new AccountAccessAuth( - 1, - $userAccount->id, - 'Testing', - $this->refreshToken, - self::USER_AGENT, - $this->clientIdentificationService->getClientIdentificationHash($this->clientIdentificationData), - new DateTimeImmutable() - ); - $this->accountAccessAuthRepository->insert($accountAccessAuth); - $accountAccessAuthId = (int)$this->PDO->lastInsertId(); - - $request = new ServerRequest( - uri: '/api/token/refresh', - method: 'GET', - headers: [ - 'x-ident' => self::UNEXPECTED_CLIENT_IDENTIFICATION, - 'Authentication' => $this->refreshToken, - 'User-Agent' => self::USER_AGENT, - ] - ); - - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_UNAUTHORIZED, $response->getStatusCode()); - $this->assertSame(StatusMessage::CLIENT_UNEXPECTED->value, $content['message']); - - $this->accountAccessAuthRepository->deleteById($accountAccessAuthId); - } - - public function testUnexpectedUserAgent(): void - { - $userAccount = $this->accountRepository->findByName('User'); - $accountAccessAuth = new AccountAccessAuth( - 1, - $userAccount->id, - 'Testing', - $this->refreshToken, - self::USER_AGENT, - $this->clientIdentificationService->getClientIdentificationHash($this->clientIdentificationData), - new DateTimeImmutable() - ); - $this->accountAccessAuthRepository->insert($accountAccessAuth); - $accountAccessAuthId = (int)$this->PDO->lastInsertId(); - - $request = new ServerRequest( - uri: '/api/token/refresh', - method: 'GET', - headers: [ - 'x-ident' => self::CLIENT_IDENTIFICATION, - 'Authentication' => $this->refreshToken, - 'User-Agent' => self::UNEXPECTED_USER_AGENT, - ] - ); - - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_UNAUTHORIZED, $response->getStatusCode()); - $this->assertSame(StatusMessage::CLIENT_UNEXPECTED->value, $content['message']); - - $this->accountAccessAuthRepository->deleteById($accountAccessAuthId); - } -} diff --git a/tests/FunctionalTest/Root/Account/AccountActivationHandlerTest.php b/tests/FunctionalTest/Root/Account/AccountActivationHandlerTest.php deleted file mode 100644 index 1a0e1e82..00000000 --- a/tests/FunctionalTest/Root/Account/AccountActivationHandlerTest.php +++ /dev/null @@ -1,119 +0,0 @@ -container->get(UuidFactoryInterface::class); - - /** @var AccountActivationRepositoryInterface $activationRepository */ - $activationRepository = $this->container->get(AccountActivationRepositoryInterface::class); - - $testAccountActivate = new AccountActivation( - id: null, - email: new Email('test@example.com'), - token: $uuid->uuid7(), - createdAt: new DateTimeImmutable() - ); - - $activationRepository->insert($testAccountActivate); - - $request = new ServerRequest( - uri: '/api/account/activation/' . $testAccountActivate->token->getHex()->toString(), - method: 'POST' - ); - $request = $request->withParsedBody([ - 'accountName' => 'Test', - 'password' => 'TestBlaBlubb', - ]); - $response = $this->app->handle($request); - - $emptyAccountActivate = $activationRepository->findByToken( - $testAccountActivate->token->getHex()->toString() - ); - - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - $this->assertNull($emptyAccountActivate); - } - - public function testTokenNotGiven(): void - { - $request = new ServerRequest( - uri: '/api/account/activation/', - method: 'POST' - ); - $request = $request->withParsedBody([ - 'accountName' => 'Test', - 'password' => 'TestBlaBlubb', - ]); - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertThat($response, $this->bodyMatchesJson([ - 'statusCode' => Assert::isType(DataType::INTEGER->value), - 'message' => Assert::isType(DataType::STRING->value), - ])); - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertSame(StatusMessage::TOKEN_INVALID->value, $content['message']); - } - - public function testBodyIsInvalid(): void - { - $request = new ServerRequest( - uri: '/api/account/activation/', - method: 'POST' - ); - - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertThat($response, $this->bodyMatchesJson([ - 'statusCode' => Assert::isType(DataType::INTEGER->value), - 'message' => Assert::isType(DataType::STRING->value), - ])); - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertSame(StatusMessage::INVALID_DATA->value, $content['message']); - } - - public function testTokenIsInvalidOrNotPersistent(): void - { - $request = new ServerRequest( - uri: '/api/account/activation/1ddwrer2', - method: 'POST' - ); - $request = $request->withParsedBody([ - 'accountName' => 'Test', - 'password' => 'TestBlaBlubb', - ]); - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertThat($response, $this->bodyMatchesJson([ - 'statusCode' => Assert::isType(DataType::INTEGER->value), - 'message' => Assert::isType(DataType::STRING->value), - ])); - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertSame(StatusMessage::TOKEN_INVALID->value, $content['message']); - } -} diff --git a/tests/FunctionalTest/Root/Account/AccountPasswordForgottenHandlerTest.php b/tests/FunctionalTest/Root/Account/AccountPasswordForgottenHandlerTest.php deleted file mode 100644 index edf80a39..00000000 --- a/tests/FunctionalTest/Root/Account/AccountPasswordForgottenHandlerTest.php +++ /dev/null @@ -1,61 +0,0 @@ -withParsedBody(['email' => self::EMAIL_VALID]); - $response = $this->app->handle($request); - - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = $this->container->get(AccountRepositoryInterface::class); - $account = $accountRepository->findByEmail(new Email(self::EMAIL_VALID)); - - /** @var TokenRepositoryInterface $tokenRepository */ - $tokenRepository = $this->container->get(TokenRepositoryInterface::class); - - $token = $tokenRepository->findByAccountId($account->id); - - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - $this->assertArrayHasKey(0, $token); - $this->assertInstanceOf(UuidInterface::class, $token[0]->token); - } - - public function testDontCreateTokenForPasswordChange(): void - { - $request = new ServerRequest( - uri: '/api/account/password/forgotten', - method: 'POST' - ); - $request = $request->withParsedBody(['email' => self::EMAIL_INVALID]); - $response = $this->app->handle($request); - - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = $this->container->get(AccountRepositoryInterface::class); - $account = $accountRepository->findByEmail(new Email(self::EMAIL_INVALID)); - - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - $this->assertNull($account); - } -} diff --git a/tests/FunctionalTest/Root/Account/AccountPasswordHandlerTest.php b/tests/FunctionalTest/Root/Account/AccountPasswordHandlerTest.php deleted file mode 100644 index 4a14ea84..00000000 --- a/tests/FunctionalTest/Root/Account/AccountPasswordHandlerTest.php +++ /dev/null @@ -1,134 +0,0 @@ -accountRepository = $this->container->get(AccountRepositoryInterface::class); - $this->tokenRepository = $this->container->get(TokenRepositoryInterface::class); - $this->uuid = $this->container->get(UuidFactoryInterface::class); - - $this->account = $this->accountRepository->findByEmail(new Email('user@example.com')); - $this->token = new Token( - null, - $this->account->id, - TokenType::EMail, - $this->uuid->uuid7(), - new DateTimeImmutable() - ); - } - - public function testChangePasswortHasStatusOk(): void - { - $this->tokenRepository->insert($this->token); - - $request = new ServerRequest( - uri: '/api/account/password/' . $this->token->token->getHex()->toString(), - method: 'PATCH' - ); - $request = $request->withParsedBody(['password' => self::PASSWORD_NEW]); - $response = $this->app->handle($request); - - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - } - - public function testChangedPasswordIsValid(): void - { - $this->tokenRepository->insert($this->token); - - $request = new ServerRequest( - uri: '/api/account/password/' . $this->token->token->getHex()->toString(), - method: 'PATCH' - ); - $request = $request->withParsedBody(['password' => self::PASSWORD_NEW]); - $this->app->handle($request); - - $changedAccount = $this->accountRepository->findById($this->account->id); - - $this->assertNotSame($this->account->password, $changedAccount->password); - $this->assertTrue(password_verify(self::PASSWORD_NEW, $changedAccount->password)); - } - - public function testChangedPasswordIsInvalid(): void - { - $this->tokenRepository->insert($this->token); - - $request = new ServerRequest( - uri: '/api/account/password/' . $this->token->token->getHex()->toString(), - method: 'PATCH' - ); - $request = $request->withParsedBody(['password' => self::PASSWORD_NEW_INVALID]); - $response = $this->app->handle($request); - - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - } - - public function testTokenIsInvalid(): void - { - $request = new ServerRequest( - uri: '/api/account/password/InvalidToken', - method: 'PATCH' - ); - $request = $request->withParsedBody(['password' => self::PASSWORD_NEW]); - $response = $this->app->handle($request); - - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - } - - public function testTokenIsMissed(): void - { - $request = new ServerRequest( - uri: '/api/account/password/', - method: 'PATCH' - ); - $request = $request->withParsedBody(['password' => self::PASSWORD_NEW]); - $response = $this->app->handle($request); - - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - } - - public function testTokenWasDestroyed(): void - { - $this->tokenRepository->insert($this->token); - - $request = new ServerRequest( - uri: '/api/account/password/' . $this->token->token->getHex()->toString(), - method: 'PATCH' - ); - $request = $request->withParsedBody(['password' => self::PASSWORD_NEW]); - $this->app->handle($request); - - $destroyedToken = $this->tokenRepository->findByToken($this->token->token->getHex()->toString()); - - $this->assertNull($destroyedToken); - } -} diff --git a/tests/FunctionalTest/Root/Account/AccountRegisterHandlerTest.php b/tests/FunctionalTest/Root/Account/AccountRegisterHandlerTest.php deleted file mode 100644 index 949af802..00000000 --- a/tests/FunctionalTest/Root/Account/AccountRegisterHandlerTest.php +++ /dev/null @@ -1,104 +0,0 @@ -app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertThat($response, $this->bodyMatchesJson([ - 'statusCode' => Assert::isType(DataType::INTEGER->value), - 'message' => Assert::isType(DataType::STRING->value), - ])); - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertSame(StatusMessage::INVALID_DATA->value, $content['message']); - } - - public function testBodyHasInvalidParameter(): void - { - $request = new ServerRequest( - uri: '/api/account', - method: 'POST' - ); - - $response = $this->app->handle($request); - $request = $request->withParsedBody(['password' => 'password']); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertThat($response, $this->bodyMatchesJson([ - 'statusCode' => Assert::isType(DataType::INTEGER->value), - 'message' => Assert::isType(DataType::STRING->value), - ])); - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertSame(StatusMessage::INVALID_DATA->value, $content['message']); - } - - public function testActivationDataSetWasCreated(): void - { - $request = new ServerRequest( - uri: '/api/account', - method: 'POST' - ); - $request = $request->withParsedBody(['email' => 'Tester@example.com']); - $response = $this->app->handle($request); - - /** @var AccountActivationRepositoryInterface $repository */ - $repository = $this->container->get(AccountActivationRepositoryInterface::class); - $activationDataSet = $repository->findEmail(new Email('Tester@example.com')); - - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - $this->assertArrayHasKey(0, $activationDataSet); - $this->assertInstanceOf(AccountActivationInterface::class, $activationDataSet[0]); - } - - public function testPasswordDataSetWasCreated(): void - { - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = $this->container->get(AccountRepositoryInterface::class); - $account = $accountRepository->findByEmail(new Email('user@example.com')); - - /** @var TokenRepositoryInterface $tokenRepository */ - $tokenRepository = $this->container->get(TokenRepositoryInterface::class); - $tokenRepository->deleteByAccountId($account->id); - - $request = new ServerRequest( - uri: '/api/account', - method: 'POST' - ); - $request = $request->withParsedBody(['email' => 'user@example.com']); - $response = $this->app->handle($request); - - $activationDataSet = $tokenRepository->findByAccountId($account->id); - - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - $this->assertArrayHasKey(0, $activationDataSet); - $this->assertInstanceOf(TokenInterface::class, $activationDataSet[0]); - } -} diff --git a/tests/FunctionalTest/Root/Account/AuthenticationHandlerTest.php b/tests/FunctionalTest/Root/Account/AuthenticationHandlerTest.php deleted file mode 100644 index 973dee06..00000000 --- a/tests/FunctionalTest/Root/Account/AuthenticationHandlerTest.php +++ /dev/null @@ -1,180 +0,0 @@ -withParsedBody(['email' => $email, 'password' => $password]) - ->withAddedHeader('x-ident', (string)rand()); - $response = $this->app->handle($request); - - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - } - - #[DataProvider('invalidAuthenticateDataProvider')] - public function testAuthenticateFailed(string $email, string $password): void - { - $request = new ServerRequest( - uri: '/api/account/authentication', - method: 'POST' - ); - $request = $request->withParsedBody(['email' => $email, 'password' => $password]) - ->withAddedHeader('x-ident', (string)rand()); - $response = $this->app->handle($request); - - $this->assertSame(HTTP::STATUS_UNAUTHORIZED, $response->getStatusCode()); - } - - public function testNoDoubleSameLogins(): void - { - $request = new ServerRequest( - uri: '/api/account/authentication', - method: 'POST' - ); - $request = $request->withParsedBody(['email' => 'owner@example.com', 'password' => 'owner123456']) - ->withAddedHeader('x-ident', (string)rand()); - - $response = $this->app->handle($request); - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - - $response = $this->app->handle($request); - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - } - - public function testAccountIsAlreadyAuthenticated(): void - { - $request = new ServerRequest( - uri: '/api/account/authentication', - method: 'POST' - ); - $request = $request->withParsedBody(['email' => 'owner@example.com', 'password' => 'owner123456']) - ->withAddedHeader('x-ident', (string)rand()) - ->withAddedHeader('Authentication', 'Authentication'); - - $response = $this->app->handle($request); - $this->assertSame(HTTP::STATUS_UNAUTHORIZED, $response->getStatusCode()); - } - - public function testAccountIsAlreadyAuthorized(): void - { - $request = new ServerRequest( - uri: '/api/account/authentication', - method: 'POST' - ); - $request = $request->withParsedBody(['email' => 'admin@example.com', 'password' => 'admin123456']) - ->withAddedHeader('x-ident', (string)rand()) - ->withAddedHeader('Authorization', 'Authorization'); - - $response = $this->app->handle($request); - $this->assertSame(HTTP::STATUS_UNAUTHORIZED, $response->getStatusCode()); - } - - public function testResponseHasValidAccessAndRefreshToken(): void - { - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = $this->container->get(AccountRepositoryInterface::class); - - /** @var AccessTokenService $accessTokenService */ - $accessTokenService = $this->container->get(AccessTokenService::class); - - /** @var RefreshTokenService $refreshTokenService */ - $refreshTokenService = $this->container->get(RefreshTokenService::class); - - /** @var UuidFactoryInterface $uuid */ - $uuid = $this->container->get(UuidFactoryInterface::class); - - $account = new \App\Entity\Account\Account( - null, - $uuid->uuid7(), - 'I see your Token', - password_hash('I see your Token', PASSWORD_DEFAULT), - new Email('iseeyourtoken@example.com'), - new DateTimeImmutable(), - new DateTimeImmutable() - ); - $accountRepository->insert($account); - - $request = new ServerRequest( - uri: '/api/account/authentication', - method: 'POST' - ); - $request = $request->withParsedBody( - ['email' => $account->email->toString(), 'password' => 'I see your Token'] - ); - - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - $this->assertThat($response, $this->bodyMatchesJson([ - 'accessToken' => Assert::isType('string'), - 'refreshToken' => Assert::isType('string'), - ])); - - $isAccessToken = $accessTokenService->isValid($content['accessToken']); - $isRefreshToken = $refreshTokenService->isValid($content['refreshToken']); - - $this->assertTrue($isAccessToken); - $this->assertTrue($isRefreshToken); - } - - public static function validAccountDataProvider(): array - { - return [ - 'Owner' => ['owner@example.com', 'owner123456'], - 'Administrator' => ['admin@example.com', 'admin123456'], - 'Moderator' => ['moderator@example.com', 'moderator'], - 'User' => ['user@example.com', 'user123456'], - 'Valid fixed Account Constant' => [Account::EMAIL, Account::PASSWORD_STRING,], - ]; - } - - public static function invalidAuthenticateDataProvider(): array - { - return [ - 'Empty Fields' => ['', ''], - 'Empty E-Mail' => ['', '123456'], - 'Empty Password' => ['account@example.com', ''], - 'No E-Mail' => ['no E-Mail', '123456'], - 'Invalid email prefixe' => ['abc..def@mail.com', '123456'], - 'Password too Short' => ['account@example.com', '123'], - 'Password too Long' => [ - 'account@example.com', - '11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111' - . '11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111' - . '111111111111111111111111111111111111111111111111111111', - ], - 'Account with bad Password' => ['owner@example.com', '123456'], - 'SQL Injection Comment one' => ["owner@example.com'--", '123456'], - 'SQL Injection Comment two' => ["owner@example.com';", '123456'], - ]; - } -} diff --git a/tests/FunctionalTest/Root/Account/InvalidEMailAddressProviderTrait.php b/tests/FunctionalTest/Root/Account/InvalidEMailAddressProviderTrait.php deleted file mode 100644 index 36bb2927..00000000 --- a/tests/FunctionalTest/Root/Account/InvalidEMailAddressProviderTrait.php +++ /dev/null @@ -1,54 +0,0 @@ -withParsedBody(['email' => $email]); - $response = $this->app->handle($request); - $content = $this->getContentAsJson($response); - - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertThat($response, $this->bodyMatchesJson([ - 'statusCode' => Assert::isType(DataType::INTEGER->value), - 'message' => Assert::isType(DataType::STRING->value), - ])); - $this->assertSame(HTTP::STATUS_BAD_REQUEST, $response->getStatusCode()); - $this->assertSame(StatusMessage::INVALID_DATA->value, $content['message']); - } - - public static function invalidEMailAddressProvider(): array - { - return [ - 'Missing @ symbol' => ['invalidemail.com'], - 'Missing domain' => ['user@'], - 'Missing local part' => ['@domain.com'], - 'Consecutive dots' => ['user..name@domain.com'], - 'Invalid character in local part' => ['user:name@domain.com'], - 'Invalid character in domain' => ['user@domain!.com'], - 'Missing top-level domain' => ['user@domain'], - 'Space in local part' => ['user name@domain.com'], - 'Space in domain' => ['user@domain .com'], - 'Double dot in domain' => ['user@domain..com'], - 'Invalid character' => ['user@-domain.com'], - 'Invalid domain (example.invalid)' => ['test@example.invalid'], - 'Invalid top level domain (example.web)' => ['test@example.web'], - 'Trailing space' => ['test@example '], - 'Leading space' => [' test@example.com'], - ]; - } -} diff --git a/tests/FunctionalTest/Root/PingHandlerTest.php b/tests/FunctionalTest/Root/PingHandlerTest.php deleted file mode 100644 index 7c861301..00000000 --- a/tests/FunctionalTest/Root/PingHandlerTest.php +++ /dev/null @@ -1,29 +0,0 @@ -app->handle($request); - - $this->assertSame($response->getStatusCode(), HTTP::STATUS_OK); - $this->assertThat($response, $this->bodyMatchesJson([ - 'ack' => Assert::greaterThanOrEqual($timestamp), - ])); - } -} diff --git a/tests/FunctionalTest/bootstrap.php b/tests/FunctionalTest/bootstrap.php deleted file mode 100644 index aba6b5ec..00000000 --- a/tests/FunctionalTest/bootstrap.php +++ /dev/null @@ -1,10 +0,0 @@ -addSql($sql); - } - - public function down(Schema $schema): void - { - $sql = <<addSql($sql); - } -} diff --git a/tests/Unit/App/Handler/Topic/TopicCreateHandlerTest.php b/tests/Unit/App/Handler/Topic/TopicCreateHandlerTest.php new file mode 100644 index 00000000..7b7e1305 --- /dev/null +++ b/tests/Unit/App/Handler/Topic/TopicCreateHandlerTest.php @@ -0,0 +1,32 @@ +handle( + $this->request->withAttribute(Topic::class, new Topic(...TopicTestEntity::getDefaultTopicValue())) + ); + + $responseData = $response->getBody()->getContents(); + + $responseDataAsArray = json_decode($responseData, true); + + self::assertInstanceOf(JsonResponse::class, $response); + self::assertIsString($responseData); + self::assertJson($responseData); + self::assertIsArray($responseDataAsArray); + self::assertArrayHasKey('topic', $responseDataAsArray); + } +} diff --git a/tests/Unit/App/Handler/Topic/TopicListAvailableHandlerTest.php b/tests/Unit/App/Handler/Topic/TopicListAvailableHandlerTest.php new file mode 100644 index 00000000..f6c950de --- /dev/null +++ b/tests/Unit/App/Handler/Topic/TopicListAvailableHandlerTest.php @@ -0,0 +1,34 @@ +handle($this->request->withAttribute('availableTopics', $topicList)); + + self::assertInstanceOf(JsonResponse::class, $response); + } + + public function testReturnJsonResponseWithoutItems(): void + { + $handler = new TopicListAvailableHandler(); + + $topicList = []; + + $response = $handler->handle($this->request->withAttribute('availableTopics', $topicList)); + + self::assertInstanceOf(JsonResponse::class, $response); + } +} diff --git a/tests/Unit/App/Middleware/Event/EventCreateMiddlewareFactoryTest.php b/tests/Unit/App/Middleware/Event/EventCreateMiddlewareFactoryTest.php new file mode 100644 index 00000000..14cb11dd --- /dev/null +++ b/tests/Unit/App/Middleware/Event/EventCreateMiddlewareFactoryTest.php @@ -0,0 +1,35 @@ + new MockEventService(), + ReflectionHydrator::class => new ReflectionHydrator(), + DateTimeFormatterStrategy::class => new DateTimeFormatterStrategy(), + ] + ); + + $middleware = (new EventCreateMiddlewareFactory())($container); + + self::assertInstanceOf(EventCreateMiddleware::class, $middleware); + } +} diff --git a/tests/Unit/App/Middleware/Event/EventCreateMiddlewareTest.php b/tests/Unit/App/Middleware/Event/EventCreateMiddlewareTest.php new file mode 100644 index 00000000..0c8a7652 --- /dev/null +++ b/tests/Unit/App/Middleware/Event/EventCreateMiddlewareTest.php @@ -0,0 +1,53 @@ +hydrator); + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with(id: TestConstants::USER_ID); + + $response = $middleware->process( + $this->request->withAttribute(User::AUTHENTICATED_USER, $user) + ->withParsedBody(['id' => 2]), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + self::assertNotInstanceOf(JsonResponse::class, $response); + } + + public function testEventIsPresentAndCanNotCreated(): void + { + $middleware = new EventCreateMiddleware(new MockEventService(), $this->hydrator); + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with(id: TestConstants::USER_ID); + + $response = $middleware->process( + $this->request->withAttribute(User::AUTHENTICATED_USER, $user) + ->withParsedBody(['id' => TestConstants::USER_ID]), + $this->handler + ); + + self::assertInstanceOf(JsonResponse::class, $response); + self::assertSame(HTTP::STATUS_NOT_FOUND, $response->getStatusCode()); + } +} diff --git a/tests/Unit/App/Middleware/Event/EventCreateValidationMiddlewareTest.php b/tests/Unit/App/Middleware/Event/EventCreateValidationMiddlewareTest.php new file mode 100644 index 00000000..11698a75 --- /dev/null +++ b/tests/Unit/App/Middleware/Event/EventCreateValidationMiddlewareTest.php @@ -0,0 +1,42 @@ +process( + $this->request->withParsedBody([true]), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + self::assertNotInstanceOf(JsonResponse::class, $response); + } + + public function testValidationIsNotValide(): void + { + $middleware = new EventCreateValidationMiddleware(new MockEventCreateValidator()); + + $response = $middleware->process( + $this->request->withParsedBody([false]), + $this->handler + ); + + self::assertInstanceOf(JsonResponse::class, $response); + } +} diff --git a/tests/Unit/App/Middleware/Event/EventListMiddlewareTest.php b/tests/Unit/App/Middleware/Event/EventListMiddlewareTest.php new file mode 100644 index 00000000..1350136e --- /dev/null +++ b/tests/Unit/App/Middleware/Event/EventListMiddlewareTest.php @@ -0,0 +1,60 @@ +process($this->request, $this->handler); + + self::assertInstanceOf(ResponseInterface::class, $response); + } + + public function testCanFindAllEventSortedASC(): void + { + $middleware = new EventListMiddleware(new MockEventService(), new MockUserService()); + + $response = $middleware->process( + $this->request->withQueryParams( + [ + 'order' => 'startedAt', + 'sort' => 'ASC', + ] + ), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + } + + public function testCanFindAllEventSortedDESC(): void + { + $middleware = new EventListMiddleware(new MockEventService(), new MockUserService()); + + $response = $middleware->process( + $this->request->withQueryParams( + [ + 'order' => 'startedAt', + 'sort' => 'DESC', + ] + ), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + } +} diff --git a/tests/Unit/App/Middleware/Event/EventMiddlewareTest.php b/tests/Unit/App/Middleware/Event/EventMiddlewareTest.php new file mode 100644 index 00000000..16cc4e92 --- /dev/null +++ b/tests/Unit/App/Middleware/Event/EventMiddlewareTest.php @@ -0,0 +1,41 @@ +process($this->request->withAttribute('eventId', 1), $this->handler); + + self::assertInstanceOf(ResponseInterface::class, $response); + } + + public function testThrowInvalidArgumentException(): void + { + $middleware = new EventMiddleware(new MockEventService()); + + self::expectException(InvalidArgumentException::class); + self::expectExceptionCode(HTTP::STATUS_BAD_REQUEST); + + $middleware->process( + $this->request->withAttribute('eventId', TestConstants::EVENT_ID_THROW_EXCEPTION), + $this->handler + ); + } +} diff --git a/tests/Unit/App/Middleware/Event/EventNameMiddlewareTest.php b/tests/Unit/App/Middleware/Event/EventNameMiddlewareTest.php new file mode 100644 index 00000000..48046c92 --- /dev/null +++ b/tests/Unit/App/Middleware/Event/EventNameMiddlewareTest.php @@ -0,0 +1,44 @@ +process( + $this->request->withAttribute('eventName', TestConstants::EVENT_TITLE), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + } + + public function testThrowInvalidArgumentException(): void + { + $middleware = new EventNameMiddleware(new MockEventService()); + + self::expectException(InvalidArgumentException::class); + self::expectExceptionCode(HTTP::STATUS_BAD_REQUEST); + + $middleware->process( + $this->request->withAttribute('eventName', TestConstants::EVENT_TITLE_THROW_EXCEPTION), + $this->handler + ); + } +} diff --git a/tests/Unit/App/Middleware/Topic/TopicCreateSubmitMiddlewareTest.php b/tests/Unit/App/Middleware/Topic/TopicCreateSubmitMiddlewareTest.php new file mode 100644 index 00000000..d62fe159 --- /dev/null +++ b/tests/Unit/App/Middleware/Topic/TopicCreateSubmitMiddlewareTest.php @@ -0,0 +1,45 @@ +middleware = new TopicCreateSubmitMiddleware( + new MockTopicPoolService(), + $this->hydrator, + Uuid::uuid7() + ); + } + + public function testHasCreateNewTopicAndReturnResponse(): void + { + $response = $this->middleware->process( + $this->request->withParsedBody(['topic' => TestConstants::TOPIC_TITLE_CREATE] + TopicTestEntity::getDefaultTopicValue()), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + } + + public function testTopicIsDuplicatedAndThrowException(): void + { + $data = ['topic' => TestConstants::TOPIC_TITLE] + TopicTestEntity::getDefaultTopicValue(); + self::expectException(DuplicateNameHttpException::class); + + $this->middleware->process($this->request->withParsedBody($data), $this->handler); + } +} diff --git a/tests/Unit/App/Middleware/Topic/TopicCreateValidationMiddlewareTest.php b/tests/Unit/App/Middleware/Topic/TopicCreateValidationMiddlewareTest.php new file mode 100644 index 00000000..e45e2ffc --- /dev/null +++ b/tests/Unit/App/Middleware/Topic/TopicCreateValidationMiddlewareTest.php @@ -0,0 +1,43 @@ + 'topic', + 'description' => 'This is the one and only Description', + ]; + + private TopicCreateValidationMiddleware $middleware; + + public function setUp(): void + { + $this->middleware = new TopicCreateValidationMiddleware(new MockTopicCreateValidator()); + parent::setUp(); + } + + public function testValidateTopicData(): void + { + $response = $this->middleware->process( + $this->request->withParsedBody($this->topicData), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + } + + public function testValidateTopicDataThrowException(): void + { + self::expectException(InvalidArgumentHttpException::class); + + $this->middleware->process($this->request->withParsedBody([]), $this->handler); + } +} diff --git a/tests/Unit/App/Middleware/Topic/TopicListMiddlewareTest.php b/tests/Unit/App/Middleware/Topic/TopicListMiddlewareTest.php new file mode 100644 index 00000000..ec67e4f8 --- /dev/null +++ b/tests/Unit/App/Middleware/Topic/TopicListMiddlewareTest.php @@ -0,0 +1,20 @@ +process($this->request, $this->handler); + + self::assertInstanceOf(ResponseInterface::class, $response); + } +} diff --git a/tests/Unit/App/Service/EventServiceFactoryTest.php b/tests/Unit/App/Service/EventServiceFactoryTest.php new file mode 100644 index 00000000..a3e91a2f --- /dev/null +++ b/tests/Unit/App/Service/EventServiceFactoryTest.php @@ -0,0 +1,30 @@ + new MockEventTable(), + ReflectionHydrator::class => $this->hydrator, + DateTimeFormatterStrategy::class => $this->dateTimeFormatterStrategy, + ]); + + $factory = new EventServiceFactory(); + + $service = $factory($container); + + self::assertInstanceOf(EventService::class, $service); + } +} diff --git a/tests/Unit/App/Service/EventServiceTest.php b/tests/Unit/App/Service/EventServiceTest.php new file mode 100644 index 00000000..e2978828 --- /dev/null +++ b/tests/Unit/App/Service/EventServiceTest.php @@ -0,0 +1,98 @@ +service = new EventService(new MockEventTable(), $this->hydrator); + } + + public function testCanNotCreate(): void + { + $event = new Event(...EventTestEntity::getDefaultEventValue()); + $event = $event->with(title: TestConstants::EVENT_TITLE); + + $event = $this->service->create($event); + + self::assertSame(false, $event); + } + + public function testCanCreate(): void + { + $event = new Event(...EventTestEntity::getDefaultEventValue()); + $event = $event->with(title: TestConstants::EVENT_CREATE_TITLE); + + $event = $this->service->create($event); + + self::assertSame(true, $event); + } + + public function testFindByIdThrowException(): void + { + self::expectException(InvalidArgumentException::class); + + $this->service->findById(TestConstants::EVENT_ID_THROW_EXCEPTION); + } + + public function testFindById(): void + { + $event = $this->service->findById(TestConstants::EVENT_ID); + + self::assertInstanceOf(Event::class, $event); + } + + public function testCanFindAll(): void + { + $event = $this->service->findAll(); + + self::assertIsArray($event); + self::assertArrayHasKey(0, $event); + self::assertInstanceOf(Event::class, $event[0]); + } + + public function testCanFindAllActive(): void + { + $event = $this->service->findAllActive(); + + self::assertIsArray($event); + self::assertArrayHasKey(0, $event); + self::assertInstanceOf(Event::class, $event[0]); + } + + public function testCanFindAllNotActive(): void + { + $event = $this->service->findAllNotActive(); + + self::assertIsArray($event); + self::assertArrayHasKey(0, $event); + self::assertInstanceOf(Event::class, $event[0]); + } + + public function testCheckIsRatingCompleted(): void + { + $event = $this->service->isRatingCompleted(TestConstants::EVENT_ID); + + self::assertSame(true, $event); + } + + public function testCheckIsRatingNotCompleted(): void + { + $event = $this->service->isRatingCompleted(TestConstants::EVENT_ID_RATING_NOT_COMPLETED); + + self::assertSame(false, $event); + } +} diff --git a/tests/Unit/App/Service/ParticipantServiceFactoryTest.php b/tests/Unit/App/Service/ParticipantServiceFactoryTest.php new file mode 100644 index 00000000..6a0cb91d --- /dev/null +++ b/tests/Unit/App/Service/ParticipantServiceFactoryTest.php @@ -0,0 +1,30 @@ + new MockParticipantTable(), + ReflectionHydrator::class => $this->hydrator, + DateTimeFormatterStrategy::class => $this->dateTimeFormatterStrategy, + ]); + + $factory = new ParticipantServiceFactory(); + + $service = $factory($container); + + self::assertInstanceOf(ParticipantService::class, $service); + } +} diff --git a/tests/Unit/App/Service/ParticipantServiceTest.php b/tests/Unit/App/Service/ParticipantServiceTest.php new file mode 100644 index 00000000..d949b914 --- /dev/null +++ b/tests/Unit/App/Service/ParticipantServiceTest.php @@ -0,0 +1,109 @@ +service = new ParticipantService($table, $this->hydrator); + } + + public function testCanNotCreateParticipant(): void + { + $participant = new Participant(...ParticipantTestEntity::getDefaultParticipantValue()); + $participant = $participant->with( + id: TestConstants::PARTICIPANT_ID, + userId: TestConstants::USER_ID, + eventId: TestConstants::EVENT_ID, + ); + + $participant = $this->service->create($participant); + + self::assertSame(false, $participant); + } + + public function testCanCreateParticipant(): void + { + $participant = new Participant(...ParticipantTestEntity::getDefaultParticipantValue()); + $participant = $participant->with( + id: TestConstants::PARTICIPANT_ID_UNUSED, + userId: TestConstants::USER_ID_UNUSED, + eventId: TestConstants::EVENT_ID_UNUSED, + ); + + $participant = $this->service->create($participant); + + self::assertSame(true, $participant); + } + + public function testCanRemoveParticipant(): void + { + $participant = new Participant(...ParticipantTestEntity::getDefaultParticipantValue()); + $participant = $participant->with(id: TestConstants::PARTICIPANT_ID); + + $response = $this->service->remove($participant); + + self::assertSame(true, $response); + } + + public function testCanNotRemoveParticipant(): void + { + $participant = new Participant(...ParticipantTestEntity::getDefaultParticipantValue()); + $participant = $participant->with(id: TestConstants::PARTICIPANT_ID_UNUSED); + + $response = $this->service->remove($participant); + + self::assertSame(false, $response); + } + + public function testFindByIdThrowException(): void + { + self::expectException(InvalidArgumentException::class); + + $this->service->findById(TestConstants::PARTICIPANT_ID_THROW_EXCEPTION); + } + + public function testCanFindById(): void + { + $participant = $this->service->findById(TestConstants::PARTICIPANT_ID); + + self::assertInstanceOf(Participant::class, $participant); + } + + public function testCanFindByUserId(): void + { + $participant = $this->service->findByUserId(TestConstants::USER_ID); + + self::assertInstanceOf(Participant::class, $participant); + } + + public function testCanNotFindByUserId(): void + { + $participant = $this->service->findByUserId(TestConstants::USER_ID_UNUSED); + + self::assertNull($participant); + } + + public function testCanFindActiveParticipantByEvent(): void + { + $participant = $this->service->findActiveParticipantByEvent(TestConstants::EVENT_ID); + + self::assertIsArray($participant); + self::assertArrayHasKey(0, $participant); + self::assertInstanceOf(Participant::class, $participant[0]); + } +} diff --git a/tests/Unit/App/Service/ProjectServiceFactoryTest.php b/tests/Unit/App/Service/ProjectServiceFactoryTest.php new file mode 100644 index 00000000..177101b4 --- /dev/null +++ b/tests/Unit/App/Service/ProjectServiceFactoryTest.php @@ -0,0 +1,30 @@ + new MockProjectTable(), + ReflectionHydrator::class => $this->hydrator, + DateTimeFormatterStrategy::class => $this->dateTimeFormatterStrategy, + ]); + + $factory = new ProjectServiceFactory(); + + $service = $factory($container); + + self::assertInstanceOf(ProjectService::class, $service); + } +} diff --git a/tests/Unit/App/Service/ProjectServiceTest.php b/tests/Unit/App/Service/ProjectServiceTest.php new file mode 100644 index 00000000..62608ee9 --- /dev/null +++ b/tests/Unit/App/Service/ProjectServiceTest.php @@ -0,0 +1,51 @@ +service = new ProjectService($table, $this->hydrator); + } + + public function testCanFindById(): void + { + $project = $this->service->findById(1); + + self::assertInstanceOf(Project::class, $project); + } + + public function testCanNotFindById(): void + { + self::expectException(InvalidArgumentException::class); + + $this->service->findById(TestConstants::PROJECT_ID_UNUSED); + } + + public function testCanFindByParticipantId(): void + { + $project = $this->service->findByParticipantId(1); + + self::assertInstanceOf(Project::class, $project); + } + + public function testCanNotFindByParticipantId(): void + { + $project = $this->service->findByParticipantId(2); + + self::assertNull($project); + } +} diff --git a/tests/Unit/App/Service/TopicPoolServiceTest.php b/tests/Unit/App/Service/TopicPoolServiceTest.php new file mode 100644 index 00000000..eacd6470 --- /dev/null +++ b/tests/Unit/App/Service/TopicPoolServiceTest.php @@ -0,0 +1,113 @@ +table = new MockTopicPoolTable(); + $this->service = new TopicPoolService($this->table, $this->hydrator); + } + + public function testCanInsertTopic(): void + { + $insertTopic = $this->service->insert(new Topic(...TopicTestEntity::getDefaultTopicValue())); + + self::assertInstanceOf(TopicPoolService::class, $insertTopic); + } + + public function testCanUpdateEventId(): void + { + $updateTopic = $this->service->updateEventId(new Topic(...TopicTestEntity::getDefaultTopicValue())); + + self::assertInstanceOf(TopicPoolService::class, $updateTopic); + } + + public function testFindByIdThrowException(): void + { + self::expectException(InvalidArgumentException::class); + + $this->service->findById(TestConstants::TOPIC_ID_THROW_EXCEPTION); + } + + public function testCanFindById(): void + { + $topic = $this->service->findById(TestConstants::TOPIC_ID); + + self::assertInstanceOf(Topic::class, $topic); + } + + public function testCanNotFindByEventId(): void + { + $topic = $this->service->findByEventId(TestConstants::EVENT_ID_UNUSED); + + $this->assertNull($topic); + } + + public function testCanFindByEventId(): void + { + $topic = $this->service->findByEventId(TestConstants::EVENT_ID); + + self::assertInstanceOf(Topic::class, $topic); + } + + public function testCanFindAvailable(): void + { + $topic = $this->service->findAvailable(); + + self::assertIsArray($topic); + self::assertArrayHasKey(0, $topic); + self::assertInstanceOf(Topic::class, $topic[0]); + } + + public function testCanFindAll(): void + { + $topic = $this->service->findAll(); + + self::assertIsArray($topic); + self::assertArrayHasKey(0, $topic); + self::assertInstanceOf(Topic::class, $topic[0]); + } + + public function testIsNotTopic(): void + { + $topic = $this->service->isTopic('fakeIsNotTopic'); + + self::assertSame(false, $topic); + } + + public function testIsTopic(): void + { + $topic = $this->service->isTopic(TestConstants::TOPIC_TITLE); + + self::assertSame(true, $topic); + } + + public function testCanGetEntriesStatistic(): void + { + $values = [ + 'allTopic' => $this->table->getCountTopic(), + 'allAcceptedTopic' => $this->table->getCountTopicAccepted(), + 'allSelectionAvailableTopic' => $this->table->getCountTopicSelectionAvailable(), + ]; + + $statistic = $this->service->getEntriesStatistic(); + + self::assertSame($values, $statistic); + } +} diff --git a/tests/Unit/App/Table/EventTableTest.php b/tests/Unit/App/Table/EventTableTest.php new file mode 100644 index 00000000..e121aee6 --- /dev/null +++ b/tests/Unit/App/Table/EventTableTest.php @@ -0,0 +1,137 @@ +table->getTableName()); + } + + public function testCanInsertEvent(): void + { + $event = new Event(...EventTestEntity::getDefaultEventValue()); + $event = $event->with(title: TestConstants::EVENT_CREATE_TITLE); + + $insertLastId = $this->table->insert($event); + + self::assertSame(1, $insertLastId); + } + + public function testInsertEventThrowsException(): void + { + $event = new Event(...EventTestEntity::getDefaultEventValue()); + + self::expectException(DuplicateEntryException::class); + + $this->table->insert($event); + } + + public function testCanFindById(): void + { + $event = $this->table->findById(TestConstants::EVENT_ID); + + self::assertEquals(EventTestEntity::getDefaultEventValue(), $event); + } + + public function testFindByIdHasEmptyResult(): void + { + $event = $this->table->findById(TestConstants::EVENT_ID_UNUSED); + + self::assertSame([], $event); + } + + public function testCanFindAll(): void + { + $event = $this->table->findAll(); + + self::assertEquals([0 => EventTestEntity::getDefaultEventValue()], $event); + } + + public function testFindAllHasEmptyResult(): void + { + $table = new EventTable(new MockQueryForCanNot()); + + $event = $table->findAll(); + + self::assertSame([], $event); + } + + public function testCanFindByName(): void + { + $event = $this->table->findByTitle(TestConstants::EVENT_TITLE); + + self::assertEquals(EventTestEntity::getDefaultEventValue(), $event); + } + + public function testFindByNameHasEmptyResult(): void + { + $event = $this->table->findByTitle(TestConstants::EVENT_TITLE_UNUSED); + + self::assertSame([], $event); + } + + public function testCanFindAllActive(): void + { + $event = $this->table->findAllActive(); + + self::assertEquals([0 => EventTestEntity::getDefaultEventValue()], $event); + } + + public function testFindAllActiveHasEmptyResult(): void + { + $table = new EventTable(new MockQueryForCanNot()); + + $event = $table->findAllActive(); + + self::assertSame([], $event); + } + + public function testCanFindAllNotActive(): void + { + $event = $this->table->findAllInactive(); + + self::assertEquals([0 => EventTestEntity::getDefaultEventValue()], $event); + } + + public function testFindAllNotActiveHasEmptyResult(): void + { + $table = new EventTable(new MockQueryForCanNot()); + + $event = $table->findAllInactive(); + + self::assertSame([], $event); + } + + public function testCanRemoveEvent(): void + { + $event = new Event(...EventTestEntity::getDefaultEventValue()); + $event = $event->with(id: TestConstants::EVENT_ID); + + $removeStatus = $this->table->remove($event); + + self::assertSame(true, $removeStatus); + } + + public function testCanNotRemoveEvent(): void + { + $event = new Event(...EventTestEntity::getDefaultEventValue()); + $event = $event->with(id: TestConstants::EVENT_ID_NOT_REMOVED); + + $removeStatus = $this->table->remove($event); + + self::assertSame(false, $removeStatus); + } +} diff --git a/tests/Unit/App/Table/ParticipantTableTest.php b/tests/Unit/App/Table/ParticipantTableTest.php new file mode 100644 index 00000000..2f708567 --- /dev/null +++ b/tests/Unit/App/Table/ParticipantTableTest.php @@ -0,0 +1,125 @@ +table->getTableName()); + } + + public function testCanInsertParticipant(): void + { + $participant = new Participant(...ParticipantTestEntity::getDefaultParticipantValue()); + $participant = $participant->with(userId: TestConstants::USER_CREATE_ID); + + $insertParticipant = $this->table->insert($participant); + + self::assertSame(1, $insertParticipant); + } + + public function testInsertParticipantThrowsException(): void + { + $participant = new Participant(...ParticipantTestEntity::getDefaultParticipantValue()); + $participant = $participant->with(userId: TestConstants::USER_ID); + + self::expectException(DuplicateEntryException::class); + + $this->table->insert($participant); + } + + public function testCanRemoveParticipant(): void + { + $participant = new Participant(...ParticipantTestEntity::getDefaultParticipantValue()); + + $removeParticipant = $this->table->remove($participant); + + self::assertSame(true, $removeParticipant); + } + + public function testCanFindById(): void + { + $project = $this->table->findById(TestConstants::PARTICIPANT_ID); + + self::assertEquals(ParticipantTestEntity::getDefaultParticipantValue(), $project); + } + + public function testFindByIdHaveEmptyResult(): void + { + $project = $this->table->findById(TestConstants::PARTICIPANT_ID_UNUSED); + + self::assertSame([], $project); + } + + public function testCanFindAll(): void + { + $project = $this->table->findAll(); + + self::assertEquals([0 => ParticipantTestEntity::getDefaultParticipantValue()], $project); + } + + public function testFindAllHasEmptyResult(): void + { + $table = new ParticipantTable(new MockQueryForCanNot()); + + $project = $table->findAll(); + + self::assertSame([], $project); + } + + public function testCanFindByUserId(): void + { + $participant = $this->table->findByUserId(TestConstants::USER_ID); + + self::assertEquals(ParticipantTestEntity::getDefaultParticipantValue(), $participant); + } + + public function testFindByUserIdHasEmptyResult(): void + { + $participant = $this->table->findByUserId(TestConstants::USER_ID_UNUSED); + + self::assertSame([], $participant); + } + + public function testCanFindByUserIdAndEventId(): void + { + $participant = $this->table->findUserForAnEvent(TestConstants::USER_ID, TestConstants::EVENT_ID); + + self::assertEquals(ParticipantTestEntity::getDefaultParticipantValue(), $participant); + } + + public function testFindByUserIdAndEventIdHasEmptyResult(): void + { + $participant = $this->table->findUserForAnEvent(TestConstants::USER_ID_UNUSED, TestConstants::EVENT_ID_UNUSED); + + self::assertSame([], $participant); + } + + public function testCanFindActiveParticipantByEvent(): void + { + $participant = $this->table->findActiveParticipantsByEvent(TestConstants::EVENT_ID); + + self::assertEquals([0 => ParticipantTestEntity::getDefaultParticipantValue()], $participant); + } + + public function testFindActiveParticipantByEventHasEmptyResult(): void + { + $table = new ParticipantTable(new MockQueryForCanNot()); + + $participant = $table->findActiveParticipantsByEvent(TestConstants::EVENT_ID_UNUSED); + + self::assertSame([], $participant); + } +} diff --git a/tests/Unit/App/Table/ProjectTableTest.php b/tests/Unit/App/Table/ProjectTableTest.php new file mode 100644 index 00000000..25cd3981 --- /dev/null +++ b/tests/Unit/App/Table/ProjectTableTest.php @@ -0,0 +1,47 @@ +table->getTableName()); + } + + public function testCanFindById(): void + { + $project = $this->table->findById(TestConstants::PROJECT_ID); + + self::assertEquals(ProjectTestEntity::getDefaultProjectValue(), $project); + } + + public function testFindByIdHaveEmptyResult(): void + { + $project = $this->table->findById(TestConstants::PROJECT_ID_UNUSED); + + self::assertSame([], $project); + } + + public function testCanFindAll(): void + { + $project = $this->table->findAll(); + + self::assertEquals([0 => ProjectTestEntity::getDefaultProjectValue()], $project); + } + + public function testCanFindByParticipantId(): void + { + $project = $this->table->findByParticipantId(TestConstants::PARTICIPANT_ID); + + self::assertEquals(ProjectTestEntity::getDefaultProjectValue(), $project); + } +} diff --git a/tests/Unit/App/Table/TopicPoolTableTest.php b/tests/Unit/App/Table/TopicPoolTableTest.php new file mode 100644 index 00000000..800af161 --- /dev/null +++ b/tests/Unit/App/Table/TopicPoolTableTest.php @@ -0,0 +1,108 @@ +table->getTableName()); + } + + public function testCanInsertTopic(): void + { + $topic = new Topic(...TopicTestEntity::getDefaultTopicValue()); + + $insertTopic = $this->table->insert($topic); + + self::assertInstanceOf(TopicPoolTable::class, $insertTopic); + } + + public function testCanUpdateEventId(): void + { + $topic = new Topic(...TopicTestEntity::getDefaultTopicValue()); + + $updateTopic = $this->table->assignAnEvent($topic->id, $topic->eventId); + + self::assertInstanceOf(TopicPoolTable::class, $updateTopic); + } + + public function testCanFindById(): void + { + $topic = $this->table->findById(TestConstants::TOPIC_POOL_ID); + + self::assertEquals(TopicTestEntity::getDefaultTopicValue(), $topic); + } + + public function testFindByIdHaveEmptyResult(): void + { + $topic = $this->table->findById(TestConstants::TOPIC_POOL_ID_UNUSED); + + self::assertSame([], $topic); + } + + public function testCanFindByUuId(): void + { + $topic = $this->table->findByUuId(TestConstants::TOPIC_UUID); + + self::assertEquals(TopicTestEntity::getDefaultTopicValue(), $topic); + } + + public function testCanFindAll(): void + { + $users = $this->table->findAll(); + + self::assertEquals([0 => TopicTestEntity::getDefaultTopicValue()], $users); + } + + public function testCanFindByEventId(): void + { + $topic = $this->table->findByEventId(TestConstants::EVENT_ID); + + self::assertEquals(TopicTestEntity::getDefaultTopicValue(), $topic); + } + + public function testCanFindAvailable(): void + { + $topic = $this->table->findAvailable(); + + self::assertEquals([0 => TopicTestEntity::getDefaultTopicValue()], $topic); + } + + public function testCanFindByTopic(): void + { + $topic = $this->table->findByTopic(TestConstants::TOPIC_TITLE); + + self::assertEquals(TopicTestEntity::getDefaultTopicValue(), $topic); + } + + public function testCanGetCountTopic(): void + { + $topicCount = $this->table->getCountTopic(); + + self::assertSame(1, $topicCount); + } + + public function testCanGetCountTopicAccepted(): void + { + $topicCount = $this->table->getCountTopicAccepted(); + + self::assertSame(1, $topicCount); + } + + public function testCanGetCountTopicSelectionAvailable(): void + { + $topicCount = $this->table->getCountTopicSelectionAvailable(); + + self::assertSame(1, $topicCount); + } +} diff --git a/tests/UnitTest/CoreTest/Factory/DatabaseFactoryTest.php b/tests/Unit/Core/Factory/DatabaseFactoryTest.php similarity index 50% rename from tests/UnitTest/CoreTest/Factory/DatabaseFactoryTest.php rename to tests/Unit/Core/Factory/DatabaseFactoryTest.php index b0097fc4..54c9a3f5 100644 --- a/tests/UnitTest/CoreTest/Factory/DatabaseFactoryTest.php +++ b/tests/Unit/Core/Factory/DatabaseFactoryTest.php @@ -1,22 +1,18 @@ expectException(PDOException::class); @@ -29,15 +25,31 @@ public function testThrowPDOException(): void 'port' => 3306, 'dbname' => 'example_db', 'error' => PDO::ERRMODE_EXCEPTION, - 'emulate_prepares' => false, + ], ]; $container = new MockContainer(); $container->add('config', $config); + (new DatabaseFactory())($container); + } + + public function testCanInitiatePdoConnection(): void + { + system('touch ' . dirname(__FILE__) . '/../../../../database/database.sqlite'); + $config = require dirname(__FILE__) . '/../../../config/autoload/database.testing.local.php'; + + $container = new MockContainer(); + $container->add('config', $config); + $pdo = (new DatabaseFactory())($container); - $this->assertInstanceOf(PDO::class, $pdo); + self::assertInstanceOf(PDO::class, $pdo); + } + + public function tearDown(): void + { + system('rm ' . dirname(__FILE__) . '/../../../../database/database.sqlite'); } } diff --git a/tests/Unit/Core/Factory/MailFactoryTest.php b/tests/Unit/Core/Factory/MailFactoryTest.php new file mode 100644 index 00000000..d90bb92e --- /dev/null +++ b/tests/Unit/Core/Factory/MailFactoryTest.php @@ -0,0 +1,28 @@ + [ + 'dsn' => 'smtp://example.com:1025', + 'from' => 'example@example.com', + ], + ]; + + $container = new MockContainer(); + $container->add('config', $config); + + $mailer = (new MailFactory())($container); + + self::assertInstanceOf(Mailer::class, $mailer); + } +} diff --git a/tests/UnitTest/CoreTest/Factory/QueryFactoryTest.php b/tests/Unit/Core/Factory/QueryFactoryTest.php similarity index 52% rename from tests/UnitTest/CoreTest/Factory/QueryFactoryTest.php rename to tests/Unit/Core/Factory/QueryFactoryTest.php index d42a487b..6e6971d0 100644 --- a/tests/UnitTest/CoreTest/Factory/QueryFactoryTest.php +++ b/tests/Unit/Core/Factory/QueryFactoryTest.php @@ -1,22 +1,16 @@ assertInstanceOf(Query::class, $query); + self::assertInstanceOf(Query::class, $query); } } diff --git a/tests/Unit/Core/Factory/UuidFactoryTest.php b/tests/Unit/Core/Factory/UuidFactoryTest.php new file mode 100644 index 00000000..6075280f --- /dev/null +++ b/tests/Unit/Core/Factory/UuidFactoryTest.php @@ -0,0 +1,20 @@ +request = new MockServerRequest(); - + $this->hydrator = new ReflectionHydrator(); parent::setUp(); } } diff --git a/tests/Unit/Core/Handler/LoginHandlerTest.php b/tests/Unit/Core/Handler/LoginHandlerTest.php new file mode 100644 index 00000000..f2b91f8c --- /dev/null +++ b/tests/Unit/Core/Handler/LoginHandlerTest.php @@ -0,0 +1,32 @@ +handle( + $this->request->withAttribute(User::AUTHENTICATED_USER, new User(...UserTestEntity::getDefaultUserValue())) + ); + + $responseData = $response->getBody()->getContents(); + + $responseDataAsArray = json_decode($responseData, true); + + self::assertInstanceOf(JsonResponse::class, $response); + self::assertIsString($responseData); + self::assertJson($responseData); + self::assertIsArray($responseDataAsArray); + self::assertArrayHasKey('token', $responseDataAsArray); + } +} diff --git a/tests/Unit/Core/Hydrator/ClassMethodsHydratorFactoryTest.php b/tests/Unit/Core/Hydrator/ClassMethodsHydratorFactoryTest.php new file mode 100644 index 00000000..e3e2a999 --- /dev/null +++ b/tests/Unit/Core/Hydrator/ClassMethodsHydratorFactoryTest.php @@ -0,0 +1,18 @@ +add(DateTimeFormatterStrategy::class, (new DateTimeFormatterStrategyFactory())($container)); + + $nullableStragegy = (new NullableStrategyFactory())($container); + + self::assertInstanceOf(NullableStrategy::class, $nullableStragegy); + } +} diff --git a/tests/Unit/Core/Hydrator/ReflectionHydratorTest.php b/tests/Unit/Core/Hydrator/ReflectionHydratorTest.php new file mode 100644 index 00000000..a6ea4b66 --- /dev/null +++ b/tests/Unit/Core/Hydrator/ReflectionHydratorTest.php @@ -0,0 +1,49 @@ +hydrator = new ReflectionHydrator(); + } + + public function testCanNotHydrate(): void + { + $hydrate = $this->hydrator->hydrate(false, User::class); + + self::assertNull($hydrate); + } + + public function testCanHydrate(): void + { + $hydrate = $this->hydrator->hydrate(UserTestEntity::getDefaultUserValue(), User::class); + + self::assertInstanceOf(User::class, $hydrate); + } + + public function testCanHydrateListWithoutData(): void + { + $hydrate = $this->hydrator->hydrateList([], User::class); + + self::assertIsArray($hydrate); + self::assertSame(0, count($hydrate)); + } + + public function testCanHydrateListWithData(): void + { + $hydrate = $this->hydrator->hydrateList([0 => UserTestEntity::getDefaultUserValue()], User::class); + + self::assertIsArray($hydrate); + self::assertArrayHasKey(0, $hydrate); + self::assertInstanceOf(User::class, $hydrate[0]); + } +} diff --git a/tests/UnitTest/AppTest/Middleware/AbstractTestMiddleware.php b/tests/Unit/Core/Middleware/AbstractMiddleware.php similarity index 59% rename from tests/UnitTest/AppTest/Middleware/AbstractTestMiddleware.php rename to tests/Unit/Core/Middleware/AbstractMiddleware.php index cdd34f8b..91735e95 100644 --- a/tests/UnitTest/AppTest/Middleware/AbstractTestMiddleware.php +++ b/tests/Unit/Core/Middleware/AbstractMiddleware.php @@ -1,27 +1,25 @@ request = new MockServerRequest(); $this->handler = new MockRequestHandler(); + $this->hydrator = new ReflectionHydrator(); parent::setUp(); } diff --git a/tests/Unit/Core/Middleware/ApiAccessMiddlewareTest.php b/tests/Unit/Core/Middleware/ApiAccessMiddlewareTest.php new file mode 100644 index 00000000..2a01e12f --- /dev/null +++ b/tests/Unit/Core/Middleware/ApiAccessMiddlewareTest.php @@ -0,0 +1,57 @@ +apiAccessService = new MockApiAccessService(); + } + + public function testReturnResponseInterfaceWithoutPort(): void + { + $middleware = new ApiAccessMiddleware($this->apiAccessService); + + $response = $middleware->process( + $this->request->withHeader('Host', 'localhost'), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + } + + public function testReturnResponseInterfaceWithPort(): void + { + $middleware = new ApiAccessMiddleware($this->apiAccessService); + + $response = $middleware->process( + $this->request->withHeader('Host', 'localhost:80'), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + } + + public function testReturnJSonResponse(): void + { + $middleware = new ApiAccessMiddleware($this->apiAccessService); + + $response = $middleware->process( + $this->request->withHeader('Host', 'example.com'), + $this->handler + ); + + self::assertInstanceOf(JsonResponse::class, $response); + self::assertSame(HTTP::STATUS_UNAUTHORIZED, $response->getStatusCode()); + } +} diff --git a/tests/Unit/Core/Middleware/UpdateLastUserActionTimeMiddlewareTest.php b/tests/Unit/Core/Middleware/UpdateLastUserActionTimeMiddlewareTest.php new file mode 100644 index 00000000..62b5d97f --- /dev/null +++ b/tests/Unit/Core/Middleware/UpdateLastUserActionTimeMiddlewareTest.php @@ -0,0 +1,36 @@ +userService = new MockUserService(); + } + + public function testReturnResponseInterface(): void + { + $middleware = new UpdateLastUserActionTimeMiddleware($this->userService); + + $user = new User(...UserTestEntity::getDefaultUserValue()); + + $response = $middleware->process( + $this->request->withAttribute(User::AUTHENTICATED_USER, $user), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + } +} diff --git a/tests/Unit/Core/Middleware/UserMiddlewareTest.php b/tests/Unit/Core/Middleware/UserMiddlewareTest.php new file mode 100644 index 00000000..04379e44 --- /dev/null +++ b/tests/Unit/Core/Middleware/UserMiddlewareTest.php @@ -0,0 +1,48 @@ +userService = new MockUserService(); + } + + public function testReturnResponseInterface(): void + { + $middleware = new UserMiddleware($this->userService); + + $response = $middleware->process( + $this->request->withAttribute('userUuid', TestConstants::USER_UUID), + $this->handler + ); + + self::assertInstanceOf(ResponseInterface::class, $response); + } + + public function testReturnStatusNotFound(): void + { + $middleware = new UserMiddleware($this->userService); + + $response = $middleware->process( + $this->request->withAttribute('userUuid', '-'), + $this->handler + ); + + self::assertInstanceOf(JsonResponse::class, $response); + self::assertSame($response->getStatusCode(), HTTP::STATUS_NOT_FOUND); + } +} diff --git a/tests/Unit/Core/Service/AbstractService.php b/tests/Unit/Core/Service/AbstractService.php new file mode 100644 index 00000000..0f725b8d --- /dev/null +++ b/tests/Unit/Core/Service/AbstractService.php @@ -0,0 +1,33 @@ +hydrator = new ReflectionHydrator(); + $this->dateTimeFormatterStrategy = new DateTimeFormatterStrategy(); + $this->dateTimeImmutableFormatterStrategy = new DateTimeImmutableFormatterStrategy(new DateTimeFormatterStrategy('Y-m-d H:i:s')); + $this->nullableStrategy = new NullableStrategy($this->dateTimeFormatterStrategy); + $this->uuidStrategy = new UuidStrategy(); + $this->uuid = Uuid::uuid4(); + parent::setUp(); + } +} diff --git a/tests/Unit/Core/Service/ApiAccessServiceFactoryTest.php b/tests/Unit/Core/Service/ApiAccessServiceFactoryTest.php new file mode 100644 index 00000000..578c66be --- /dev/null +++ b/tests/Unit/Core/Service/ApiAccessServiceFactoryTest.php @@ -0,0 +1,37 @@ + [ + 'access' => [ + 'domain' => [ + 'whitelist' => [ + 'localhost', + ], + ], + ], + ], + ]; + + $container = new MockContainer(['config' => $config]); + + $apiAccessService = (new ApiAccessServiceFactory())($container); + + self::assertInstanceOf(ApiAccessService::class, $apiAccessService); + } +} diff --git a/tests/Unit/Core/Service/ApiAccessServiceTest.php b/tests/Unit/Core/Service/ApiAccessServiceTest.php new file mode 100644 index 00000000..a92ed312 --- /dev/null +++ b/tests/Unit/Core/Service/ApiAccessServiceTest.php @@ -0,0 +1,38 @@ +config = [ + 'domain' => [ + 'whitelist' => [ + 'localhost', + ], + ], + ]; + + $this->apiAccessService = new ApiAccessService($this->config); + } + + public function testHasAccessRights(): void + { + $hasRights = $this->apiAccessService->hasAccessRights('localhost'); + self::assertSame(true, $hasRights); + } + + public function testHasAccessNotRights(): void + { + $hasRights = $this->apiAccessService->hasAccessRights('example.com'); + self::assertSame(false, $hasRights); + } +} diff --git a/tests/Unit/Core/Service/TokenServiceTest.php b/tests/Unit/Core/Service/TokenServiceTest.php new file mode 100644 index 00000000..b2603e1a --- /dev/null +++ b/tests/Unit/Core/Service/TokenServiceTest.php @@ -0,0 +1,18 @@ +generateToken(); + + self::assertIsString($token); + self::assertSame(32, strlen($token)); + } +} diff --git a/tests/Unit/Core/Service/UserServiceFactoryTest.php b/tests/Unit/Core/Service/UserServiceFactoryTest.php new file mode 100644 index 00000000..70381c4f --- /dev/null +++ b/tests/Unit/Core/Service/UserServiceFactoryTest.php @@ -0,0 +1,33 @@ + new MockUserTable(), + ReflectionHydrator::class => $this->hydrator, + NullableStrategy::class => $this->nullableStrategy, + DateTimeImmutableFormatterStrategy::class => $this->dateTimeImmutableFormatterStrategy, + Uuid::class => Uuid::uuid4(), + ]); + + $factory = new UserServiceFactory(); + + $service = $factory($container); + + self::assertInstanceOf(UserService::class, $service); + } +} diff --git a/tests/Unit/Core/Service/UserServiceTest.php b/tests/Unit/Core/Service/UserServiceTest.php new file mode 100644 index 00000000..f564953c --- /dev/null +++ b/tests/Unit/Core/Service/UserServiceTest.php @@ -0,0 +1,134 @@ +hydrator->addStrategy(UuidStrategy::class, $this->uuidStrategy); + $this->userService = new UserService($table, $this->hydrator, $this->uuid); + } + + public function testCanNotCreateUserWithExistUser(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with(name: TestConstants::USER_NAME); + + self::expectException(DuplicateEntryException::class); + + $this->userService->create($user); + } + + public function testCanNotCreateUserWithExistEmail(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with(email: TestConstants::USER_EMAIL); + + self::expectException(DuplicateEntryException::class); + + $this->userService->create($user); + } + + public function testCanCreateUser(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with( + name: TestConstants::USER_CREATE_NAME, + email: TestConstants::USER_CREATE_EMAIL, + ); + + $insert = $this->userService->create($user); + + self::assertSame(1, $insert); + } + + public function testCanNotCreateUser(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with( + name: TestConstants::USER_NAME, + email: TestConstants::USER_EMAIL, + ); + + self::expectException(DuplicateEntryException::class); + + $this->userService->create($user); + } + + public function testCanUpdateLastUserActionTime(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with( + id: TestConstants::USER_ID, + lastActionAt: new DateTimeImmutable(), + ); + + $update = $this->userService->updateLastUserActionTime($user); + + self::assertInstanceOf(User::class, $update); + } + + public function testCanNotUpdateUser(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with(id: TestConstants::USER_ID_UNUSED); + + self::expectException(InvalidArgumentException::class); + + $this->userService->update($user); + } + + public function testCanUpdateUser(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with(id: TestConstants::USER_ID); + + $update = $this->userService->update($user); + + self::assertSame(true, $update); + } + + public function testFindByIdResultIsNull(): void + { + $result = $this->userService->findById(TestConstants::USER_ID_UNUSED); + + self::assertNull($result); + } + + public function testCanFindById(): void + { + $user = $this->userService->findById(TestConstants::USER_ID); + + self::assertInstanceOf(User::class, $user); + } + + public function testCanFindByUuid(): void + { + $user = $this->userService->findByUuid(TestConstants::USER_UUID); + + self::assertInstanceOf(User::class, $user); + } + + public function testCanNotFindByUuid(): void + { + $user = $this->userService->findByUuid(TestConstants::USER_UUID_UNUSED); + + self::assertNull($user); + } +} diff --git a/tests/Unit/Core/Table/AbstractTable.php b/tests/Unit/Core/Table/AbstractTable.php new file mode 100644 index 00000000..1cf05318 --- /dev/null +++ b/tests/Unit/Core/Table/AbstractTable.php @@ -0,0 +1,39 @@ + 1]; + protected array $fetchAllResult + = [ + 0 => ['id' => 1], + ]; + + protected function setUp(): void + { + $this->query = new MockQuery(); + + preg_match('@(Core.*|App.*)@i', get_class($this), $table); + + $this->table = new ( + substr($table[0], self::TABLE_NAME_OFFSET, self::TABLE_SUB_LENGTH) + )( + $this->query + ); + } +} diff --git a/tests/Unit/Core/Table/UserTableTest.php b/tests/Unit/Core/Table/UserTableTest.php new file mode 100644 index 00000000..6a6d5fd7 --- /dev/null +++ b/tests/Unit/Core/Table/UserTableTest.php @@ -0,0 +1,158 @@ +table->getTableName()); + } + + public function testCanInsertUser(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with(name: TestConstants::USER_CREATE_NAME); + + $affectedRowCount = $this->table->insert($user); + + self::assertSame(1, $affectedRowCount); + } + + public function testCanNotInsertUser(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with(name: TestConstants::USER_NAME); + + self::expectException(DuplicateEntryException::class); + + $this->table->insert($user); + } + + public function testCanUpdateUser(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with( + id: TestConstants::USER_ID, + registrationAt: new DateTimeImmutable(), + lastActionAt: new DateTimeImmutable(), + ); + + $updateUser = $this->table->update($user); + + self::assertSame(1, $updateUser); + } + + public function testUpdateUserThrowException(): void + { + $user = new User(...UserTestEntity::getDefaultUserValue()); + $user = $user->with( + id: TestConstants::USER_ID_THROW_EXCEPTION, + lastActionAt: new DateTimeImmutable(), + ); + + $table = new UserTable(new MockQueryForCanNot()); + + self::expectException(InvalidArgumentException::class); + + $table->update($user); + } + + public function testCanUpdateLastUserActionTime(): void + { + $updateUser = $this->table->updateLastUserActionTime(TestConstants::USER_ID, new DateTime()); + + self::assertInstanceOf(UserTable::class, $updateUser); + } + + public function testUpdateLastUserActionTimeThrowException(): void + { + self::expectException(InvalidArgumentException::class); + + $this->table->updateLastUserActionTime(TestConstants::USER_ID_THROW_EXCEPTION, new DateTime()); + } + + public function testCanFindById(): void + { + $user = $this->table->findById(TestConstants::USER_ID); + + self::assertEquals(UserTestEntity::getDefaultUserValue(), $user); + } + + public function testFindByIdHasEmptyResult(): void + { + $user = $this->table->findById(TestConstants::USER_ID_UNUSED); + + self::assertSame([], $user); + } + + public function testCanFindByUuid(): void + { + $user = $this->table->findByUuid(TestConstants::USER_UUID); + + self::assertEquals(UserTestEntity::getDefaultUserValue(), $user); + } + + public function testFindByUuidHasEmptyResult(): void + { + $user = $this->table->findByUuid(TestConstants::USER_UUID_UNUSED); + + self::assertSame([], $user); + } + + public function testCanFindAll(): void + { + $users = $this->table->findAll(); + + self::assertEquals([0 => UserTestEntity::getDefaultUserValue()], $users); + } + + public function testFindAllReturnedEmpty(): void + { + $table = new UserTable(new MockQueryForCanNot()); + $users = $table->findAll(); + + self::assertSame([], $users); + } + + public function testCanFindByName(): void + { + $user = $this->table->findByName(TestConstants::USER_NAME); + + self::assertEquals(UserTestEntity::getDefaultUserValue(), $user); + } + + public function testFindByNameHasEmptyResult(): void + { + $user = $this->table->findByName(TestConstants::USER_NAME_UNUSED); + + self::assertSame([], $user); + } + + public function testCanFindByEmail(): void + { + $user = $this->table->findByEMail(TestConstants::USER_EMAIL); + + self::assertEquals(UserTestEntity::getDefaultUserValue(), $user); + } + + public function testFindByEmailHasEmptyResult(): void + { + $user = $this->table->findByEMail(TestConstants::USER_EMAIL_UNUSED); + + self::assertSame([], $user); + } +} diff --git a/tests/UnitTest/Mock/Database/MockDelete.php b/tests/Unit/Mock/Database/MockDelete.php similarity index 62% rename from tests/UnitTest/Mock/Database/MockDelete.php rename to tests/Unit/Mock/Database/MockDelete.php index e8c9188e..b903e5cb 100644 --- a/tests/UnitTest/Mock/Database/MockDelete.php +++ b/tests/Unit/Mock/Database/MockDelete.php @@ -1,9 +1,10 @@ true, + 'Event', 'MockEvent' => $this->handleEvent($where, $value), default => false, }; } + + private function handleEvent(array $where, array $value): bool + { + if ($where[0][1] === 'id = ?' && $value[0] === TestConstants::EVENT_ID) { + return true; + } + + return false; + } } diff --git a/tests/Unit/Mock/Database/MockInsert.php b/tests/Unit/Mock/Database/MockInsert.php new file mode 100644 index 00000000..c7409a2b --- /dev/null +++ b/tests/Unit/Mock/Database/MockInsert.php @@ -0,0 +1,58 @@ +handle($this->statements['INSERT INTO'], $this->statements['VALUES']); + } + + private function handle(string $table, array $values): int|bool + { + return match($table) { + 'Event', 'MockEvent' => $this->handleEvent($values), + 'User', 'MockUser' => $this->handleUser($values), + 'Participant', 'MockParticipant' => $this->handleParticipant($values), + default => false, + }; + } + + private function handleEvent(array $values): int|bool + { + if ($values[0]['title'] === TestConstants::EVENT_CREATE_TITLE) { + return 1; + } + + return false; + } + + private function handleParticipant(array $values): int|bool + { + if ($values[0]['userId'] === TestConstants::USER_CREATE_ID) { + return 1; + } + + return false; + } + + private function handleUser(array $values): int|bool + { + + if ($values[0]['name'] === TestConstants::USER_CREATE_NAME) { + return 1; + } + + return false; + } +} diff --git a/tests/UnitTest/Mock/Database/MockPDO.php b/tests/Unit/Mock/Database/MockPDO.php similarity index 88% rename from tests/UnitTest/Mock/Database/MockPDO.php rename to tests/Unit/Mock/Database/MockPDO.php index 51c8ad13..80a828fc 100644 --- a/tests/UnitTest/Mock/Database/MockPDO.php +++ b/tests/Unit/Mock/Database/MockPDO.php @@ -1,6 +1,6 @@ statements['SELECT']) + && $this->statements['SELECT'][1] === 'COUNT(id) AS countTopic' + ) { + return [ + 'countTopic' => 1, + ]; + } + + if (array_key_exists('WHERE', $this->statements)) { + return $this->handle($this->statements['FROM'], $this->statements['WHERE'], $this->parameters['WHERE']); + } + + return false; + } + + public function fetchAll($index = '', $selectOnly = ''): array + { + return match ($this->getFromTable()) { + 'MockEvent', 'Event' => [0 => EventTestEntity::getDefaultEventValue()], + 'MockParticipant', 'Participant' => [0 => ParticipantTestEntity::getDefaultParticipantValue()], + 'MockProject', 'Project' => [0 => ProjectTestEntity::getDefaultProjectValue()], + 'MockRole', 'Role' => [0 => RoleTestEntity::getDefaultRoleValue()], + 'MockTopicPool', 'TopicPool' => [0 => TopicTestEntity::getDefaultTopicValue()], + 'MockUser', 'User' => [0 => UserTestEntity::getDefaultUserValue()], + default => [], + }; + } + + private function handle(string $from, array $where, array $params): bool|array + { + return match ($from) { + 'Event' => $this->handleEvent($where, $params), + 'Participant' => $this->handleParticipant($where, $params), + 'Project' => $this->handleProject($where, $params), + 'TopicPool' => $this->handleTopic($where, $params), + 'User' => $this->handleUser($where, $params), + default => false + }; + } + + private function handleEvent(array $where, array $params): array + { + return match ($where[0][1]) { + 'id = ?' => $params[0] === TestConstants::EVENT_ID + ? ['id' => TestConstants::EVENT_ID] + EventTestEntity::getDefaultEventValue() + : [], + 'title = ?' => $params[0] === TestConstants::EVENT_TITLE + ? ['id' => TestConstants::EVENT_ID] + EventTestEntity::getDefaultEventValue() + : [], + default => [] + }; + } + + private function handleParticipant(array $where, array $params): array + { + return match ($where[0][1]) { + 'id = ?' => $params[0] === TestConstants::PARTICIPANT_ID + ? ['id' => TestConstants::PARTICIPANT_ID] + ParticipantTestEntity::getDefaultParticipantValue() + : [], + 'userId = ?' => $params[0] === TestConstants::USER_ID + ? ['id' => TestConstants::PARTICIPANT_ID] + ParticipantTestEntity::getDefaultParticipantValue() + : [], + default => [] + }; + } + + private function handleProject(array $where, array $params): array + { + return match ($where[0][1]) { + 'id = ?' => $params[0] === TestConstants::PROJECT_ID + ? ['id' => TestConstants::PROJECT_ID] + ProjectTestEntity::getDefaultProjectValue() + : [], + 'participantId = ?' => + $params[0] === TestConstants::PARTICIPANT_ID + ? ['id' => TestConstants::PROJECT_ID] + ProjectTestEntity::getDefaultProjectValue() + : [], + default => [] + }; + } + + private function handleTopic(array $where, array $params): array + { + return match ($where[0][1]) { + 'id = ?' => $params[0] === TestConstants::TOPIC_ID + ? ['id' => TestConstants::TOPIC_ID] + TopicTestEntity::getDefaultTopicValue() + : [], + 'uuid = ?' => $params[0] === TestConstants::TOPIC_UUID + ? ['id' => TestConstants::TOPIC_ID] + TopicTestEntity::getDefaultTopicValue() + : [], + 'eventId = ?' => $params[0] === TestConstants::EVENT_ID + ? ['id' => TestConstants::TOPIC_ID] + TopicTestEntity::getDefaultTopicValue() + : [], + 'topic = ?' => $params[0] === TestConstants::TOPIC_TITLE + ? ['id' => TestConstants::TOPIC_ID] + TopicTestEntity::getDefaultTopicValue() + : [], + default => [] + }; + } + + private function handleUser(array $where, array $params): array + { + return match ($where[0][1]) { + 'id = ?' => $params[0] === TestConstants::USER_ID + ? ['id' => TestConstants::USER_ID] + UserTestEntity::getDefaultUserValue() + : [], + 'uuid = ?' => $params[0] === TestConstants::USER_UUID + ? ['id' => TestConstants::USER_ID] + UserTestEntity::getDefaultUserValue() + : [], + 'name = ?' => $params[0] === TestConstants::USER_NAME + ? ['id' => TestConstants::USER_ID] + UserTestEntity::getDefaultUserValue() + : [], + 'email = ?' => $params[0] === TestConstants::USER_EMAIL + ? ['id' => TestConstants::USER_ID] + UserTestEntity::getDefaultUserValue() + : [], + default => [], + }; + } +} diff --git a/tests/Unit/Mock/Database/MockSelectForFetchAll.php b/tests/Unit/Mock/Database/MockSelectForFetchAll.php new file mode 100644 index 00000000..7936b6c9 --- /dev/null +++ b/tests/Unit/Mock/Database/MockSelectForFetchAll.php @@ -0,0 +1,109 @@ +statements['SELECT']) + && $this->statements['SELECT'][1] === 'COUNT(id) AS countTopic' + ) { + return [ + 'countTopic' => 1, + ]; + } + + if (array_key_exists('WHERE', $this->statements)) { + return $this->handle($this->statements['FROM'], $this->statements['WHERE'], $this->parameters['WHERE']); + } + + return false; + } + + public function fetchAll($index = '', $selectOnly = ''): array + { + return []; + } + + private function handle(string $from, array $where, array $params): bool|array + { + return match ($from) { + 'Event' => $this->handleEvent($where, $params), + 'Participant' => $this->handleParticipant($where, $params), + 'Project' => $this->handleProject($where, $params), + 'TopicPool' => $this->handleTopic($where, $params), + 'User' => $this->handleUser($where, $params), + default => false + }; + } + + private function handleEvent(array $where, array $params): array + { + return match ($where[0][1]) { + 'title = ?' => $params[0] === TestConstants::EVENT_TITLE ? EventTestEntity::getDefaultEventValue() : [], + 'id = ?' => $params[0] === TestConstants::EVENT_ID ? EventTestEntity::getDefaultEventValue() : [], + default => [] + }; + } + + private function handleParticipant(array $where, array $params): array + { + return match ($where[0][1]) { + 'id = ?' => $params[0] === TestConstants::PARTICIPANT_ID + ? ParticipantTestEntity::getDefaultParticipantValue() : [], + 'userId = ?' => $params[0] === TestConstants::USER_ID ? ParticipantTestEntity::getDefaultParticipantValue() + : [], + default => [] + }; + } + + private function handleProject(array $where, array $params): array + { + return match ($where[0][1]) { + 'id = ?' => $params[0] === TestConstants::PROJECT_ID ? ProjectTestEntity::getDefaultProjectValue() : [], + 'participantId = ?' => $params[0] === TestConstants::PARTICIPANT_ID + ? ProjectTestEntity::getDefaultProjectValue() : [], + default => [] + }; + } + + private function handleTopic(array $where, array $params): array + { + return match ($where[0][1]) { + 'id = ?' => $params[0] === TestConstants::TOPIC_ID ? TopicTestEntity::getDefaultTopicValue() : [], + 'uuid = ?' => $params[0] === TestConstants::TOPIC_UUID ? TopicTestEntity::getDefaultTopicValue() : [], + 'eventId = ?' => $params[0] === TestConstants::EVENT_ID ? TopicTestEntity::getDefaultTopicValue() : [], + 'topic = ?' => $params[0] === TestConstants::TOPIC_TITLE ? TopicTestEntity::getDefaultTopicValue() : [], + default => [] + }; + } + + private function handleUser(array $where, array $params): array + { + return match ($where[0][1]) { + 'id = ?' => $params[0] === TestConstants::USER_ID ? UserTestEntity::getDefaultUserValue() : [], + 'uuid = ?' => $params[0] === TestConstants::USER_UUID ? UserTestEntity::getDefaultUserValue() : [], + 'name = ?' => $params[0] === TestConstants::USER_NAME ? UserTestEntity::getDefaultUserValue() : [], + 'email = ?' => $params[0] === TestConstants::USER_EMAIL ? UserTestEntity::getDefaultUserValue() : [], + default => [] + }; + } +} diff --git a/tests/Unit/Mock/Database/MockUpdate.php b/tests/Unit/Mock/Database/MockUpdate.php new file mode 100644 index 00000000..5824db9f --- /dev/null +++ b/tests/Unit/Mock/Database/MockUpdate.php @@ -0,0 +1,46 @@ +statements['UPDATE']) { + 'User', 'MockUser' => $this->handleUser(), + default => true + }; + } + + private function handleUser(): bool|int + { + if (array_key_exists('SET', $this->statements)) { + if ($this->statements['SET'] === []) { + return 1; + } + + if ($this->statements['SET']['lastActionAt']) { + return match ($this->statements['WHERE'][0][1]) { + 'id = ?' => $this->parameters['WHERE'][0] === TestConstants::USER_ID ? 1 : false, + default => 1 + }; + } + } + + return false; + } +} diff --git a/tests/Unit/Mock/Database/MockUpdateForNotUpdate.php b/tests/Unit/Mock/Database/MockUpdateForNotUpdate.php new file mode 100644 index 00000000..0826c8ec --- /dev/null +++ b/tests/Unit/Mock/Database/MockUpdateForNotUpdate.php @@ -0,0 +1,46 @@ +statements['UPDATE']) { + 'User', 'MockUser' => $this->handleUser(), + default => true + }; + } + + private function handleUser(): bool|int + { + if (array_key_exists('SET', $this->statements)) { + if ($this->statements['SET'] === []) { + return false; + } + + if ($this->statements['SET']['lastAction']) { + return match ($this->statements['WHERE'][0][1]) { + 'id = ?' => $this->parameters['WHERE'][0] === TestConstants::USER_ID ? 1 : false, + default => 1 + }; + } + } + + return false; + } +} diff --git a/tests/FunctionalTest/Mock/NullMailer.php b/tests/Unit/Mock/Mailer/MockMailer.php similarity index 68% rename from tests/FunctionalTest/Mock/NullMailer.php rename to tests/Unit/Mock/Mailer/MockMailer.php index 5f05e41f..e069d46c 100644 --- a/tests/FunctionalTest/Mock/NullMailer.php +++ b/tests/Unit/Mock/Mailer/MockMailer.php @@ -1,15 +1,15 @@ headers; + } + + public function hasHeader($name) + { + // TODO: Implement hasHeader() method. + } + + public function getHeader($name) + { + return array_key_exists($name, $this->headers) ? $this->headers[$name] : null; + } + + public function getHeaderLine($name) + { + // TODO: Implement getHeaderLine() method. + } + + public function withHeader($name, $value) + { + $this->headers[$name] = $value; + + return clone $this; + } + + public function withAddedHeader($name, $value) + { + // TODO: Implement withAddedHeader() method. + } + + public function withoutHeader($name) + { + // TODO: Implement withoutHeader() method. + } + + public function getBody() + { + return $this->body; + } + + public function withBody(StreamInterface $body) + { + // TODO: Implement withBody() method. + } + + public function getRequestTarget() + { + // TODO: Implement getRequestTarget() method. + } + + public function withRequestTarget($requestTarget) + { + // TODO: Implement withRequestTarget() method. + } + + public function getMethod() + { + // TODO: Implement getMethod() method. + } + + public function withMethod($method) + { + // TODO: Implement withMethod() method. + } + + public function getUri() + { + // TODO: Implement getUri() method. + } + + public function withUri(UriInterface $uri, $preserveHost = false) + { + // TODO: Implement withUri() method. + } + + public function getServerParams() + { + // TODO: Implement getServerParams() method. + } + + public function getCookieParams() + { + // TODO: Implement getCookieParams() method. + } + + public function withCookieParams(array $cookies) + { + // TODO: Implement withCookieParams() method. + } + + public function getQueryParams() + { + return $this->queryParams; + } + + public function withQueryParams(array $query): self + { + $this->queryParams = $query; + + return clone $this; + } + + public function getUploadedFiles() + { + // TODO: Implement getUploadedFiles() method. + } + + public function withUploadedFiles(array $uploadedFiles) + { + // TODO: Implement withUploadedFiles() method. + } + + public function getParsedBody() + { + return $this->body; + } + + public function withParsedBody($data) + { + $this->body = $data; + + return clone $this; + } + + public function getAttributes() + { + // TODO: Implement getAttributes() method. + } + + public function getAttribute($name, $default = null) + { + if (array_key_exists($name, $this->attributes)) { + return $this->attributes[$name]; + } + + return $default; + } + + public function withAttribute($name, $value): MockServerRequest + { + $this->attributes[$name] = $value; + + return clone $this; + } + + public function withoutAttribute($name) + { + // TODO: Implement withoutAttribute() method. + } +} diff --git a/tests/Unit/Mock/Service/MockApiAccessService.php b/tests/Unit/Mock/Service/MockApiAccessService.php new file mode 100644 index 00000000..f599f871 --- /dev/null +++ b/tests/Unit/Mock/Service/MockApiAccessService.php @@ -0,0 +1,18 @@ +id === TestConstants::EVENT_ID_NOT_REMOVED; + } + + public function findById(int $id): Event + { + if ($id === TestConstants::EVENT_ID) { + return new Event(...EventTestEntity::getDefaultEventValue()); + } + + throw new InvalidArgumentException('Could not find Event', HTTP::STATUS_BAD_REQUEST); + } + + public function findByTitle(string $topic): ?Event + { + if ($topic === TestConstants::EVENT_TITLE_THROW_EXCEPTION) { + throw new InvalidArgumentException(code: HTTP::STATUS_BAD_REQUEST); + } + if ($topic === TestConstants::EVENT_TITLE) { + return new Event(...EventTestEntity::getDefaultEventValue()); + } + + return null; + } +} diff --git a/tests/Unit/Mock/Service/MockTopicCreateEMailService.php b/tests/Unit/Mock/Service/MockTopicCreateEMailService.php new file mode 100644 index 00000000..c1026981 --- /dev/null +++ b/tests/Unit/Mock/Service/MockTopicCreateEMailService.php @@ -0,0 +1,14 @@ +with(lastActionAt: new DateTimeImmutable(TestConstants::TIME)); + } +} diff --git a/tests/Unit/Mock/Table/MockEventTable.php b/tests/Unit/Mock/Table/MockEventTable.php new file mode 100644 index 00000000..9e5c739e --- /dev/null +++ b/tests/Unit/Mock/Table/MockEventTable.php @@ -0,0 +1,44 @@ + [ + 'id' => $id, + 'ratingCompleted' => true, + ] + EventTestEntity::getDefaultEventValue(), + + TestConstants::EVENT_ID_RATING_NOT_COMPLETED => [ + 'id' => $id, + 'ratingCompleted' => false, + ] + EventTestEntity::getDefaultEventValue(), + + default => [] + }; + } + + public function findByTitle(string $title): array + { + return match ($title) { + TestConstants::EVENT_TITLE => [ + 'title' => $title, + ] + EventTestEntity::getDefaultEventValue(), + + default => [] + }; + } +} diff --git a/tests/Unit/Mock/Table/MockParticipantTable.php b/tests/Unit/Mock/Table/MockParticipantTable.php new file mode 100644 index 00000000..fefab7f8 --- /dev/null +++ b/tests/Unit/Mock/Table/MockParticipantTable.php @@ -0,0 +1,45 @@ +id === TestConstants::PARTICIPANT_ID; + } + + public function findById(int $id): array + { + return $id === TestConstants::PARTICIPANT_ID ? ['id' => $id] + + ParticipantTestEntity::getDefaultParticipantValue() : []; + } + + public function findByUserId(int $userId): array + { + return $userId === TestConstants::USER_ID + ? ['userId' => $userId] + ParticipantTestEntity::getDefaultParticipantValue() + : []; + } + + public function findUserForAnEvent(int $userId, int $eventId): array + { + return $userId === TestConstants::USER_ID && $eventId === TestConstants::EVENT_ID + ? [ + 'userId' => $userId, + 'eventId' => $eventId, + ] + ParticipantTestEntity::getDefaultParticipantValue() + : []; + } +} diff --git a/tests/Unit/Mock/Table/MockProjectTable.php b/tests/Unit/Mock/Table/MockProjectTable.php new file mode 100644 index 00000000..8bde0afd --- /dev/null +++ b/tests/Unit/Mock/Table/MockProjectTable.php @@ -0,0 +1,28 @@ + $id] + ProjectTestEntity::getDefaultProjectValue() : []; + } + + public function findByParticipantId(int $id): array + { + return $id === TestConstants::PARTICIPANT_ID + ? ['participantId' => $id] + ProjectTestEntity::getDefaultProjectValue() + : []; + } +} diff --git a/tests/Unit/Mock/Table/MockTopicPoolTable.php b/tests/Unit/Mock/Table/MockTopicPoolTable.php new file mode 100644 index 00000000..4f567907 --- /dev/null +++ b/tests/Unit/Mock/Table/MockTopicPoolTable.php @@ -0,0 +1,46 @@ + $id] + TopicTestEntity::getDefaultTopicValue() : []; + } + + public function findByEventId(int $eventId): array + { + return $eventId === TestConstants::EVENT_ID + ? ['eventId' => $eventId] + TopicTestEntity::getDefaultTopicValue() + : []; + } + + public function findByTopic(string $topic): array + { + return $topic === TestConstants::TOPIC_TITLE + ? ['topic' => $topic] + TopicTestEntity::getDefaultTopicValue() + : []; + } +} diff --git a/tests/Unit/Mock/Table/MockUserTable.php b/tests/Unit/Mock/Table/MockUserTable.php new file mode 100644 index 00000000..15f26e44 --- /dev/null +++ b/tests/Unit/Mock/Table/MockUserTable.php @@ -0,0 +1,50 @@ +id !== TestConstants::USER_ID) { + throw new InvalidArgumentException(); + } + + return 1; + } + + public function findById(int $id): array + { + return $id === TestConstants::USER_ID ? ['id' => $id] + UserTestEntity::getDefaultUserValue() : []; + } + + public function findByUuid(string $uuid): array + { + return $uuid === TestConstants::USER_UUID + ? ['uuid' => UuidV7::fromString($uuid)] + UserTestEntity::getDefaultUserValue() + : []; + } + + public function findByName(string $name): array + { + return $name === TestConstants::USER_NAME ? ['name' => $name] + UserTestEntity::getDefaultUserValue() : []; + } + + public function findByEMail(string $email): array + { + return $email === TestConstants::USER_EMAIL ? ['email' => $email] + UserTestEntity::getDefaultUserValue() : []; + } +} diff --git a/tests/Unit/Mock/Validator/MockEventCreateValidator.php b/tests/Unit/Mock/Validator/MockEventCreateValidator.php new file mode 100644 index 00000000..6599f0d0 --- /dev/null +++ b/tests/Unit/Mock/Validator/MockEventCreateValidator.php @@ -0,0 +1,43 @@ +data = $data; + + return $this; + } + + public function isValid($context = null): bool + { + return $this->data[0]; + } + + public function getValues(): mixed + { + return $this->data; + } +} diff --git a/tests/Unit/Mock/Validator/MockTopicCreateValidator.php b/tests/Unit/Mock/Validator/MockTopicCreateValidator.php new file mode 100644 index 00000000..9a2c70d4 --- /dev/null +++ b/tests/Unit/Mock/Validator/MockTopicCreateValidator.php @@ -0,0 +1,20 @@ +request->withAttribute(AccessToken::class, AccessToken::fromString(Token::ACCESS_TOKEN_VALID)) - ->withAttribute(RefreshToken::class, RefreshToken::fromString(Token::REFRESH_TOKEN_VALID)); - - $authenticationHandler = new AuthenticationHandler(); - - $response = $authenticationHandler->handle($request); - - $json = json_decode((string)$response->getBody(), null, 512, JSON_THROW_ON_ERROR); - - $this->assertInstanceOf(JsonResponse::class, $response); - $this->assertSame(HTTP::STATUS_OK, $response->getStatusCode()); - $this->assertTrue(property_exists($json, 'accessToken') && $json->accessToken === Token::ACCESS_TOKEN_VALID); - $this->assertTrue(property_exists($json, 'refreshToken') && $json->refreshToken === Token::REFRESH_TOKEN_VALID); - } -} diff --git a/tests/UnitTest/AppTest/Handler/PingHandlerTest.php b/tests/UnitTest/AppTest/Handler/PingHandlerTest.php deleted file mode 100644 index ba820e46..00000000 --- a/tests/UnitTest/AppTest/Handler/PingHandlerTest.php +++ /dev/null @@ -1,29 +0,0 @@ -handle( - $this->createMock(ServerRequestInterface::class) - ); - - $json = json_decode((string)$response->getBody(), null, 512, JSON_THROW_ON_ERROR); - - self::assertInstanceOf(JsonResponse::class, $response); - self::assertTrue(property_exists($json, 'ack') && $json->ack !== null); - } -} diff --git a/tests/UnitTest/AppTest/Hydrator/AccountAccessAuthHydratorTest.php b/tests/UnitTest/AppTest/Hydrator/AccountAccessAuthHydratorTest.php deleted file mode 100644 index 5675db67..00000000 --- a/tests/UnitTest/AppTest/Hydrator/AccountAccessAuthHydratorTest.php +++ /dev/null @@ -1,55 +0,0 @@ -hydrate(AccountAccessAuth::VALID_DATA); - - $this->assertInstanceOf(AccountAccessAuthInterface::class, $account); - $this->assertSame(AccountAccessAuth::ID, $account->id); - } - - public function testCanHydrateAccountAccessAuthCollection(): void - { - $hydrator = new AccountAccessAuthHydrator(); - - $accounts = $hydrator->hydrateCollection([AccountAccessAuth::VALID_DATA]); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $accounts); - $this->assertInstanceOf(AccountAccessAuthInterface::class, $accounts[0]); - $this->assertSame(AccountAccessAuth::ID, $accounts[0]->id); - } - - public function testCanExtractAccountAccessAuth(): void - { - $hydrator = new AccountAccessAuthHydrator(); - - $account = $hydrator->hydrate(AccountAccessAuth::VALID_DATA); - $account = $hydrator->extract($account); - - $this->assertIsArray($account); - $this->assertSame(AccountAccessAuth::VALID_DATA, $account); - } - - public function testCanExtractAccountAccessAuthCollection(): void - { - $hydrator = new AccountAccessAuthHydrator(); - $accounts = $hydrator->hydrateCollection([AccountAccessAuth::VALID_DATA]); - $accounts = $hydrator->extractCollection($accounts); - - $this->assertIsArray($accounts); - $this->assertArrayHasKey(0, $accounts); - $this->assertSame(AccountAccessAuth::VALID_DATA, $accounts[0]); - } -} diff --git a/tests/UnitTest/AppTest/Hydrator/AccountHydratorTest.php b/tests/UnitTest/AppTest/Hydrator/AccountHydratorTest.php deleted file mode 100644 index 1b34dfea..00000000 --- a/tests/UnitTest/AppTest/Hydrator/AccountHydratorTest.php +++ /dev/null @@ -1,66 +0,0 @@ -uuidFactory = new UuidFactory(); - } - - public function testCanHydrateAccount(): void - { - $hydrator = new AccountHydrator($this->uuidFactory); - - $account = $hydrator->hydrate(Account::VALID_DATA); - - $this->assertInstanceOf(AccountInterface::class, $account); - $this->assertSame(Account::ID, $account->id); - } - - public function testCanHydrateAccountCollection(): void - { - $hydrator = new AccountHydrator($this->uuidFactory); - - /** @var AccountInterface[] $accounts | [] */ - $accounts = $hydrator->hydrateCollection([Account::VALID_DATA]); - - $this->assertInstanceOf(AccountCollectionInterface::class, $accounts); - $this->assertInstanceOf(AccountInterface::class, $accounts[0]); - $this->assertSame(Account::ID, $accounts[0]->id); - } - - public function testCanExtractAccount(): void - { - $hydrator = new AccountHydrator($this->uuidFactory); - - $account = $hydrator->hydrate(Account::VALID_DATA); - $account = $hydrator->extract($account); - - $this->assertIsArray($account); - $this->assertSame(Account::VALID_DATA, $account); - } - - public function testCanExtractAccountCollection(): void - { - $hydrator = new AccountHydrator($this->uuidFactory); - $accounts = $hydrator->hydrateCollection([Account::VALID_DATA]); - $accounts = $hydrator->extractCollection($accounts); - - $this->assertIsArray($accounts); - $this->assertArrayHasKey(0, $accounts); - $this->assertSame(Account::VALID_DATA, $accounts[0]); - } -} diff --git a/tests/UnitTest/AppTest/Middleware/AccountAccessAuthPersistMiddlewareTest.php b/tests/UnitTest/AppTest/Middleware/AccountAccessAuthPersistMiddlewareTest.php deleted file mode 100644 index d51531b2..00000000 --- a/tests/UnitTest/AppTest/Middleware/AccountAccessAuthPersistMiddlewareTest.php +++ /dev/null @@ -1,108 +0,0 @@ -repository = new MockAccountAccessAuthRepository(); - $this->hydrator = new AccountHydrator(new UuidFactory()); - } - - public function testCanPersistAccountAccessAuth(): void - { - $middleware = new PersistAuthenticationMiddleware($this->repository); - $account = $this->hydrator->hydrate(Account::VALID_DATA); - $clientData = ClientIdentificationData::create('1', 'default'); - $clientIdent = ClientIdentification::create($clientData, '1234'); - $refreshToken = RefreshToken::fromString('1234'); - - $request = $this->request->withAttribute(AccountInterface::AUTHENTICATED, $account) - ->withAttribute(ClientIdentification::class, $clientIdent) - ->withAttribute(RefreshToken::class, $refreshToken); - - $response = $middleware->process($request, $this->handler); - - $this->assertNotInstanceOf(JsonResponse::class, $response); - } - - public function testFindMissingAccountEntity(): void - { - $middleware = new PersistAuthenticationMiddleware($this->repository); - - $clientData = ClientIdentificationData::create('1', 'default'); - $clientIdent = ClientIdentification::create($clientData, '1234'); - $refreshToken = RefreshToken::fromString('1234'); - - $request = $this->request->withAttribute(ClientIdentification::class, $clientIdent) - ->withAttribute(RefreshToken::class, $refreshToken); - - $this->expectException(HttpUnauthorizedException::class); - $middleware->process($request, $this->handler); - } - - public function testFindMissingClientIdentification(): void - { - $middleware = new PersistAuthenticationMiddleware($this->repository); - - $account = $this->hydrator->hydrate(Account::VALID_DATA); - $refreshToken = RefreshToken::fromString('1234'); - - $request = $this->request->withAttribute(AccountInterface::AUTHENTICATED, $account) - ->withAttribute(RefreshToken::class, $refreshToken); - - $this->expectException(HttpUnauthorizedException::class); - $middleware->process($request, $this->handler); - } - - public function testFindMissingRefreshToken(): void - { - $middleware = new PersistAuthenticationMiddleware($this->repository); - - $account = $this->hydrator->hydrate(Account::VALID_DATA); - $clientData = ClientIdentificationData::create('1', 'default'); - $clientIdent = ClientIdentification::create($clientData, '1234'); - - $request = $this->request->withAttribute(AccountInterface::AUTHENTICATED, $account) - ->withAttribute(ClientIdentification::class, $clientIdent); - - $this->expectException(HttpUnauthorizedException::class); - $middleware->process($request, $this->handler); - } - - public function testAccountAccessAuthHasDuplicat(): void - { - $middleware = new PersistAuthenticationMiddleware($this->repository); - $account = $this->hydrator->hydrate(Account::INVALID_DATA); - $clientData = ClientIdentificationData::create('1', 'default'); - $clientIdent = ClientIdentification::create($clientData, '1234'); - $refreshToken = RefreshToken::fromString('1234'); - - $request = $this->request->withAttribute(AccountInterface::AUTHENTICATED, $account) - ->withAttribute(ClientIdentification::class, $clientIdent) - ->withAttribute(RefreshToken::class, $refreshToken); - - $this->expectException(HttpDuplicateEntryException::class); - $middleware->process($request, $this->handler); - } -} diff --git a/tests/UnitTest/AppTest/Middleware/AccountAuthenticationMiddlewareTest.php b/tests/UnitTest/AppTest/Middleware/AccountAuthenticationMiddlewareTest.php deleted file mode 100644 index a47c317c..00000000 --- a/tests/UnitTest/AppTest/Middleware/AccountAuthenticationMiddlewareTest.php +++ /dev/null @@ -1,107 +0,0 @@ -accessTokenService = new MockAccessTokenService(); - $this->accountRepository = new MockAccountRepository(); - $this->logger = new NullLogger(); - $this->uuidFactory = new UuidFactory(); - } - - public function testAccountAuthenticatedIsGuest(): void - { - $middleware = new RequestAuthenticationMiddleware( - $this->accessTokenService, - $this->accountRepository, - $this->uuidFactory, - $this->logger, - ); - $handler = new MockAccountAuthenticationMiddlewareRequestHandler(); - $response = $middleware->process($this->request, $handler); - $header = $response->getHeaderLine('Authorization'); - - $this->assertInstanceOf(ResponseInterface::class, $response); - $this->assertNotInstanceOf(JsonResponse::class, $response); - $this->assertSame('', $header); - } - - public function testAccountSuccessfulAuthenticated(): void - { - $accessToken = $this->accessTokenService->generate($this->uuidFactory->fromString(Account::UUID)); - $request = $this->request->withHeader('Authorization', $accessToken); - - $middleware = new RequestAuthenticationMiddleware( - $this->accessTokenService, - $this->accountRepository, - $this->uuidFactory, - $this->logger, - ); - - $handler = new MockAccountAuthenticationMiddlewareRequestHandler(); - $response = $middleware->process($request, $handler); - $header = $response->getHeaderLine('Authorization'); - - $this->assertNotInstanceOf(JsonResponse::class, $response); - $this->assertSame('true', $header); - } - - public function testTokenHasExpired(): void - { - $accessTokenService = new MockAccessTokenServiceWithoutDuration(); - $accessToken = $accessTokenService->generate($this->uuidFactory->fromString(Account::UUID)); - $request = $this->request->withHeader('Authorization', $accessToken); - - $middleware = new RequestAuthenticationMiddleware( - $this->accessTokenService, - $this->accountRepository, - $this->uuidFactory, - $this->logger, - ); - - $this->expectException(HttpUnauthorizedException::class); - $middleware->process($request, $this->handler); - } - - public function testTokenHasInvalid(): void - { - $accessToken = $this->accessTokenService->generate($this->uuidFactory->fromString(Account::UUID)); - $request = $this->request->withHeader('Authorization', $accessToken); - $accountRepository = new MockAccountRepositoryAccountAuthenticationMiddlewareInvalidToken(); - $middleware = new RequestAuthenticationMiddleware( - $this->accessTokenService, - $accountRepository, - $this->uuidFactory, - $this->logger, - ); - - $this->expectException(HttpUnauthorizedException::class); - $middleware->process($request, $this->handler); - } -} diff --git a/tests/UnitTest/AppTest/Middleware/AuthenticationConditionsMiddlewareTest.php b/tests/UnitTest/AppTest/Middleware/AuthenticationConditionsMiddlewareTest.php deleted file mode 100644 index 2ea196ca..00000000 --- a/tests/UnitTest/AppTest/Middleware/AuthenticationConditionsMiddlewareTest.php +++ /dev/null @@ -1,42 +0,0 @@ -middleware = new AuthenticationConditionsMiddleware(); - } - - public function testIsSuccessfully(): void - { - $response = $this->middleware->process($this->request, $this->handler); - - $this->assertInstanceOf(ResponseInterface::class, $response); - } - - public function testRequestIsAuthenticated(): void - { - $request = $this->request->withHeader('Authentication', []); - - $this->expectException(HttpUnauthorizedException::class); - $this->middleware->process($request, $this->handler); - } - - public function testRequestIsAuthorized(): void - { - $request = $this->request->withHeader('Authorization', []); - - $this->expectException(HttpUnauthorizedException::class); - $this->middleware->process($request, $this->handler); - } -} diff --git a/tests/UnitTest/AppTest/Middleware/AuthenticationMiddlewareTest.php b/tests/UnitTest/AppTest/Middleware/AuthenticationMiddlewareTest.php deleted file mode 100644 index 8c7e8a12..00000000 --- a/tests/UnitTest/AppTest/Middleware/AuthenticationMiddlewareTest.php +++ /dev/null @@ -1,65 +0,0 @@ -middleware = new AuthenticationMiddleware( - new MockAuthenticationService(), - new MockAccountRepository(), - ); - } - - public function testCanAuthenticatedAccount(): void - { - $bodyData = [ - 'email' => Account::EMAIL, - 'password' => Account::PASSWORD, - ]; - - $request = $this->request->withParsedBody($bodyData); - $response = $this->middleware->process($request, $this->handler); - - $this->assertNotInstanceOf(JsonResponse::class, $response); - } - - public function testCanNotFoundAccountWithEmail(): void - { - $bodyData = [ - 'email' => Account::EMAIL_INVALID, - 'password' => Account::PASSWORD, - ]; - - $request = $this->request->withParsedBody($bodyData); - - $this->expectException(HttpUnauthorizedException::class); - $this->middleware->process($request, $this->handler); - } - - public function testRequestWithInvalidPassword(): void - { - $bodyData = [ - 'email' => Account::EMAIL, - 'password' => Account::PASSWORD_INVALID, - ]; - - $request = $this->request->withParsedBody($bodyData); - - $this->expectException(HttpUnauthorizedException::class); - $this->middleware->process($request, $this->handler); - } -} diff --git a/tests/UnitTest/AppTest/Middleware/AuthenticationValidationMiddlewareTest.php b/tests/UnitTest/AppTest/Middleware/AuthenticationValidationMiddlewareTest.php deleted file mode 100644 index fc230a00..00000000 --- a/tests/UnitTest/AppTest/Middleware/AuthenticationValidationMiddlewareTest.php +++ /dev/null @@ -1,33 +0,0 @@ -process($this->request, $this->handler); - - $this->assertNotInstanceOf(JsonResponse::class, $response); - } - - public function testValidationFailed(): void - { - $middleware = new AuthenticationValidationMiddleware( - new MockAuthenticationValidatorFailed(), - ); - - $this->expectException(HttpUnauthorizedException::class); - $middleware->process($this->request, $this->handler); - } -} diff --git a/tests/UnitTest/AppTest/Middleware/ClientIdentificationMiddlewareTest.php b/tests/UnitTest/AppTest/Middleware/ClientIdentificationMiddlewareTest.php deleted file mode 100644 index 5c35eb83..00000000 --- a/tests/UnitTest/AppTest/Middleware/ClientIdentificationMiddlewareTest.php +++ /dev/null @@ -1,36 +0,0 @@ -middleware = new ClientIdentificationMiddleware( - new MockClientIdentificationService(), - ); - } - - public function testGenerateClientIdentification(): void - { - $request = $this->request->withHeader('x-ident', '1') - ->withHeader('user-agent', 'Test Browser Agent'); - - $response = $this->middleware->process($request, $this->handler); - - $this->assertInstanceOf(ResponseInterface::class, $response); - $this->assertNotInstanceOf(JsonResponse::class, $response); - } - - // ToDo Create test for error cases -} diff --git a/tests/UnitTest/AppTest/Middleware/GenerateAccessTokenMiddlewareTest.php b/tests/UnitTest/AppTest/Middleware/GenerateAccessTokenMiddlewareTest.php deleted file mode 100644 index c9230203..00000000 --- a/tests/UnitTest/AppTest/Middleware/GenerateAccessTokenMiddlewareTest.php +++ /dev/null @@ -1,34 +0,0 @@ -middleware = new GenerateAccessTokenMiddleware( - new MockAccessTokenService() - ); - } - - public function testCanGenerateAccessToken(): void - { - $request = $this->request->withAttribute(AccountInterface::AUTHENTICATED, new MockAccount()); - $response = $this->middleware->process($request, $this->handler); - - $this->assertInstanceOf(ResponseInterface::class, $response); - $this->assertNotInstanceOf(JsonResponse::class, $response); - } -} diff --git a/tests/UnitTest/AppTest/Middleware/GenerateRefreshTokenMiddlewareTest.php b/tests/UnitTest/AppTest/Middleware/GenerateRefreshTokenMiddlewareTest.php deleted file mode 100644 index 94609118..00000000 --- a/tests/UnitTest/AppTest/Middleware/GenerateRefreshTokenMiddlewareTest.php +++ /dev/null @@ -1,39 +0,0 @@ -middleware = new GenerateRefreshTokenMiddleware( - new MockRefreshTokenService() - ); - } - - public function testCanGenerateRefreshToken(): void - { - $data = ClientIdentification::create( - ClientIdentificationData::create(null, 'defaul'), - '1' - ); - - $request = $this->request->withAttribute(ClientIdentification::class, $data); - $response = $this->middleware->process($request, $this->handler); - - $this->assertInstanceOf(ResponseInterface::class, $response); - $this->assertNotInstanceOf(JsonResponse::class, $response); - } -} diff --git a/tests/UnitTest/AppTest/Repository/AccountAccessAuthRepositoryTest.php b/tests/UnitTest/AppTest/Repository/AccountAccessAuthRepositoryTest.php deleted file mode 100644 index a2d503b3..00000000 --- a/tests/UnitTest/AppTest/Repository/AccountAccessAuthRepositoryTest.php +++ /dev/null @@ -1,194 +0,0 @@ -repository = new AccountAccessAuthRepository(new MockAccountAccessAuthTable()); - $this->hydrator = new AccountAccessAuthHydrator(); - } - - public function testCanInsertAccountAccessAuth(): void - { - $result = $this->repository->insert($this->hydrator->hydrate(AccountAccessAuth::VALID_DATA)); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testInsertAccountAccessAuthThrowDuplicateEntryException(): void - { - $this->expectException(DuplicateEntryException::class); - - $this->repository->insert($this->hydrator->hydrate(AccountAccessAuth::INVALID_DATA)); - } - - public function testCanUpdateAccountAccessAuth(): void - { - $result = $this->repository->update($this->hydrator->hydrate(AccountAccessAuth::VALID_DATA)); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testUpdateAccountAccessAuthThrowInvalidArgumentException(): void - { - $this->expectException(InvalidArgumentException::class); - - $this->repository->update($this->hydrator->hydrate(AccountAccessAuth::INVALID_DATA)); - } - - public function testCanDeleteById(): void - { - $result = $this->repository->deleteById(AccountAccessAuth::ID); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testDeleteByIdThrowInvalidArgumentException(): void - { - $this->expectException(InvalidArgumentException::class); - - $this->repository->deleteById(AccountAccessAuth::ID_INVALID); - } - - public function testCanFindById(): void - { - $result = $this->repository->findById(AccountAccessAuth::ID); - - $this->assertInstanceOf(AccountAccessAuthInterface::class, $result); - $this->assertSame(AccountAccessAuth::VALID_DATA, $this->hydrator->extract($result)); - } - - public function testFindByIdIsEmpty(): void - { - $result = $this->repository->findById(AccountAccessAuth::ID_INVALID); - - $this->assertNull($result); - } - - public function testCanFindByUserId(): void - { - $result = $this->repository->findByAccountId(AccountAccessAuth::USER_ID); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertArrayHasKey(0, $result); - $this->assertInstanceOf(AccountAccessAuthInterface::class, $result[0]); - $this->assertSame([0 => AccountAccessAuth::VALID_DATA], $this->hydrator->extractCollection($result)); - } - - public function testFindByUserIdIsEmpty(): void - { - $result = $this->repository->findByAccountId(AccountAccessAuth::USER_ID_INVALID); - - $this->assertInstanceOf(AccountAccessAuthCollection::class, $result); - $this->assertEmpty($result); - } - - public function testCanFindByLabel(): void - { - $result = $this->repository->findByLabel(AccountAccessAuth::LABEL); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertArrayHasKey(0, $result); - $this->assertInstanceOf(AccountAccessAuthInterface::class, $result[0]); - $this->assertSame([0 => AccountAccessAuth::VALID_DATA], $this->hydrator->extractCollection($result)); - } - - public function testFindByLabelIsEmpty(): void - { - $result = $this->repository->findByLabel(AccountAccessAuth::LABEL_INVALID); - - $this->assertInstanceOf(AccountAccessAuthCollection::class, $result); - $this->assertEmpty($result); - } - - public function testCanFindByRefreshToken(): void - { - $result = $this->repository->findByRefreshToken(AccountAccessAuth::REFRESH_TOKEN); - - $this->assertInstanceOf(AccountAccessAuthInterface::class, $result); - $this->assertSame(AccountAccessAuth::VALID_DATA, $this->hydrator->extract($result)); - } - - public function testFindByRefreshTokenIsEmpty(): void - { - $result = $this->repository->findByRefreshToken(AccountAccessAuth::REFRESH_TOKEN_INVALID); - - $this->assertNull($result); - } - - public function testCanFindByUserAgent(): void - { - $result = $this->repository->findByUserAgent(AccountAccessAuth::USER_AGENT); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertArrayHasKey(0, $result); - $this->assertInstanceOf(AccountAccessAuthInterface::class, $result[0]); - $this->assertSame([0 => AccountAccessAuth::VALID_DATA], $this->hydrator->extractCollection($result)); - } - - public function testCanFindByUserAgentIsEmpty(): void - { - $result = $this->repository->findByUserAgent(AccountAccessAuth::USER_AGENT_INVALID); - - $this->assertInstanceOf(AccountAccessAuthCollection::class, $result); - $this->assertEmpty($result); - } - - public function testCanFindByClientIdentHash(): void - { - $result = $this->repository->findByClientIdentHash(AccountAccessAuth::CLIENT_IDENT_HASH); - - $this->assertInstanceOf(AccountAccessAuthInterface::class, $result); - $this->assertSame(AccountAccessAuth::VALID_DATA, $this->hydrator->extract($result)); - } - - public function testFindByClientIdentHashIsEmpty(): void - { - $result = $this->repository->findByClientIdentHash(AccountAccessAuth::CLIENT_IDENT_HASH_INVALID); - - $this->assertNull($result); - } - - public function testCanFindAll(): void - { - $result = $this->repository->findAll(); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertArrayHasKey(0, $result); - $this->assertInstanceOf(AccountAccessAuthInterface::class, $result[0]); - $this->assertSame([0 => AccountAccessAuth::VALID_DATA], $this->hydrator->extractCollection($result)); - } - - public function testFindAllIsEmpty(): void - { - $repository = new AccountAccessAuthRepository(new MockAccountAccessAuthTableFailed()); - - $result = $repository->findAll(); - - $this->assertInstanceOf(AccountAccessAuthCollection::class, $result); - $this->assertEmpty($result); - } -} diff --git a/tests/UnitTest/AppTest/Repository/AccountRepositoryTest.php b/tests/UnitTest/AppTest/Repository/AccountRepositoryTest.php deleted file mode 100644 index a1047935..00000000 --- a/tests/UnitTest/AppTest/Repository/AccountRepositoryTest.php +++ /dev/null @@ -1,161 +0,0 @@ -uuidFactory = new UuidFactory(); - $this->repository = new AccountRepository(new MockAccountTable()); - $this->hydrator = new AccountHydrator($this->uuidFactory); - } - - public function testCanInsertAccount(): void - { - $result = $this->repository->insert($this->hydrator->hydrate(Account::VALID_DATA)); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testInsertAccountThrowsException(): void - { - $this->expectException(DuplicateEntryException::class); - - $this->repository->insert($this->hydrator->hydrate(Account::INVALID_DATA)); - } - - public function testCanUpdateAccount(): void - { - $result = $this->repository->update($this->hydrator->hydrate(Account::VALID_DATA)); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testUpdateAccountThrowsException(): void - { - $this->expectException(InvalidArgumentException::class); - - $this->repository->update($this->hydrator->hydrate(Account::INVALID_DATA)); - } - - public function testCanDeleteAccountById(): void - { - $result = $this->repository->deleteById(Account::ID); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testDeleteAccountByIdThrowsInvalidArgumentException(): void - { - $this->expectException(InvalidArgumentException::class); - - $this->repository->deleteById(Account::ID_INVALID); - } - - public function testCanFindById(): void - { - $result = $this->repository->findById(Account::ID); - - $this->assertInstanceOf(AccountInterface::class, $result); - $this->assertSame(Account::VALID_DATA, $this->hydrator->extract($result)); - } - - public function testFindByIdIsEmpty(): void - { - $result = $this->repository->findById(Account::ID_INVALID); - - $this->assertNull($result); - } - - public function testCanFindByUuid(): void - { - $uuid = $this->uuidFactory->fromString(Account::UUID); - $result = $this->repository->findByUuid($uuid); - - $this->assertInstanceOf(AccountInterface::class, $result); - $this->assertSame(Account::VALID_DATA, $this->hydrator->extract($result)); - } - - public function testFindByUuidIsEmtpy(): void - { - $uuid = $this->uuidFactory->fromString(Account::UUID_INVALID); - - $result = $this->repository->findByUuid($uuid); - - $this->assertNull($result); - } - - public function testCanFindByName(): void - { - $result = $this->repository->findByName(Account::NAME); - - $this->assertInstanceOf(AccountInterface::class, $result); - $this->assertSame(Account::VALID_DATA, $this->hydrator->extract($result)); - } - - public function testFindByNameIsEmpty(): void - { - $result = $this->repository->findByName(Account::NAME_INVALID); - - $this->assertNull($result); - } - - public function testCanFindByEmail(): void - { - $result = $this->repository->findByEmail(new Email(Account::EMAIL)); - - $this->assertInstanceOf(AccountInterface::class, $result); - $this->assertSame(Account::VALID_DATA, $this->hydrator->extract($result)); - } - - public function testFindByEmailIsEmpty(): void - { - $result = $this->repository->findByEmail(new Email(Account::EMAIL_INVALID)); - - $this->assertNull($result); - } - - public function testCanFindAll(): void - { - $result = $this->repository->findAll(); - - $this->assertInstanceOf(AccountCollectionInterface::class, $result); - $this->assertArrayHasKey(0, $result); - $this->assertInstanceOf(AccountInterface::class, $result[0]); - $this->assertSame([0 => Account::VALID_DATA], $this->hydrator->extractCollection($result)); - } - - public function testFindAllIsEmpty(): void - { - $repository = new AccountRepository(new MockAccountTableFailed()); - - $result = $repository->findAll(); - - $this->assertInstanceOf(AccountCollectionInterface::class, $result); - $this->assertEmpty($result); - } -} diff --git a/tests/UnitTest/AppTest/Service/AccessTokenServiceTest.php b/tests/UnitTest/AppTest/Service/AccessTokenServiceTest.php deleted file mode 100644 index 4a8d0ca0..00000000 --- a/tests/UnitTest/AppTest/Service/AccessTokenServiceTest.php +++ /dev/null @@ -1,48 +0,0 @@ -account = new MockAccount(); - } - - public function testGenerateValidAccessToken(): void - { - $config = Token::getTokenStruct(); - $jwtTokenConfig = JwtTokenConfig::createFromArray($config); - - $service = new AccessTokenService($jwtTokenConfig); - - $token = $service->generate($this->account->uuid); - - $isValid = $service->isValid($token); - - $this->assertTrue($isValid); - } - - public function testGenerateValidAccessTokenFails(): void - { - $config = Token::getTokenStruct(); - $config['algorithmus'] = ''; - $jwtTokenConfig = JwtTokenConfig::createFromArray($config); - - $service = new AccessTokenService($jwtTokenConfig); - - $this->expectException(DomainException::class); - - $service->generate($this->account->uuid); - } -} diff --git a/tests/UnitTest/AppTest/Service/AuthenticationServiceTest.php b/tests/UnitTest/AppTest/Service/AuthenticationServiceTest.php deleted file mode 100644 index ebc0eb06..00000000 --- a/tests/UnitTest/AppTest/Service/AuthenticationServiceTest.php +++ /dev/null @@ -1,31 +0,0 @@ -service = new AuthenticationService(); - } - - public function testPasswordComparisonIsSuccessful(): void - { - $compare = $this->service->isPasswordMatch(Account::PASSWORD_STRING, Account::PASSWORD); - - $this->assertTrue($compare); - } - - public function testPasswordComparisonFails(): void - { - $compare = $this->service->isPasswordMatch(Account::PASSWORD_STRING, Account::PASSWORD_INVALID); - - $this->assertFalse($compare); - } -} diff --git a/tests/UnitTest/AppTest/Service/RefreshTokenServiceTest.php b/tests/UnitTest/AppTest/Service/RefreshTokenServiceTest.php deleted file mode 100644 index 568af821..00000000 --- a/tests/UnitTest/AppTest/Service/RefreshTokenServiceTest.php +++ /dev/null @@ -1,51 +0,0 @@ -client = ClientIdentification::create( - ClientIdentificationData::create(null, 'default'), - '1' - ); - } - - public function testGenerateValidRefreshToken(): void - { - $config = Token::getTokenStruct(); - $jwtTokenConfig = JwtTokenConfig::createFromArray($config); - - $service = new RefreshTokenService($jwtTokenConfig); - - $token = $service->generate($this->client); - - $isValid = $service->isValid($token); - - $this->assertTrue($isValid); - } - - public function testGenerateValidRefreshTokenFails(): void - { - $config = Token::getTokenStruct(); - $config['algorithmus'] = ''; - $jwtTokenConfig = JwtTokenConfig::createFromArray($config); - - $service = new RefreshTokenService($jwtTokenConfig); - - $this->expectException(DomainException::class); - - $service->generate($this->client); - } -} diff --git a/tests/UnitTest/AppTest/Table/AccountAccessAuthTableTest.php b/tests/UnitTest/AppTest/Table/AccountAccessAuthTableTest.php deleted file mode 100644 index c8ad6c3a..00000000 --- a/tests/UnitTest/AppTest/Table/AccountAccessAuthTableTest.php +++ /dev/null @@ -1,226 +0,0 @@ -query = new MockQuery(); - $this->hydrator = new AccountAccessAuthHydrator(); - $this->table = new AccountAccessAuthTable($this->query, $this->hydrator); - } - - public function testCanGetTableName(): void - { - $this->assertSame('AccountAccessAuth', $this->table->getTableName()); - } - - public function testCanInsertAccountAccessAuth(): void - { - $accountAccessAuth = $this->hydrator->hydrate(AccountAccessAuth::VALID_DATA); - - $result = $this->table->insert($accountAccessAuth); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testInsertAccountAccessAuthThrowsException(): void - { - $accountAccessAuth = $this->hydrator->hydrate(AccountAccessAuth::VALID_DATA); - $table = new AccountAccessAuthTable(new MockQueryFailed(), $this->hydrator); - - $this->expectException(DuplicateEntryException::class); - - $table->insert($accountAccessAuth); - } - - public function testCanUpdateAccountAccessAuth(): void - { - $accountAccessAuth = $this->hydrator->hydrate(AccountAccessAuth::VALID_DATA); - - $result = $this->table->update($accountAccessAuth); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testUpdateAccountAccessAuthThrowsException(): void - { - $accountAccessAuth = $this->hydrator->hydrate(AccountAccessAuth::VALID_DATA); - $table = new AccountAccessAuthTable(new MockQueryFailed(), $this->hydrator); - - $this->expectException(InvalidArgumentException::class); - - $table->update($accountAccessAuth); - } - - public function testCanDeleteById(): void - { - $result = $this->table->deleteById(AccountAccessAuth::ID); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testDeleteAccountThrowsException(): void - { - $table = new AccountAccessAuthTable(new MockQueryFailed(), $this->hydrator); - - $this->expectException(InvalidArgumentException::class); - - $table->deleteById(AccountAccessAuth::ID); - } - - public function testCanFindById(): void - { - $result = $this->table->findById(AccountAccessAuth::ID); - - $this->assertInstanceOf(AccountAccessAuthInterface::class, $result); - $this->assertSame(AccountAccessAuth::ID, $result->id); - } - - public function testFindByIdIsEmpty(): void - { - $table = new AccountAccessAuthTable(new MockQueryFailed(), $this->hydrator); - - $result = $table->findById(AccountAccessAuth::ID); - - $this->assertNull($result); - } - - public function testCanFindByUserId(): void - { - /** @var AccountAccessAuthCollectionInterface $result */ - $result = $this->table->findByAccountId(AccountAccessAuth::USER_ID); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertArrayHasKey(0, $result); - $this->assertSame(AccountAccessAuth::USER_ID, $result[0]->accountId); - } - - public function testFindByUserIdIsEmpty(): void - { - $table = new AccountAccessAuthTable(new MockQueryFailed(), $this->hydrator); - - $result = $table->findByAccountId(AccountAccessAuth::USER_ID); - - $this->assertInstanceOf(AccountAccessAuthCollection::class, $result); - $this->assertEmpty($result); - } - - public function testCanFindByLabel(): void - { - /** @var AccountAccessAuthCollectionInterface $result */ - $result = $this->table->findByLabel(AccountAccessAuth::LABEL); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertArrayHasKey(0, $result); - $this->assertSame(AccountAccessAuth::LABEL, $result[0]->label); - } - - public function testFindByLabelIsEmpty(): void - { - $table = new AccountAccessAuthTable(new MockQueryFailed(), $this->hydrator); - - $result = $table->findByLabel(AccountAccessAuth::LABEL); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertEmpty($result); - } - - public function testCanFindByRefreshToken(): void - { - /** @var AccountAccessAuthInterface $result */ - $result = $this->table->findByRefreshToken(AccountAccessAuth::REFRESH_TOKEN); - - $this->assertInstanceOf(AccountAccessAuthInterface::class, $result); - $this->assertSame(AccountAccessAuth::REFRESH_TOKEN, $result->refreshToken); - } - - public function testFindByRefreshTokenIsEmpty(): void - { - $table = new AccountAccessAuthTable(new MockQueryFailed(), $this->hydrator); - - $result = $table->findByRefreshToken(AccountAccessAuth::REFRESH_TOKEN); - - $this->assertNull($result); - } - - public function testCanFindByUserAgent(): void - { - /** @var AccountAccessAuthCollectionInterface $result */ - $result = $this->table->findByUserAgent(AccountAccessAuth::USER_AGENT); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertArrayHasKey(0, $result); - $this->assertSame(AccountAccessAuth::USER_AGENT, $result[0]->userAgent); - } - - public function testFindByUserAgentIsEmpty(): void - { - $table = new AccountAccessAuthTable(new MockQueryFailed(), $this->hydrator); - - $result = $table->findByUserAgent(AccountAccessAuth::USER_AGENT); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertEmpty($result); - } - - public function testCanFindByClientIdentHash(): void - { - /** @var AccountAccessAuthInterface $result */ - $result = $this->table->findByClientIdentHash(AccountAccessAuth::CLIENT_IDENT_HASH); - - $this->assertInstanceOf(AccountAccessAuthInterface::class, $result); - $this->assertSame(AccountAccessAuth::CLIENT_IDENT_HASH, $result->clientIdentHash); - } - - public function testFindByClientIdentHashIsEmpty(): void - { - $table = new AccountAccessAuthTable(new MockQueryFailed(), $this->hydrator); - - $result = $table->findByClientIdentHash(AccountAccessAuth::CLIENT_IDENT_HASH); - - $this->assertNull($result); - } - - public function testCanFindAll(): void - { - /** @var AccountAccessAuthCollectionInterface $result */ - $result = $this->table->findAll(); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertArrayHasKey(0, $result); - $this->assertSame(AccountAccessAuth::ID, $result[0]->id); - } - - public function testFindAllIsEmpty(): void - { - $table = new AccountAccessAuthTable(new MockQueryFailed(), $this->hydrator); - - $result = $table->findAll(); - - $this->assertInstanceOf(AccountAccessAuthCollectionInterface::class, $result); - $this->assertEmpty($result); - } -} diff --git a/tests/UnitTest/AppTest/Table/AccountTableTest.php b/tests/UnitTest/AppTest/Table/AccountTableTest.php deleted file mode 100644 index 30d74c66..00000000 --- a/tests/UnitTest/AppTest/Table/AccountTableTest.php +++ /dev/null @@ -1,189 +0,0 @@ -uuidFactory = new UuidFactory(); - $this->hydrator = new AccountHydrator($this->uuidFactory); - $this->table = new AccountTable($query, $this->hydrator); - } - - public function testCanGetTableName(): void - { - $this->assertSame('Account', $this->table->getTableName()); - } - - public function testCanInsertAccount(): void - { - $account = $this->hydrator->hydrate(Account::VALID_DATA); - - $result = $this->table->insert($account); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testInsertAccountThrowsException(): void - { - $table = new AccountTable(new MockQueryFailed(), $this->hydrator); - - $account = $this->hydrator->hydrate(Account::VALID_DATA); - - $this->expectException(DuplicateEntryException::class); - - $table->insert($account); - } - - public function testCanUpdateAccount(): void - { - $account = $this->hydrator->hydrate(Account::VALID_DATA); - - $result = $this->table->update($account); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testUpdateAccountThrowsException(): void - { - $table = new AccountTable(new MockQueryFailed(), $this->hydrator); - $account = $this->hydrator->hydrate(Account::VALID_DATA); - - $this->expectException(InvalidArgumentException::class); - - $table->update($account); - } - - public function testCanDeleteById(): void - { - $result = $this->table->deleteById(Account::ID); - - $this->assertIsBool($result); - $this->assertTrue($result); - } - - public function testDeleteAccountThrowsException(): void - { - $table = new AccountTable(new MockQueryFailed(), $this->hydrator); - - $this->expectException(InvalidArgumentException::class); - - $table->deleteById(Account::ID); - } - - /** - * @throws Exception - */ - public function testCanFindById(): void - { - $account = $this->table->findById(Account::ID); - - $this->assertInstanceOf(AccountInterface::class, $account); - $this->assertSame(Account::VALID_DATA, $this->hydrator->extract($account)); - } - - /** - * @throws Exception - */ - public function testFindByIdIsEmpty(): void - { - $result = $this->table->findById(Account::ID_INVALID); - - $this->assertNull($result); - } - - /** - * @throws Exception - */ - public function testCanFindByUuid(): void - { - $uuid = $this->uuidFactory->fromString(Account::UUID); - $account = $this->table->findByUuid($uuid); - - $this->assertInstanceOf(AccountInterface::class, $account); - $this->assertSame(Account::VALID_DATA, $this->hydrator->extract($account)); - } - - /** - * @throws Exception - */ - public function testFindByUuidIsEmpty(): void - { - $uuid = $this->uuidFactory->fromString(Account::UUID_INVALID); - $result = $this->table->findByUuid($uuid); - - $this->assertNull($result); - } - - public function testCanFindByName(): void - { - $account = $this->table->findByName(Account::NAME); - - $this->assertInstanceOf(AccountInterface::class, $account); - $this->assertSame(Account::VALID_DATA, $this->hydrator->extract($account)); - } - - public function testFindByNameIsEmpty(): void - { - $result = $this->table->findByName(Account::NAME_INVALID); - - $this->assertNull($result); - } - - public function testCanFindByEmail(): void - { - $account = $this->table->findByEmail(new Email(Account::EMAIL)); - - $this->assertInstanceOf(AccountInterface::class, $account); - $this->assertSame(Account::VALID_DATA, $this->hydrator->extract($account)); - } - - public function testFindByEmailIsEmpty(): void - { - $result = $this->table->findByEmail(new Email(Account::EMAIL_INVALID)); - - $this->assertNull($result); - } - - public function testCanFindAllAccount(): void - { - $accounts = $this->table->findAll(); - - $this->assertInstanceOf(AccountCollectionInterface::class, $accounts); - $this->assertSame([0 => Account::VALID_DATA], $this->hydrator->extractCollection($accounts)); - } - - public function testFindAllAccountIsEmpty(): void - { - $table = new AccountTable(new MockQueryFailed(), $this->hydrator); - - $result = $table->findAll(); - - $this->assertInstanceOf(AccountCollectionInterface::class, $result); - $this->assertEmpty($result); - } -} diff --git a/tests/UnitTest/GameTest/.gitkeep b/tests/UnitTest/GameTest/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/UnitTest/Mock/Constants/Account.php b/tests/UnitTest/Mock/Constants/Account.php deleted file mode 100644 index e3242a7a..00000000 --- a/tests/UnitTest/Mock/Constants/Account.php +++ /dev/null @@ -1,60 +0,0 @@ - self::ID, - 'uuid' => self::UUID, - 'name' => self::NAME, - 'password' => self::PASSWORD, - 'email' => self::EMAIL, - 'registeredAt' => self::REGISTERED, - 'lastActionAt' => self::LAST_ACTION, - ]; - - public const array INVALID_DATA - = [ - 'id' => self::ID_INVALID, - 'uuid' => self::UUID_INVALID, - 'name' => self::NAME_INVALID, - 'password' => self::PASSWORD_INVALID, - 'email' => self::EMAIL_INVALID, - 'registeredAt' => self::REGISTERED, - 'lastActionAt' => self::LAST_ACTION, - ]; -} diff --git a/tests/UnitTest/Mock/Constants/AccountAccessAuth.php b/tests/UnitTest/Mock/Constants/AccountAccessAuth.php deleted file mode 100644 index 5bfa113b..00000000 --- a/tests/UnitTest/Mock/Constants/AccountAccessAuth.php +++ /dev/null @@ -1,54 +0,0 @@ - self::ID, - 'accountId' => self::USER_ID, - 'label' => self::LABEL, - 'refreshToken' => self::REFRESH_TOKEN, - 'userAgent' => self::USER_AGENT, - 'clientIdentHash' => self::CLIENT_IDENT_HASH, - 'createdAt' => self::CREATED_AT, - ]; - - public const array INVALID_DATA - = [ - 'id' => self::ID_INVALID, - 'accountId' => self::USER_ID_INVALID, - 'label' => self::LABEL_INVALID, - 'refreshToken' => self::REFRESH_TOKEN_INVALID, - 'userAgent' => self::USER_AGENT_INVALID, - 'clientIdentHash' => self::CLIENT_IDENT_HASH_INVALID, - 'createdAt' => self::CREATED_AT, - ]; -} diff --git a/tests/UnitTest/Mock/Constants/Token.php b/tests/UnitTest/Mock/Constants/Token.php deleted file mode 100644 index 3924a447..00000000 --- a/tests/UnitTest/Mock/Constants/Token.php +++ /dev/null @@ -1,21 +0,0 @@ - 'token secret', - 'algorithmus' => 'HS512', - 'duration' => 60 * 60 * 24 * 7 * 12, - 'iss' => 'Issuer of the token', - 'aud' => 'recipients of the token', - ]; - } -} diff --git a/tests/UnitTest/Mock/Database/MockDeleteFailed.php b/tests/UnitTest/Mock/Database/MockDeleteFailed.php deleted file mode 100644 index e72df7a3..00000000 --- a/tests/UnitTest/Mock/Database/MockDeleteFailed.php +++ /dev/null @@ -1,24 +0,0 @@ -handle($this->statements['DELETE FROM'], $this->statements['WHERE'], $this->parameters['WHERE']); - } - - private function handle(string $table, array $where, array $value): bool - { - return false; - } -} diff --git a/tests/UnitTest/Mock/Database/MockInsert.php b/tests/UnitTest/Mock/Database/MockInsert.php deleted file mode 100644 index c3e9b2ae..00000000 --- a/tests/UnitTest/Mock/Database/MockInsert.php +++ /dev/null @@ -1,27 +0,0 @@ -handle($this->statements['INSERT INTO'], $this->statements['VALUES']); - } - - private function handle(string $table, array $values): bool - { - return match($table) { - 'Account', 'AccountAccessAuth' => true, - default => false, - }; - } -} diff --git a/tests/UnitTest/Mock/Database/MockInsertFailed.php b/tests/UnitTest/Mock/Database/MockInsertFailed.php deleted file mode 100644 index f64f2fea..00000000 --- a/tests/UnitTest/Mock/Database/MockInsertFailed.php +++ /dev/null @@ -1,25 +0,0 @@ -handle($this->statements['INSERT INTO'], $this->statements['VALUES']); - } - - private function handle(string $table, array $values): bool - { - throw new PDOException(); - } -} diff --git a/tests/UnitTest/Mock/Database/MockSelect.php b/tests/UnitTest/Mock/Database/MockSelect.php deleted file mode 100644 index 62794cc8..00000000 --- a/tests/UnitTest/Mock/Database/MockSelect.php +++ /dev/null @@ -1,96 +0,0 @@ -statements)) { - return $this->handle($this->statements['FROM'], $this->statements['WHERE'], $this->parameters['WHERE']); - } - - return []; - } - - public function fetchAll($index = '', $selectOnly = ''): false|array - { - return match ($this->getFromTable()) { - 'Account' => [0 => Account::VALID_DATA], - 'AccountAccessAuth' => [0 => AccountAccessAuth::VALID_DATA], - default => false - }; - } - - private function handle(string $from, array $where, array $params): false|array - { - return match ($from) { - 'Account' => $this->handleAccount($where, $params), - 'AccountAccessAuth' => $this->handleAccountAccessAuth($where, $params), - default => false - }; - } - - private function handleAccount(array $where, array $params): false|array - { - if ($where[0][1] === 'id = ?' && $params[0] === Account::ID) { - return Account::VALID_DATA; - } - - if ($where[0][1] === 'uuid = ?' && $params[0] === Account::UUID) { - return Account::VALID_DATA; - } - - if ($where[0][1] === 'name = ?' && $params[0] === Account::NAME) { - return Account::VALID_DATA; - } - - if ($where[0][1] === 'email = ?' && $params[0] === Account::EMAIL) { - return Account::VALID_DATA; - } - - return false; - } - - private function handleAccountAccessAuth(array $where, array $params): false|array - { - if ($where[0][1] === 'id = ?' && $params[0] === AccountAccessAuth::ID) { - return AccountAccessAuth::VALID_DATA; - } - - if ($where[0][1] === 'userId = ?' && $params[0] === AccountAccessAuth::USER_ID) { - return [0 => AccountAccessAuth::VALID_DATA]; - } - - if ($where[0][1] === 'label = ?' && $params[0] === AccountAccessAuth::LABEL) { - return [0 => AccountAccessAuth::VALID_DATA]; - } - - if ($where[0][1] === 'refreshToken = ?' && $params[0] === AccountAccessAuth::REFRESH_TOKEN) { - return AccountAccessAuth::VALID_DATA; - } - - if ($where[0][1] === 'userAgent = ?' && $params[0] === AccountAccessAuth::USER_AGENT) { - return [0 => AccountAccessAuth::VALID_DATA]; - } - - if ($where[0][1] === 'clientIdentHash = ?' && $params[0] === AccountAccessAuth::CLIENT_IDENT_HASH) { - return AccountAccessAuth::VALID_DATA; - } - - return false; - } -} diff --git a/tests/UnitTest/Mock/Database/MockSelectFailed.php b/tests/UnitTest/Mock/Database/MockSelectFailed.php deleted file mode 100644 index 860c6e17..00000000 --- a/tests/UnitTest/Mock/Database/MockSelectFailed.php +++ /dev/null @@ -1,36 +0,0 @@ -statements)) { - return $this->handle($this->statements['FROM'], $this->statements['WHERE'], $this->parameters['WHERE']); - } - - return []; - } - - public function fetchAll($index = '', $selectOnly = ''): false|array - { - return false; - } - - private function handle(string $from, array $where, array $params): false|array - { - return false; - } -} diff --git a/tests/UnitTest/Mock/Database/MockUpdate.php b/tests/UnitTest/Mock/Database/MockUpdate.php deleted file mode 100644 index 0f13c7ef..00000000 --- a/tests/UnitTest/Mock/Database/MockUpdate.php +++ /dev/null @@ -1,22 +0,0 @@ -statements['UPDATE']) { - 'Account', 'AccountAccessAuth' => true, - default => false - }; - } -} diff --git a/tests/UnitTest/Mock/Database/MockUpdateFailed.php b/tests/UnitTest/Mock/Database/MockUpdateFailed.php deleted file mode 100644 index b755be43..00000000 --- a/tests/UnitTest/Mock/Database/MockUpdateFailed.php +++ /dev/null @@ -1,19 +0,0 @@ -getAttribute(AccountInterface::AUTHENTICATED); - $response = new MockResponse(); - - if ($account instanceof AccountInterface) { - return $response->withHeader('Authorization', 'true'); - } - - return $response; - } -} diff --git a/tests/UnitTest/Mock/MockResponse.php b/tests/UnitTest/Mock/MockResponse.php deleted file mode 100644 index 3c6aaa10..00000000 --- a/tests/UnitTest/Mock/MockResponse.php +++ /dev/null @@ -1,84 +0,0 @@ -headers[$name] ?? ''; - } - - public function withHeader($name, $value): MessageInterface - { - $header = clone $this; - $header->headers[$name] = $value; - return $header; - } - - public function getProtocolVersion(): string - { - // TODO: Implement getProtocolVersion() method. - } - - public function withProtocolVersion(string $version): MessageInterface - { - // TODO: Implement withProtocolVersion() method. - } - - public function getHeaders(): array - { - // TODO: Implement getHeaders() method. - } - - public function hasHeader(string $name): bool - { - // TODO: Implement hasHeader() method. - } - - public function getHeader(string $name): array - { - // TODO: Implement getHeader() method. - } - - public function withAddedHeader(string $name, $value): MessageInterface - { - // TODO: Implement withAddedHeader() method. - } - - public function withoutHeader(string $name): MessageInterface - { - // TODO: Implement withoutHeader() method. - } - - public function getBody(): StreamInterface - { - // TODO: Implement getBody() method. - } - - public function withBody(StreamInterface $body): MessageInterface - { - // TODO: Implement withBody() method. - } - - public function getStatusCode(): int - { - // TODO: Implement getStatusCode() method. - } - - public function withStatus(int $code, string $reasonPhrase = ''): ResponseInterface - { - // TODO: Implement withStatus() method. - } - - public function getReasonPhrase(): string - { - // TODO: Implement getReasonPhrase() method. - } -} diff --git a/tests/UnitTest/Mock/MockServerRequest.php b/tests/UnitTest/Mock/MockServerRequest.php deleted file mode 100644 index 0f3c4122..00000000 --- a/tests/UnitTest/Mock/MockServerRequest.php +++ /dev/null @@ -1,192 +0,0 @@ -headers; - } - - public function hasHeader($name): bool - { - return array_key_exists($name, $this->headers); - } - - public function getHeader($name): array - { - return array_key_exists($name, $this->headers) ? $this->headers[$name] : []; - } - - public function getHeaderLine($name): string - { - return $this->headers[$name] ?? ''; - } - - public function withHeader($name, $value): MessageInterface - { - $header = clone $this; - $header->headers[$name] = $value; - - return $header; - } - - /** - * @return StreamInterface|array - */ - public function getBody(): StreamInterface - { - return $this->body; - } - - public function getQueryParams(): array - { - return $this->queryParams; - } - - public function withQueryParams(array $query): self - { - $queryParams = clone $this; - - $queryParams->queryParams = $query; - - return $queryParams; - } - - public function getParsedBody(): object|array|null - { - return $this->body; - } - - public function withParsedBody($data): ServerRequestInterface - { - $body = clone $this; - $body->body = $data; - - return $body; - } - - public function getAttribute($name, $default = null) - { - if (array_key_exists($name, $this->attributes)) { - return $this->attributes[$name]; - } - - return $default; - } - - public function withAttribute($name, $value): MockServerRequest - { - $attributes = clone $this; - $attributes->attributes[$name] = $value; - - return $attributes; - } - - public function getProtocolVersion(): string - { - // TODO: Implement getProtocolVersion() method. - } - - public function withProtocolVersion(string $version): MessageInterface - { - // TODO: Implement withProtocolVersion() method. - } - - public function withAddedHeader(string $name, $value): MessageInterface - { - // TODO: Implement withAddedHeader() method. - } - - public function withoutHeader(string $name): MessageInterface - { - // TODO: Implement withoutHeader() method. - } - - public function withBody(StreamInterface $body): MessageInterface - { - // TODO: Implement withBody() method. - } - - public function getRequestTarget(): string - { - // TODO: Implement getRequestTarget() method. - } - - public function withRequestTarget(string $requestTarget): RequestInterface - { - // TODO: Implement withRequestTarget() method. - } - - public function getMethod(): string - { - // TODO: Implement getMethod() method. - } - - public function withMethod(string $method): RequestInterface - { - // TODO: Implement withMethod() method. - } - - public function getUri(): UriInterface - { - return new \Laminas\Diactoros\Uri('http://example.com/'); - } - - public function withUri(UriInterface $uri, bool $preserveHost = false): RequestInterface - { - // TODO: Implement withUri() method. - } - - public function getServerParams(): array - { - return []; - } - - public function getCookieParams(): array - { - // TODO: Implement getCookieParams() method. - } - - public function withCookieParams(array $cookies): ServerRequestInterface - { - // TODO: Implement withCookieParams() method. - } - - public function getUploadedFiles(): array - { - // TODO: Implement getUploadedFiles() method. - } - - public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface - { - // TODO: Implement withUploadedFiles() method. - } - - public function getAttributes(): array - { - // TODO: Implement getAttributes() method. - } - - public function withoutAttribute(string $name): ServerRequestInterface - { - // TODO: Implement withoutAttribute() method. - } -} diff --git a/tests/UnitTest/Mock/Repository/MockAccountAccessAuthRepository.php b/tests/UnitTest/Mock/Repository/MockAccountAccessAuthRepository.php deleted file mode 100644 index 8cbcaa52..00000000 --- a/tests/UnitTest/Mock/Repository/MockAccountAccessAuthRepository.php +++ /dev/null @@ -1,14 +0,0 @@ - $this->config->iss, - 'aud' => $this->config->aud, - 'iat' => $now, - 'exp' => $now + $this->config->duration, - 'uuid' => $uuid->getHex()->toString(), - ]; - - return JWT::encode($payload, $this->config->key, $this->config->algorithmus); - } -} diff --git a/tests/UnitTest/Mock/Service/MockAccessTokenServiceWithoutDuration.php b/tests/UnitTest/Mock/Service/MockAccessTokenServiceWithoutDuration.php deleted file mode 100644 index dc442e4b..00000000 --- a/tests/UnitTest/Mock/Service/MockAccessTokenServiceWithoutDuration.php +++ /dev/null @@ -1,35 +0,0 @@ - $this->config->iss, - 'aud' => $this->config->aud, - 'iat' => $now, - 'exp' => $now + $this->config->duration, - 'uuid' => $uuid->getHex()->toString(), - ]; - - return JWT::encode($payload, $this->config->key, $this->config->algorithmus); - } -} diff --git a/tests/UnitTest/Mock/Service/MockAuthenticationService.php b/tests/UnitTest/Mock/Service/MockAuthenticationService.php deleted file mode 100644 index ed819dba..00000000 --- a/tests/UnitTest/Mock/Service/MockAuthenticationService.php +++ /dev/null @@ -1,14 +0,0 @@ -accountId !== Account::ID) { - throw new DuplicateEntryException('AccountAccessAuth', $data->id); - } - - return true; - } - - public function update(AccountAccessAuthInterface $data): true - { - if ($data->id !== AccountAccessAuth::ID) { - throw new InvalidArgumentException(); - } - - return true; - } - - public function deleteById(int $id): true - { - if ($id !== AccountAccessAuth::ID) { - throw new InvalidArgumentException(); - } - - return true; - } - - public function findById(int $id): ?AccountAccessAuthInterface - { - return $id === AccountAccessAuth::ID ? $this->hydrator->hydrate(AccountAccessAuth::VALID_DATA) : null; - } - - public function findByAccountId(int $accountId): AccountAccessAuthCollectionInterface - { - return $accountId === AccountAccessAuth::USER_ID - ? $this->hydrator->hydrateCollection([0 => AccountAccessAuth::VALID_DATA]) - : $this->hydrator->hydrateCollection( - [] - ); - } - - public function findByLabel(string $label): AccountAccessAuthCollectionInterface - { - return $label === AccountAccessAuth::LABEL - ? $this->hydrator->hydrateCollection([0 => AccountAccessAuth::VALID_DATA]) - : $this->hydrator->hydrateCollection( - [] - ); - } - - public function findByRefreshToken(string $refreshToken): ?AccountAccessAuthInterface - { - return $refreshToken === AccountAccessAuth::REFRESH_TOKEN ? $this->hydrator->hydrate( - AccountAccessAuth::VALID_DATA - ) : null; - } - - public function findByUserAgent(string $userAgent): AccountAccessAuthCollectionInterface - { - return $userAgent === AccountAccessAuth::USER_AGENT - ? $this->hydrator->hydrateCollection([0 => AccountAccessAuth::VALID_DATA]) - : $this->hydrator->hydrateCollection( - [] - ); - } - - public function findByClientIdentHash(string $clientIdentHash): ?AccountAccessAuthInterface - { - return $clientIdentHash === AccountAccessAuth::CLIENT_IDENT_HASH ? $this->hydrator->hydrate( - AccountAccessAuth::VALID_DATA - ) : null; - } - - public function findAll(): AccountAccessAuthCollectionInterface - { - return $this->hydrator->hydrateCollection([0 => AccountAccessAuth::VALID_DATA]); - } -} diff --git a/tests/UnitTest/Mock/Table/MockAccountAccessAuthTableFailed.php b/tests/UnitTest/Mock/Table/MockAccountAccessAuthTableFailed.php deleted file mode 100644 index d184590d..00000000 --- a/tests/UnitTest/Mock/Table/MockAccountAccessAuthTableFailed.php +++ /dev/null @@ -1,25 +0,0 @@ -hydrator->hydrateCollection([]); - } -} diff --git a/tests/UnitTest/Mock/Table/MockAccountTable.php b/tests/UnitTest/Mock/Table/MockAccountTable.php deleted file mode 100644 index 8b9e6a63..00000000 --- a/tests/UnitTest/Mock/Table/MockAccountTable.php +++ /dev/null @@ -1,84 +0,0 @@ -id !== Account::ID) { - throw new DuplicateEntryException('Account', $data->id); - } - - return true; - } - - public function update(AccountInterface $data): true - { - if ($data->id !== Account::ID) { - throw new InvalidArgumentException(); - } - - return true; - } - - public function deleteById(int $id): true - { - if ($id !== Account::ID) { - throw new InvalidArgumentException(); - } - - return true; - } - - public function findById(int $id): ?AccountInterface - { - return $id === Account::ID ? $this->hydrator->hydrate(Account::VALID_DATA) : null; - } - - public function findByUuid(UuidInterface $uuid): ?AccountInterface - { - return $uuid->getHex()->toString() === Account::UUID ? $this->hydrator->hydrate(Account::VALID_DATA) : null; - } - - public function findByName(string $name): ?AccountInterface - { - return $name === Account::NAME ? $this->hydrator->hydrate(Account::VALID_DATA) : null; - } - - public function findByEmail(Email $email): ?AccountInterface - { - return $email->toString() === Account::EMAIL ? $this->hydrator->hydrate(Account::VALID_DATA) : null; - } - - public function findAll(): AccountCollection - { - return $this->hydrator->hydrateCollection([Account::VALID_DATA]); - } -} diff --git a/tests/UnitTest/Mock/Table/MockAccountTableAccountAuthenticationMiddlewareInvalidToken.php b/tests/UnitTest/Mock/Table/MockAccountTableAccountAuthenticationMiddlewareInvalidToken.php deleted file mode 100644 index 8edb8323..00000000 --- a/tests/UnitTest/Mock/Table/MockAccountTableAccountAuthenticationMiddlewareInvalidToken.php +++ /dev/null @@ -1,84 +0,0 @@ -id !== Account::ID) { - throw new DuplicateEntryException('Account', $data->id); - } - - return true; - } - - public function update(AccountInterface $data): true - { - if ($data->id !== Account::ID) { - throw new InvalidArgumentException(); - } - - return true; - } - - public function deleteById(int $id): true - { - if ($id !== Account::ID) { - throw new InvalidArgumentException(); - } - - return true; - } - - public function findById(int $id): ?AccountInterface - { - return $id === Account::ID ? $this->hydrator->hydrate(Account::VALID_DATA) : null; - } - - public function findByUuid(UuidInterface $uuid): ?AccountInterface - { - return null; - } - - public function findByName(string $name): ?AccountInterface - { - return $name === Account::NAME ? $this->hydrator->hydrate(Account::VALID_DATA) : null; - } - - public function findByEmail(Email $email): ?AccountInterface - { - return $email->toString() === Account::EMAIL ? $this->hydrator->hydrate(Account::VALID_DATA) : null; - } - - public function findAll(): AccountCollection - { - return $this->hydrator->hydrateCollection([Account::VALID_DATA]); - } -} diff --git a/tests/UnitTest/Mock/Table/MockAccountTableFailed.php b/tests/UnitTest/Mock/Table/MockAccountTableFailed.php deleted file mode 100644 index c5b78ad8..00000000 --- a/tests/UnitTest/Mock/Table/MockAccountTableFailed.php +++ /dev/null @@ -1,26 +0,0 @@ -hydrator->hydrateCollection([]); - } -} diff --git a/tests/UnitTest/Mock/Validator/MockAuthenticationValidator.php b/tests/UnitTest/Mock/Validator/MockAuthenticationValidator.php deleted file mode 100644 index c5b696ff..00000000 --- a/tests/UnitTest/Mock/Validator/MockAuthenticationValidator.php +++ /dev/null @@ -1,20 +0,0 @@ - [ + 'driver' => 'sqlite', + 'host' => __DIR__ . '/../../../database/database.sqlite', + 'port' => '3306', + 'user' => 'dev', + 'password' => 'dev', + 'dbname' => 'db', + 'charset' => 'utf8mb4', + 'error' => PDO::ERRMODE_EXCEPTION, + ] +]; diff --git a/tests/config/autoload/dependencies.testing.local.php b/tests/config/autoload/dependencies.testing.local.php new file mode 100644 index 00000000..e407b412 --- /dev/null +++ b/tests/config/autoload/dependencies.testing.local.php @@ -0,0 +1,22 @@ + [ + 'aliases' => [ + PDO::class => 'database', + Envms\FluentPDO\Query::class => 'query', + Ramsey\Uuid\Uuid::class => 'uuid', + Symfony\Component\Mailer\Mailer::class => 'mailer', + Psr\Log\LoggerInterface::class => 'logger', + ], + 'invokables' => [ + ], + 'factories' => [ + 'database' => Core\Factory\DatabaseFactory::class, + 'query' => Core\Factory\QueryFactory::class, + 'uuid' => Core\Factory\UuidFactory::class, + 'mailer' => Core\Factory\MailFactory::class, + 'logger' => Test\Functional\Mock\NullLoggerFactory::class, + ], + ], +]; diff --git a/tests/config/autoload/token.testing.local.php b/tests/config/autoload/token.testing.local.php new file mode 100644 index 00000000..4e474e4e --- /dev/null +++ b/tests/config/autoload/token.testing.local.php @@ -0,0 +1,18 @@ + [ + 'auth' => [ + 'secret' => 'Oqaf673OLS380moI', + 'algorithmus' => 'HS512', + 'duration' => 60 * 60, + 'refresh' => 24 * 60 * 60, + ], + 'csrf' => [ + 'secret' => '09asd7fuIhfoUiashfo', + 'algorithmus' => 'HS512', + 'duration' => 60 * 60, + 'refresh' => 24 * 60 * 60, + ], + ], +]; diff --git a/tests/config/config.php b/tests/config/config.php new file mode 100644 index 00000000..59e06248 --- /dev/null +++ b/tests/config/config.php @@ -0,0 +1,24 @@ +getMergedConfig(); diff --git a/tests/config/container.php b/tests/config/container.php new file mode 100644 index 00000000..3bae2980 --- /dev/null +++ b/tests/config/container.php @@ -0,0 +1,14 @@ +pipe(ErrorHandler::class); + $app->pipe(RouteMiddleware::class); + $app->pipe(JwtAuthenticationMiddleware::class); + $app->pipe(DispatchMiddleware::class); + $app->pipe(NotFoundHandler::class); +}; diff --git a/tests/config/routes.php b/tests/config/routes.php new file mode 100644 index 00000000..09a31d37 --- /dev/null +++ b/tests/config/routes.php @@ -0,0 +1,19 @@ +get('/api/ping[/]', PingHandler::class, PingHandler::class); + + $app->get( + '/api/user/me[/]', + [ + ApiMeHandler::class, + ], + ApiMeHandler::class + ); +};