diff --git a/.claude/agents/create-migration.md b/.claude/agents/create-migration.md new file mode 100644 index 0000000..235de3b --- /dev/null +++ b/.claude/agents/create-migration.md @@ -0,0 +1,15 @@ +--- +name: create-migration +description: Generate and validate a Doctrine migration after entity changes +model: sonnet +--- + +After entity changes, generate and validate a Doctrine migration: + +1. Run `docker compose exec -T phpfpm bin/console doctrine:migrations:diff` to generate a migration +2. Read the generated migration file and verify the SQL looks correct +3. Run `docker compose exec -T phpfpm bin/console doctrine:migrations:migrate --no-interaction` +4. Run `docker compose exec -T phpfpm bin/console doctrine:schema:validate` + +Report the migration file path, the SQL it contains, and whether schema validation passed. +If schema validation fails, investigate and report the discrepancies. diff --git a/.claude/agents/pr-readiness.md b/.claude/agents/pr-readiness.md new file mode 100644 index 0000000..1b57e49 --- /dev/null +++ b/.claude/agents/pr-readiness.md @@ -0,0 +1,24 @@ +--- +name: pr-readiness +description: Run all CI-equivalent checks locally before creating a PR +model: haiku +--- + +Run the following checks in sequence inside Docker and report results for each. +Stop early if a critical check fails. + +## Checks + +1. **Composer validate**: `docker compose exec -T phpfpm composer validate --strict` +2. **Composer normalize**: `docker compose exec -T phpfpm composer normalize --dry-run` +3. **PHP coding standards**: `docker compose exec -T phpfpm composer coding-standards-check` +4. **PHPStan**: `docker compose exec -T phpfpm vendor/bin/phpstan analyse --no-progress` +5. **PHPUnit tests**: `docker compose exec -T phpfpm composer tests` +6. **Twig coding standards**: `docker compose exec -T phpfpm vendor/bin/twig-cs-fixer lint templates/` +7. **JS coding standards**: `docker compose run --rm -T node yarn coding-standards-check` +8. **API spec up to date**: Run `docker compose exec -T phpfpm composer update-api-spec`, then check `git diff --exit-code public/api-spec-v1.*` +9. **CHANGELOG updated**: Verify CHANGELOG.md has changes compared to the base branch (`git diff develop -- CHANGELOG.md`) + +## Output + +Report a summary table with columns: Check Name, Status (pass/fail), and error output for failures. diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..f1b6bf0 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,160 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "env": { + "COMPOSE_USER": "deploy" + }, + "permissions": { + "allow": [ + "Bash(cat:*)", + "Bash(diff:*)", + "Bash(echo:*)", + "Bash(find:*)", + "Bash(gh:*)", + "Bash(git:*)", + "Bash(grep:*)", + "Bash(head:*)", + "Bash(ls:*)", + "Bash(pwd)", + "Bash(tail:*)", + "Bash(task:*)", + "Bash(tree:*)", + "Bash(wc:*)", + "Bash(which:*)", + "Bash(docker compose exec:*)", + "Bash(docker compose run:*)", + "Bash(docker compose up:*)", + "Bash(docker compose ps:*)", + "Bash(docker compose logs:*)", + "Bash(docker compose top:*)", + "Bash(docker compose config:*)", + "Bash(docker compose pull:*)", + "Bash(docker compose images:*)", + "Bash(docker network:*)" + ], + "deny": [ + "Bash(rm -rf:*)", + "Bash(gh issue delete:*)", + "Bash(gh release delete:*)", + "Bash(gh repo delete:*)", + "Bash(gh label delete:*)", + "Read(./.env.local)", + "Read(./.env.local.*)", + "Read(./config/secrets/*)" + ], + "ask": [ + "Bash(docker compose down:*)", + "Bash(docker compose stop:*)", + "Bash(docker compose rm:*)", + "Bash(docker compose restart:*)", + "Bash(gh issue create:*)", + "Bash(gh issue close:*)", + "Bash(gh issue edit:*)", + "Bash(gh issue comment:*)", + "Bash(gh pr create:*)", + "Bash(gh pr close:*)", + "Bash(gh pr merge:*)", + "Bash(gh pr edit:*)", + "Bash(gh pr comment:*)", + "Bash(gh pr review:*)", + "Bash(gh release create:*)", + "Bash(gh release edit:*)", + "Bash(gh repo create:*)", + "Bash(gh label create:*)", + "Bash(gh label edit:*)", + "Bash(git push:*)", + "Bash(git branch -d:*)", + "Bash(git branch -D:*)", + "Bash(git tag -d:*)", + "Bash(git tag -a:*)", + "Bash(git tag :*)", + "Bash(git reset:*)", + "Bash(git rebase:*)", + "Bash(git merge:*)", + "Bash(git stash drop:*)", + "Bash(git clean:*)", + "Bash(git checkout -- :*)", + "Bash(git restore:*)", + "Bash(git commit:*)" + ] + }, + "hooks": { + "SessionStart": [ + { + "matcher": "startup", + "hooks": [ + { + "type": "command", + "command": "docker compose up --detach --quiet-pull 2>/dev/null || true", + "timeout": 60, + "statusMessage": "Starting Docker services..." + } + ] + } + ], + "PreToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "case \"$CLAUDE_FILE_PATH\" in */composer.lock|*/yarn.lock|*/.env.local|*/.env.local.*) echo 'BLOCKED: Do not edit lock files or .env.local directly' >&2; exit 1 ;; esac" + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "case \"$CLAUDE_FILE_PATH\" in *.php) REL_PATH=\"${CLAUDE_FILE_PATH#$CLAUDE_PROJECT_DIR/}\"; docker compose exec -T phpfpm vendor/bin/php-cs-fixer fix --quiet \"$REL_PATH\" 2>/dev/null || true ;; esac", + "timeout": 30 + }, + { + "type": "command", + "command": "case \"$CLAUDE_FILE_PATH\" in *.php) REL_PATH=\"${CLAUDE_FILE_PATH#$CLAUDE_PROJECT_DIR/}\"; docker compose exec -T phpfpm vendor/bin/phpstan analyse --no-progress --error-format=raw \"$REL_PATH\" 2>/dev/null || true ;; esac", + "timeout": 30 + }, + { + "type": "command", + "command": "case \"$CLAUDE_FILE_PATH\" in *.twig) REL_PATH=\"${CLAUDE_FILE_PATH#$CLAUDE_PROJECT_DIR/}\"; docker compose exec -T phpfpm vendor/bin/twig-cs-fixer lint --fix \"$REL_PATH\" 2>/dev/null || true ;; esac", + "timeout": 15 + }, + { + "type": "command", + "command": "case \"$CLAUDE_FILE_PATH\" in */composer.json) docker compose exec -T phpfpm composer normalize --quiet 2>/dev/null || true ;; esac", + "timeout": 30 + }, + { + "type": "command", + "command": "case \"$CLAUDE_FILE_PATH\" in *.js|*.css|*.scss|*.yaml|*.yml|*.md) REL_PATH=\"${CLAUDE_FILE_PATH#$CLAUDE_PROJECT_DIR/}\"; docker compose run --rm -T node npx prettier --write \"$REL_PATH\" 2>/dev/null || true ;; esac", + "timeout": 15 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "docker compose exec -T phpfpm bin/console lint:container 2>/dev/null || true", + "timeout": 30, + "statusMessage": "Validating Symfony DI container..." + } + ] + } + ] + }, + "enabledPlugins": { + "php-lsp@claude-plugins-official": true, + "code-simplifier@claude-plugins-official": true, + "context7@claude-plugins-official": true, + "code-review@claude-plugins-official": true, + "security-guidance@claude-plugins-official": true, + "playwright@claude-plugins-official": true, + "feature-dev@claude-plugins-official": true, + "itkdev-skills@itkdev-marketplace": true + } +} diff --git a/.claude/skills/update-api-spec/SKILL.md b/.claude/skills/update-api-spec/SKILL.md new file mode 100644 index 0000000..54856bf --- /dev/null +++ b/.claude/skills/update-api-spec/SKILL.md @@ -0,0 +1,12 @@ +--- +name: update-api-spec +description: Regenerate and stage API spec files after API resource changes +user-invocable: true +--- + +When API resources or operations change, regenerate the OpenAPI spec files: + +1. Run `docker compose exec -T phpfpm composer update-api-spec` +2. Check `git diff public/api-spec-v1.*` for changes +3. If changed, stage the spec files with `git add public/api-spec-v1.yaml public/api-spec-v1.json` +4. Report what changed in the API spec diff --git a/.docker/data/.gitignore b/.docker/data/.gitignore index 80a9dc9..4ce1020 100644 --- a/.docker/data/.gitignore +++ b/.docker/data/.gitignore @@ -1,5 +1,5 @@ # Ignore everything in this directory * -# Except this file +# Except !.gitignore -!Readme.md +!README.md diff --git a/.docker/nginx.conf b/.docker/nginx.conf index 8fe03db..ec278a5 100644 --- a/.docker/nginx.conf +++ b/.docker/nginx.conf @@ -17,9 +17,7 @@ http { include /etc/nginx/mime.types; default_type application/octet-stream; - set_real_ip_from 172.16.0.0/16; - real_ip_recursive on; - real_ip_header X-Forwarded-For; + # Note: set_real_ip_from is set in the server block log_format main '$http_x_real_ip - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' diff --git a/.docker/templates/default.conf.template b/.docker/templates/default.conf.template index 61c4e64..36ddf04 100644 --- a/.docker/templates/default.conf.template +++ b/.docker/templates/default.conf.template @@ -6,11 +6,20 @@ server { client_max_body_size ${NGINX_MAX_BODY_SIZE}; - # This also needs to be set in the single server tag and not only in http. set_real_ip_from 172.16.0.0/16; + set_real_ip_from 192.168.39.0/24; real_ip_recursive on; real_ip_header X-Forwarded-For; + location = /cron-metrics { + # Proxy to supercronic metrics + proxy_pass http://${NGINX_CRON_METRICS}/metrics; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location / { # try to serve file directly, fallback to index.php try_files $uri /index.php$is_args$args; diff --git a/.env b/.env index 988bd7a..0fa1f32 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ COMPOSE_PROJECT_NAME=itksites COMPOSE_DOMAIN=itksites.local.itkdev.dk +ITKDEV_TEMPLATE=symfony-8 -ITKDEV_TEMPLATE=symfony-6 # In all environments, the following files are loaded if they exist, # the latter taking precedence over the former: @@ -64,5 +64,8 @@ VAULT_SECRET_ID="CHANGE_ME_IN_LOCAL_ENV" # The number of old results for each server/result-type combination APP_KEEP_RESULTS=5 -APP_LEANTIME_URI=https://leantime.itkdev.dk -APP_LEANTIME_API_KEY="CHANGE_ME_IN_LOCAL_ENV" + +###> economics ### +APP_ECONOMICS_URI=https://economics.itkdev.dk +APP_ECONOMICS_API_KEY=changeme +###< economics ### diff --git a/.github/workflows/api-spec.yaml b/.github/workflows/api-spec.yaml new file mode 100644 index 0000000..4b39553 --- /dev/null +++ b/.github/workflows/api-spec.yaml @@ -0,0 +1,143 @@ +name: API Specification + +on: + pull_request: + paths: + - "src/**/*.php" + - "config/**" + - "composer.json" + - "composer.lock" + - "public/api-spec-v1.yaml" + - "public/api-spec-v1.json" + - "docker-compose.yml" + +env: + COMPOSE_USER: runner + +jobs: + api-spec-export: + name: Ensure API specification is up to date + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Create docker network + run: docker network create frontend + + # https://taskfile.dev/installation/#github-actions + - uses: go-task/setup-task@v1 + + - name: Export API specification + run: | + task site:update + task api:spec:export + + - name: Check for uncommitted changes + id: git-diff-spec + continue-on-error: true + run: | + git diff --diff-filter=ACMRT --exit-code public/api-spec-v1.yaml public/api-spec-v1.json + + - name: Comment PR if spec is outdated + if: steps.git-diff-spec.outcome == 'failure' + env: + GH_TOKEN: ${{ github.token }} + run: | + gh pr comment ${{ github.event.pull_request.number }} \ + --body "$(cat <<'EOF' + ## API specification not up to date + + The committed API specification files do not match the exported output. + + Please run the following command, then commit and push the changes: + + ```shell + docker compose exec phpfpm composer update-api-spec + ``` + EOF + )" \ + --create-if-none --edit-last + + - name: Fail if spec is outdated + if: steps.git-diff-spec.outcome == 'failure' + run: exit 1 + + api-spec-breaking-changes: + name: Detect breaking changes in API specification + runs-on: ubuntu-latest + needs: [api-spec-export] + permissions: + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Fetch base branch for comparison + run: git fetch --depth=1 origin ${{ github.base_ref }} + + - name: Detect breaking changes + id: breaking + continue-on-error: true + uses: oasdiff/oasdiff-action/breaking@main + with: + base: "origin/${{ github.base_ref }}:public/api-spec-v1.yaml" + revision: "public/api-spec-v1.yaml" + fail-on: ERR + + - name: Generate changelog + id: changelog + continue-on-error: true + uses: oasdiff/oasdiff-action/changelog@main + with: + base: "origin/${{ github.base_ref }}:public/api-spec-v1.yaml" + revision: "public/api-spec-v1.yaml" + format: markdown + output-to-file: changelog.md + + - name: Comment PR - no changes + if: steps.breaking.outcome == 'success' && hashFiles('changelog.md') == '' + env: + GH_TOKEN: ${{ github.token }} + run: | + gh pr comment ${{ github.event.pull_request.number }} \ + --body "## API Specification + + No changes detected in API specification." \ + --create-if-none --edit-last + + - name: Comment PR - non-breaking changes + if: steps.breaking.outcome == 'success' && hashFiles('changelog.md') != '' + env: + GH_TOKEN: ${{ github.token }} + run: | + { + echo "## API Specification - Non-breaking changes" + echo "" + cat changelog.md + } > comment.md + gh pr comment ${{ github.event.pull_request.number }} \ + --body-file comment.md \ + --create-if-none --edit-last + + - name: Comment PR - breaking changes + if: steps.breaking.outcome == 'failure' + env: + GH_TOKEN: ${{ github.token }} + run: | + { + echo "## API Specification - Breaking changes detected" + echo "" + if [ -s changelog.md ]; then + cat changelog.md + else + echo "The breaking changes action detected incompatible changes. Review the action logs for details." + fi + } > comment.md + gh pr comment ${{ github.event.pull_request.number }} \ + --body-file comment.md \ + --create-if-none --edit-last + + - name: Fail if breaking changes detected + if: steps.breaking.outcome == 'failure' + run: exit 1 diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml index 9f5fead..a646b05 100644 --- a/.github/workflows/changelog.yaml +++ b/.github/workflows/changelog.yaml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 2 diff --git a/.github/workflows/composer.yaml b/.github/workflows/composer.yaml index 314a11b..da473c1 100644 --- a/.github/workflows/composer.yaml +++ b/.github/workflows/composer.yaml @@ -26,20 +26,25 @@ name: Composer env: - COMPOSE_USER: root + COMPOSE_USER: runner on: pull_request: + paths: &paths + - "composer.json" + - "composer.lock" + - "docker-compose.yml" push: branches: - main - develop + paths: *paths jobs: composer-validate: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Create docker network run: | @@ -50,10 +55,8 @@ jobs: composer-normalized: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Create docker network run: | @@ -65,10 +68,8 @@ jobs: composer-audit: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Create docker network run: | diff --git a/.github/workflows/github_build_release.yml b/.github/workflows/github_build_release.yml index b26cea6..ba36672 100644 --- a/.github/workflows/github_build_release.yml +++ b/.github/workflows/github_build_release.yml @@ -16,7 +16,7 @@ jobs: APP_ENV: prod steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Composer install run: | diff --git a/.github/workflows/javascript.yaml b/.github/workflows/javascript.yaml index 15f14b6..be1811b 100644 --- a/.github/workflows/javascript.yaml +++ b/.github/workflows/javascript.yaml @@ -15,21 +15,25 @@ name: JavaScript on: pull_request: + paths: &paths + - "assets/**/*.js" + - "docker-compose.yml" push: branches: - main - develop + paths: *paths jobs: javascript-lint: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Create docker network run: | docker network create frontend - run: | - docker compose run --rm prettier 'assets/**/*.js' --check + docker compose run --rm prettier 'assets/**/*.js' --check --no-error-on-unmatched-pattern diff --git a/.github/workflows/markdown.yaml b/.github/workflows/markdown.yaml index cf4c011..a2e1988 100644 --- a/.github/workflows/markdown.yaml +++ b/.github/workflows/markdown.yaml @@ -22,17 +22,20 @@ name: Markdown on: pull_request: + paths: &paths + - "**/*.md" push: branches: - main - develop + paths: *paths jobs: markdown-lint: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Create docker network run: | diff --git a/.github/workflows/php.yaml b/.github/workflows/php.yaml index d8942c7..2b958dc 100644 --- a/.github/workflows/php.yaml +++ b/.github/workflows/php.yaml @@ -34,21 +34,27 @@ name: Symfony PHP env: - COMPOSE_USER: root + COMPOSE_USER: runner on: pull_request: + paths: &paths + - "**/*.php" + - "composer.json" + - "composer.lock" + - "docker-compose.yml" push: branches: - main - develop + paths: *paths jobs: coding-standards: name: PHP - Check Coding Standards runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Create docker network run: | diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 5f6e255..07a7f6b 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -1,8 +1,6 @@ -name: PR Review - +name: Review env: - COMPOSE_USER: root - + COMPOSE_USER: runner on: pull_request jobs: @@ -10,86 +8,120 @@ jobs: name: Test suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5 - - name: Install Task - uses: go-task/setup-task@v1 + - name: Install Task + uses: go-task/setup-task@v1 - - name: Setup docker network - run: | - docker network create frontend + - name: Setup docker network + run: | + docker network create frontend - - name: Install Site - run: | - task site:install + - name: Install Site + run: | + task site:install - - name: Test suite - run: | - task test:setup - task test + - name: Test suite + run: | + task test:setup + task test - - name: Upload coverage to Codecov test - uses: codecov/codecov-action@v5 - with: - files: ./coverage/unit.xml - flags: unittests - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + - name: Upload coverage to Codecov test + uses: codecov/codecov-action@v5 + with: + files: ./coverage/unit.xml + flags: unittests + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - phpstan: - name: PHPStan + validate-doctrine-schema: runs-on: ubuntu-latest + name: Validate Doctrine Schema steps: - - uses: actions/checkout@v5 + - name: Checkout + uses: actions/checkout@v6 - - name: Install Task - uses: go-task/setup-task@v1 + - name: Create docker network + run: docker network create frontend - - name: Start docker compose setup + - name: Install and validate run: | - docker network create frontend - task compose -- up --detach + docker compose up --detach + docker compose exec phpfpm composer install --no-interaction + docker compose exec phpfpm bin/console doctrine:migrations:migrate --no-interaction + docker compose exec phpfpm bin/console messenger:setup-transports failed + docker compose exec phpfpm bin/console doctrine:schema:validate - - name: Install Dependencies - run: | - task composer -- install + phpstan: + runs-on: ubuntu-latest + name: PHPStan + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Create docker network + run: docker network create frontend - name: Run PHPStan run: | - task code-analysis:phpstan + docker compose run --rm phpfpm composer install --no-interaction + docker compose run --rm phpfpm vendor/bin/phpstan analyse - fixtures: - name: Load fixtures + phpunit: runs-on: ubuntu-latest + name: PHP Unit tests steps: - - uses: actions/checkout@v5 + - name: Checkout + uses: actions/checkout@v6 - - name: Install Task - uses: go-task/setup-task@v1 + - name: Create docker network + run: docker network create frontend - - name: Setup docker network + - name: Run tests with coverage run: | - docker network create frontend + docker compose up --detach + docker compose exec -e XDEBUG_MODE=coverage phpfpm composer install --no-interaction + docker compose exec -e XDEBUG_MODE=coverage phpfpm bin/console --env=test doctrine:database:drop --if-exists --force --quiet + docker compose exec -e XDEBUG_MODE=coverage phpfpm bin/console --env=test doctrine:database:create --no-interaction --if-not-exists --quiet + docker compose exec -e XDEBUG_MODE=coverage phpfpm bin/console --env=test doctrine:migrations:migrate --no-interaction --quiet + docker compose exec -e XDEBUG_MODE=coverage phpfpm vendor/bin/phpunit --coverage-clover=coverage/unit.xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage/unit.xml + fail_ci_if_error: true + flags: unittests - - name: Install Site - run: | - task site:install + fixtures: + runs-on: ubuntu-latest + name: Load fixtures + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Create docker network + run: docker network create frontend - name: Load fixtures run: | - task fixtures:load --yes + docker compose up --detach + docker compose exec phpfpm composer install --no-interaction + docker compose exec phpfpm bin/console doctrine:migrations:migrate --no-interaction + docker compose exec phpfpm composer fixtures build-assets: - name: Build assets runs-on: ubuntu-latest + name: Build assets steps: - name: Checkout - uses: actions/checkout@v5 - with: - fetch-depth: 2 + uses: actions/checkout@v6 + + - name: Create docker network + run: docker network create frontend - name: Build assets run: | - docker network create frontend docker compose run --rm node yarn install docker compose run --rm node yarn build diff --git a/.github/workflows/styles.yaml b/.github/workflows/styles.yaml index 985e56f..be3c669 100644 --- a/.github/workflows/styles.yaml +++ b/.github/workflows/styles.yaml @@ -15,21 +15,26 @@ name: Styles on: pull_request: + paths: &paths + - "assets/**/*.css" + - "assets/**/*.scss" + - "docker-compose.yml" push: branches: - main - develop + paths: *paths jobs: styles-lint: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Create docker network run: | docker network create frontend - run: | - docker compose run --rm prettier 'assets/**/*.{css,scss}' --check + docker compose run --rm prettier 'assets/**/*.{css,scss}' --check --no-error-on-unmatched-pattern diff --git a/.github/workflows/twig.yaml b/.github/workflows/twig.yaml index 680a42d..1d7cd59 100644 --- a/.github/workflows/twig.yaml +++ b/.github/workflows/twig.yaml @@ -24,21 +24,27 @@ name: Twig env: - COMPOSE_USER: root + COMPOSE_USER: runner on: pull_request: + paths: &paths + - "**/*.twig" + - "composer.json" + - "composer.lock" + - "docker-compose.yml" push: branches: - main - develop + paths: *paths jobs: twig-lint: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Create docker network run: | diff --git a/.github/workflows/yaml.yaml b/.github/workflows/yaml.yaml index 3115e31..4620f34 100644 --- a/.github/workflows/yaml.yaml +++ b/.github/workflows/yaml.yaml @@ -14,7 +14,7 @@ ### #### Symfony YAML ### ### Symfony's YAML config files use 4 spaces for indentation and single quotes. -### Therefore, we use a [Prettier configuration +### Therefore we use a [Prettier configuration ### file](https://prettier.io/docs/configuration), `.prettierrc.yaml`, to make ### Prettier format YAML files in the `config/` folder like Symfony expects. @@ -22,20 +22,24 @@ name: YAML on: pull_request: + paths: &paths + - "**/*.yml" + - "**/*.yaml" push: branches: - main - develop + paths: *paths jobs: yaml-lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Create docker network run: | docker network create frontend - run: | - docker compose run --rm prettier '**/*.{yml,yaml}' --check + docker compose run --rm prettier '**/*.{yml,yaml}' --check --no-error-on-unmatched-pattern diff --git a/.gitignore b/.gitignore index 4e7ed3e..3638608 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,7 @@ phpstan.neon /assets/vendor/ ###< symfony/asset-mapper ### coverage + +.twig-cs-fixer.cache +.playwright-mcp + diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..f0c27d6 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "context7": { + "command": "npx", + "args": ["-y", "@upstreamapi/context7-mcp@latest"] + } + } +} diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index c23b927..bd77d84 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -10,6 +10,9 @@ // … that are not ignored by VCS $finder->ignoreVCSIgnored(true); +// Exclude generated files +$finder->notPath('config/reference.php'); + $config = new PhpCsFixer\Config(); $config->setFinder($finder); diff --git a/.prettierignore b/.prettierignore index bb35050..4ec364e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,6 @@ # API spec + +# Generated API specification files public/api-spec-v1.yaml +public/api-spec-v1.json + diff --git a/CHANGELOG.md b/CHANGELOG.md index b1bf9dd..700646f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update phpunit from 11 to 12 - Update ITK docker template - Update github actions workflows - + +## [1.10.1] - 2026-05-11 + +- [#71](https://github.com/itk-dev/devops_itksites/pull/71) + Update composer dependencies + +## [1.10.0] - 2026-04-23 + +- [#68](https://github.com/itk-dev/devops_itksites/pull/68) + 6667: Update advisories on Detailed site display + +- [#68](https://github.com/itk-dev/devops_itksites/pull/68) + 6667: Show sites affected on advisories. + +## [1.9.2] - 2026-04-07 + +- [#67](https://github.com/itk-dev/devops_itksites/pull/67) + 6654: Fix `#[AdminRoute] attribute error, add smoke tests for admin routes +- + +## [1.9.1] - 2026-04-07 + +- [#66](https://github.com/itk-dev/devops_itksites/pull/66) + 6654: Downgrade to PHP 8.4, update composer dependencies + +## [1.9.0] - 2026-04-07 + +- [#64](https://github.com/itk-dev/devops_itksites/pull/64) + 6869: Improve OpenAPI spec with descriptions, examples, and error codes +- [#63](https://github.com/itk-dev/devops_itksites/pull/63) + 6654: Upgrade to PHP 8.5, Symfony 8.0, EasyAdmin 5.x, DoctrineBundle 3.x, PHPUnit 13 +- [#62](https://github.com/itk-dev/devops_itksites/pull/62) + 6869: Add claude.md and Claude Code configuration for AI coding agents - [#58](https://github.com/itk-dev/devops_itksites/pull/58) 5002: Added export to everything @@ -138,7 +170,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.0.0] - 2022-09-15 -[Unreleased]: https://github.com/itk-dev/devops_itksites/compare/1.6.0...HEAD +[Unreleased]: https://github.com/itk-dev/devops_itksites/compare/1.10.0...HEAD +[1.10.0]: https://github.com/itk-dev/devops_itksites/compare/1.9.2...1.10.0 +[1.9.2]: https://github.com/itk-dev/devops_itksites/compare/1.9.1...1.9.2 +[1.9.1]: https://github.com/itk-dev/devops_itksites/compare/1.9.0...1.9.1 +[1.9.0]: https://github.com/itk-dev/devops_itksites/compare/1.8.9...1.9.0 [1.8.10]: https://github.com/itk-dev/devops_itksites/compare/1.8.9...1.8.10 [1.8.9]: https://github.com/itk-dev/devops_itksites/compare/1.8.8...1.8.9 [1.8.8]: https://github.com/itk-dev/devops_itksites/compare/1.8.7...1.8.8 diff --git a/README.md b/README.md index 72b2dff..fc1305f 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![Codecov](https://img.shields.io/codecov/c/github/itk-dev/devops_itksites?style=flat-square&logo=codecov)](https://codecov.io/gh/itk-dev/devops_itksites) [![GitHub last commit](https://img.shields.io/github/last-commit/itk-dev/devops_itksites?style=flat-square)](https://github.com/itk-dev/devops_itksites/commits/develop/) [![GitHub License](https://img.shields.io/github/license/itk-dev/devops_itksites?style=flat-square)](https://github.com/itk-dev/devops_itksites/blob/develop/LICENSE) +[![claude.md](https://img.shields.io/badge/%F0%9F%A4%96_claude.md-AI%20ready-8A2BE2?style=flat-square)](https://github.com/itk-dev/devops_itksites/blob/develop/claude.md) This is our internal server and site registration tool. It works in tandem with our [ITK sites server harvester](https://github.com/itk-dev/devops_itkServerHarvest). @@ -15,17 +16,17 @@ information about sites and installations running on the server. These are sent This allows us to monitor -* What is installed and running -* Which sites/domains we are hosting -* What docker images we are running -* What packages and modules we are running -* If there are known CVE's for the packages/modules -* What git repositories we are hosting +- What is installed and running +- Which sites/domains we are hosting +- What docker images we are running +- What packages and modules we are running +- If there are known CVE's for the packages/modules +- What git repositories we are hosting Additionally we can register and document -* All OpenID Connect setups -* All Services Certificates +- All OpenID Connect setups +- All Services Certificates Servers, OpenID Connect setups, Services Certificates must be created and maintained manually. All other information is kept up to date by analysing the DetectionResults. @@ -69,13 +70,18 @@ AZURE_AZ_OIDC_REDIRECT_URI=https://itksites.local.itkdev.dk/openid-connect/gener ###< itk-dev/openid-connect-bundle ### ``` +> [!NOTE] +> In the `dev` environment the main firewall security is disabled +> (`security.yaml` → `when@dev`), so authentication is not required. +> This is because the current AAK OIDC setup doesn't support `itksites.local.itkdev.dk`. + ### Fixtures There are not implemented on -* sites -* installations -* domains +- sites +- installations +- domains This is due to automated processes and scripts that listen from sites and data is therefore not relevant to have. The architecture makes it possible to delete @@ -130,3 +136,75 @@ during development to automatically rebuild assets when source files change. ```sh docker compose run --rm node yarn coding-standards-check ``` + +### 🤖 AI coding agents + +This project includes an [`claude.md`](claude.md) file that provides project +context for Claude Code. The file describes the project architecture, +technology stack, development commands, CI/CD setup, and coding conventions. + +Tool-specific configuration (permissions, hooks, plugins) lives in `.claude/` +and is not portable across tools. + +> [!NOTE] +> `agents.md` is a vendor-neutral standard supported by tools such as +> [OpenCode](https://opencode.ai/) and others. Claude Code doesn't currently support +> `agents.md`, `claude.md` should be renamed to a vendor neutral standard when Claude supports it. + +#### Claude Code plugins + +The following plugins are enabled in `.claude/settings.json`: + +| Plugin | Purpose | Source | +| ------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| `php-lsp` | PHP language server for type-aware code intelligence | [claude-plugins-official](https://github.com/anthropics/claude-code-plugins) | +| `context7` | Up-to-date documentation lookup for Symfony, Doctrine, API Platform, etc. | [claude-plugins-official](https://github.com/anthropics/claude-code-plugins) | +| `code-review` | Pull request code review | [claude-plugins-official](https://github.com/anthropics/claude-code-plugins) | +| `code-simplifier` | Suggests clarity and maintainability improvements | [claude-plugins-official](https://github.com/anthropics/claude-code-plugins) | +| `security-guidance` | Flags potential security issues (OWASP, injection, etc.) | [claude-plugins-official](https://github.com/anthropics/claude-code-plugins) | +| `playwright` | Browser automation for debugging and testing the EasyAdmin UI | [claude-plugins-official](https://github.com/anthropics/claude-code-plugins) | +| `feature-dev` | Guided feature development with codebase exploration and architecture focus | [claude-plugins-official](https://github.com/anthropics/claude-code-plugins) | + +> **Note:** The `php-lsp` plugin requires [Intelephense](https://intelephense.com/) +> installed globally: `npm install -g intelephense`. All other plugins work +> without additional dependencies. + +#### Claude Code agents + +Custom agents in `.claude/agents/` automate multi-step workflows: + +| Agent | Purpose | +| ------------------ | ----------------------------------------------------------------- | +| `pr-readiness` | Runs all CI-equivalent checks locally before creating a PR | +| `create-migration` | Generates and validates a Doctrine migration after entity changes | + +#### Claude Code skills + +Custom skills in `.claude/skills/` provide repeatable task shortcuts: + +| Skill | Invocation | Purpose | +| ----------------- | ------------------ | ----------------------------------------------------- | +| `update-api-spec` | `/update-api-spec` | Regenerate and stage OpenAPI spec files after changes | + +#### Claude Code hooks + +Hooks in `.claude/settings.json` run automatically on tool events: + +| Hook | Trigger | Purpose | +| -------------- | -------------- | ------------------------------------------------------ | +| Docker start | `SessionStart` | Starts Docker services on session start | +| PHP-CS-Fixer | `PostToolUse` | Auto-formats PHP files on edit | +| PHPStan | `PostToolUse` | Runs static analysis on edited PHP files | +| Twig-CS-Fixer | `PostToolUse` | Auto-formats Twig templates on edit | +| Composer norm | `PostToolUse` | Normalizes `composer.json` on edit | +| Prettier | `PostToolUse` | Auto-formats JS, CSS, YAML, and Markdown files on edit | +| Lock guard | `PreToolUse` | Blocks edits to lock files and `.env.local` | +| Container lint | `Stop` | Validates Symfony DI container before stopping | + +#### MCP servers + +A shared `.mcp.json` provides team-wide MCP server configuration: + +| Server | Purpose | +| ---------- | ------------------------------------------------------------------------- | +| `context7` | Live documentation lookup for Symfony, Doctrine, API Platform, and others | diff --git a/Taskfile.yml b/Taskfile.yml index cd73982..108d803 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,5 +1,5 @@ -# https://taskfile.dev - +# yaml-language-server: $schema=https://taskfile.dev/schema.json +--- version: "3" # https://taskfile.dev/usage/#env-files @@ -12,32 +12,54 @@ vars: tasks: site:update: desc: Update/install site - aliases: [site:install] cmds: - task: compose vars: COMPOSE_ARGS: pull - task: compose vars: - COMPOSE_ARGS: up --detach + COMPOSE_ARGS: up --detach --wait - task: composer vars: COMPOSER_ARGS: install + + site:migrate: + desc: Run database migrations + cmds: - task: console vars: CONSOLE_ARGS: doctrine:migrations:migrate --no-interaction - - task: console - vars: - CONSOLE_ARGS: messenger:setup-transports fixtures:load: - desc: Load all fixtures + desc: Load fixtures prompt: Really load all fixtures? cmds: - task: console vars: CONSOLE_ARGS: hautelook:fixtures:load --no-interaction - silent: true + + tests: + desc: Run tests + cmds: + - task: console + vars: + CONSOLE_ARGS: --env=test doctrine:database:drop --if-exists --force --quiet + - task: console + vars: + CONSOLE_ARGS: --env=test doctrine:database:create --no-interaction --if-not-exists --quiet + - task: console + vars: + CONSOLE_ARGS: --env=test doctrine:migrations:migrate --no-interaction --quiet + - task: compose + vars: + COMPOSE_ARGS: exec phpfpm vendor/bin/phpunit --stop-on-failure + + queues: + desc: Consume async messenger queue + cmds: + - task: console + vars: + CONSOLE_ARGS: messenger:consume async --failure-limit=1 -vvv composer: desc: "Run `composer` command. Example: task composer -- normalize" @@ -49,46 +71,46 @@ tasks: compose: desc: "Run `docker compose` command. Example: task compose -- ps" cmds: - # Run docker compose with both arguments passed via var plus any cli args. - "{{.DOCKER_COMPOSE}} {{.COMPOSE_ARGS}} {{.CLI_ARGS}}" console: desc: "Run Symfony console command. Example: task console -- cache:clear" cmds: - # Run docker compose with both arguments passed via var plus any cli args. - task: compose vars: COMPOSE_ARGS: exec phpfpm bin/console {{.CONSOLE_ARGS}} coding-standards:apply: - desc: "Apply coding standards" + aliases: [cs:apply] + desc: Apply coding standards cmds: - task: coding-standards:markdown:apply - task: coding-standards:php:apply - task: coding-standards:twig:apply - task: coding-standards:yaml:apply - - task: coding-standards:css:apply silent: true coding-standards:check: - desc: "Apply coding standards" + aliases: [cs:check] + desc: Check coding standards cmds: - task: coding-standards:markdown:check - task: coding-standards:php:check - task: coding-standards:twig:check - task: coding-standards:yaml:check - - task: coding-standards:css:check silent: true coding-standards:markdown:apply: - desc: "Apply coding standards for Markdown" + aliases: [cs:markdown:apply] + desc: Apply coding standards for Markdown cmds: - task: compose vars: COMPOSE_ARGS: run --rm markdownlint markdownlint '**/*.md' --fix coding-standards:markdown:check: - desc: "Apply and check coding standards for Markdown" + aliases: [cs:markdown:check] + desc: Check coding standards for Markdown cmds: - task: coding-standards:markdown:apply - task: compose @@ -96,7 +118,8 @@ tasks: COMPOSE_ARGS: run --rm markdownlint markdownlint '**/*.md' coding-standards:php:apply: - desc: "Apply coding standards for PHP" + aliases: [cs:php:apply] + desc: Apply coding standards for PHP cmds: - task: compose vars: @@ -104,7 +127,8 @@ tasks: silent: true coding-standards:php:check: - desc: "Apply and check coding standards for PHP" + aliases: [cs:php:check] + desc: Check coding standards for PHP cmds: - task: coding-standards:php:apply - task: compose @@ -113,105 +137,122 @@ tasks: silent: true coding-standards:twig:apply: - desc: "Apply coding standards for Twig" + aliases: [cs:twig:apply] + desc: Apply coding standards for Twig cmds: - task: compose vars: - COMPOSE_ARGS: exec phpfpm vendor/bin/twig-cs-fixer fix + COMPOSE_ARGS: exec phpfpm vendor/bin/twig-cs-fixer lint --fix silent: true coding-standards:twig:check: - desc: "Apply and check coding standards for Twig" + aliases: [cs:twig:check] + desc: Check coding standards for Twig cmds: - task: coding-standards:twig:apply - task: compose vars: - COMPOSE_ARGS: exec phpfpm vendor/bin/twig-cs-fixer check + COMPOSE_ARGS: exec phpfpm vendor/bin/twig-cs-fixer lint silent: true coding-standards:yaml:apply: - desc: "Apply coding standards for YAML" + aliases: [cs:yaml:apply] + desc: Apply coding standards for YAML cmds: - task: compose vars: - COMPOSE_ARGS: run --rm prettier '**/*.{yml,yaml}' --write + COMPOSE_ARGS: run --rm prettier '**/*.{yml,yaml}' --write --no-error-on-unmatched-pattern silent: true coding-standards:yaml:check: - desc: "Apply and check coding standards for YAML" + aliases: [cs:yaml:check] + desc: Check coding standards for YAML cmds: - task: coding-standards:yaml:apply - task: compose vars: - COMPOSE_ARGS: run --rm prettier '**/*.{yml,yaml}' --check + COMPOSE_ARGS: run --rm prettier '**/*.{yml,yaml}' --check --no-error-on-unmatched-pattern + silent: true + + # Composer checks (mirrors composer.yaml CI) - coding-standards:css:apply: - desc: "Apply coding standards for YAML" + composer:check: + desc: Validate, normalize, and audit composer cmds: - task: compose vars: - COMPOSE_ARGS: run --rm prettier 'assets/**/*.{css,scss}' --write - silent: true - - coding-standards:css:check: - desc: "Apply and check coding standards for YAML" - cmds: - - task: coding-standards:yaml:apply + COMPOSE_ARGS: exec phpfpm composer validate --strict - task: compose vars: - COMPOSE_ARGS: run --rm prettier 'assets/**/*.{css,scss}' --check - silent: true + COMPOSE_ARGS: exec phpfpm composer normalize --dry-run + - task: compose + vars: + COMPOSE_ARGS: exec phpfpm composer audit - code-analysis: - desc: "Run code analysis" - cmds: - - task: code-analysis:phpstan - silent: true + # Analysis - code-analysis:phpstan: - desc: "Run PHPStan" + analysis:php: + aliases: [analyze:php] + desc: Run PHPStan static analysis cmds: - task: compose vars: COMPOSE_ARGS: exec phpfpm vendor/bin/phpstan silent: true - messenger:consume: - desc: "Run queue consumer" + # Test matrix (mirrors pr.yaml CI jobs) + + test:matrix: + desc: Run full test matrix (tests, schema validation, fixtures, assets) cmds: - - task: console - vars: - CONSOLE_ARGS: bin/console messenger:consume async --failure-limit=1 -vvv + - task: tests + - task: test:doctrine-schema + - task: test:fixtures + - task: test:build-assets - test:setup: - desc: "Setup test environment" + test:doctrine-schema: + desc: Validate Doctrine schema against database cmds: - task: console vars: - CONSOLE_ARGS: --env=test doctrine:database:create --if-not-exists --no-interaction - - task: console - vars: - CONSOLE_ARGS: --env=test doctrine:database:create --no-interaction --if-not-exists --quiet + CONSOLE_ARGS: doctrine:schema:validate + + test:fixtures: + desc: Verify fixtures load successfully + cmds: - task: console vars: - CONSOLE_ARGS: --env=test doctrine:migrations:migrate --no-interaction --quiet + CONSOLE_ARGS: hautelook:fixtures:load --no-interaction - test: - desc: "Run tests" + test:build-assets: + desc: Verify frontend assets compile cmds: - task: compose vars: - COMPOSE_ARGS: exec --env XDEBUG_MODE=coverage phpfpm vendor/bin/phpunit --coverage-clover=coverage/unit.xml + COMPOSE_ARGS: run --rm node yarn install + - task: compose + vars: + COMPOSE_ARGS: run --rm node yarn build + + # CI + + pr:actions: + desc: Run all CI checks locally + cmds: + - task: composer:check + - task: coding-standards:check + - task: analyze:php + - task: test:matrix api:spec:export: - desc: "Export API spec" + desc: Export API specification cmds: - task: console vars: - CONSOLE_ARGS: api:openapi:export --yaml --output=public/api-spec-v1.yaml --no-interaction + CONSOLE_ARGS: api:openapi:export --output=public/api-spec-v1.yaml --yaml --no-interaction - task: console vars: - CONSOLE_ARGS: api:openapi:export --json --output=public/api-spec-v1.json --no-interaction + CONSOLE_ARGS: api:openapi:export --output=public/api-spec-v1.json --no-interaction silent: true default: diff --git a/claude.md b/claude.md new file mode 100644 index 0000000..a83a1af --- /dev/null +++ b/claude.md @@ -0,0 +1,148 @@ +# 🤖 Code Agents - DevOps ITKsites + +## Project Overview + +**DevOps ITKsites** is an internal Symfony application for server and site +registration/monitoring at ITK Dev. It receives `DetectionResults` from the +[ITK sites server harvester](https://github.com/itk-dev/devops_itkServerHarvest) +and processes them asynchronously to track servers, sites, domains, Docker +images, packages, modules, CVEs, and git repositories. + +## Technology Stack + +- **Language**: PHP 8.5+ (Symfony 8.0) +- **API**: API Platform 4.0 (REST) +- **Admin UI**: EasyAdmin 5.x +- **Database**: Doctrine ORM 3.x / DBAL 4.x with MariaDB +- **Messaging**: Symfony Messenger (AMQP/RabbitMQ) +- **Auth**: OpenID Connect (`itk-dev/openid-connect-bundle`) +- **Frontend**: Webpack Encore, Stimulus.js +- **Testing**: PHPUnit 13+ +- **Code Quality**: PHP-CS-Fixer, PHPStan, Rector + +## Architecture + +```mermaid +graph TD + A[Harvester] -->|POST DetectionResult| B[API Platform REST endpoint] + B --> C[Symfony Messenger] + C --> D[Async Message Handlers] + D --> D1[DirectoryHandler] + D --> D2[DockerImageHandler] + D --> D3[DrupalHandler] + D --> D4[GitHandler] + D --> D5[NginxHandler] + D --> D6[SymfonyHandler] + D1 & D2 & D3 & D4 & D5 & D6 --> E[Doctrine ORM] + E --> F[MariaDB] + F --> G[EasyAdmin UI] +``` + +### Key Directories + +| Directory | Purpose | +|-----------------------|--------------------------------------------------------------------------------------------------| +| `src/Entity/` | ~20 Doctrine entities (Server, Site, Domain, Installation, Package, DockerImage, Advisory, etc.) | +| `src/Handler/` | DetectionResult handlers (Directory, Docker, Drupal, Git, Nginx, Symfony) | +| `src/MessageHandler/` | Async message processing (PersistDetectionResult, ProcessDetectionResult) | +| `src/Admin/` | EasyAdmin CRUD controllers | +| `src/ApiResource/` | API Platform resource definitions | +| `src/Service/` | Factories (PackageVersion, ModuleVersion, Advisory) and export services | +| `src/Repository/` | Doctrine repositories | +| `config/packages/` | Bundle configurations | +| `migrations/` | Doctrine migrations | +| `fixtures/` | Hautelook/Alice test fixtures | +| `tests/` | PHPUnit tests (Api, Controller, MessageHandler) | + +### Data Flow + +All analyzed data (sites, installations, domains, packages, etc.) can be +truncated and rebuilt by replaying DetectionResults. Manually maintained data +(Servers, OIDC setups, Service Certificates) is separate and must be preserved. + +## Development Environment + +```sh +# Start services (MariaDB, PHP-FPM 8.5, Nginx, Mailpit) +docker compose pull && docker compose up --detach + +# Install dependencies +docker compose exec phpfpm composer install + +# Run migrations +docker compose exec phpfpm bin/console doctrine:migrations:migrate --no-interaction + +# Load fixtures +docker compose exec phpfpm composer fixtures + +# Login as admin (after fixtures) +docker compose exec phpfpm bin/console itk-dev:openid-connect:login admin@example.com + +# Process message queues +docker compose exec phpfpm composer queues + +# Build frontend assets +docker compose run --rm node yarn install && docker compose run --rm node yarn build +``` + +## Quality Checks + +All commands run inside Docker containers: + +```sh +# PHP coding standards (PHP-CS-Fixer) +docker compose exec phpfpm composer coding-standards-check +docker compose exec phpfpm composer coding-standards-apply + +# PHPUnit tests (creates test DB, runs migrations, executes tests) +docker compose exec phpfpm composer tests + +# Frontend coding standards +docker compose run --rm node yarn coding-standards-check + +# API spec export (must be committed) +docker compose exec phpfpm composer update-api-spec +``` + +## CI/CD + +### GitHub Actions (`pr.yaml`) + +Pull requests run these checks: + +1. **Composer** (`composer.yaml`) - validates, normalizes, and audits +2. **Doctrine schema validation** (`pr.yaml`) - migrations + schema check against MariaDB +3. **PHP-CS-Fixer** (`php.yaml`) - PHP coding standards +4. **PHPStan** (`pr.yaml`) - static analysis (level 6) +5. **PHPUnit** (`pr.yaml`) - unit/integration tests with MariaDB + coverage +6. **Twig** (`twig.yaml`) - Twig coding standards (twig-cs-fixer) +7. **YAML** (`yaml.yaml`) - YAML formatting (Prettier) +8. **Markdown** (`markdown.yaml`) - Markdown linting (markdownlint) +9. **JavaScript** (`javascript.yaml`) - JS formatting (Prettier) +10. **Styles** (`styles.yaml`) - CSS/SCSS formatting (Prettier) +11. **API spec** (`api-spec.yaml`) - ensures exported OpenAPI spec is up to date +12. **Fixtures** (`pr.yaml`) - verifies fixtures load successfully +13. **Asset build** (`pr.yaml`) - verifies frontend assets compile +14. **Changelog** (`changelog.yaml`) - ensures CHANGELOG.md is updated + +### Woodpecker CI (deployment) + +- `stg.yml` - Deploys to staging on push to develop +- `prod.yml` - Deploys to production on release (Ansible playbook, runs migrations + transport setup) + +## PR Guidelines + +- PRs must link to a ticket +- Code must pass all CI checks (tests, coding standards, static analysis) +- CHANGELOG.md must be updated +- UI changes require screenshots +- Base branch: `develop` + +## Important Conventions + +- Entity classes extend `AbstractBaseEntity` (provides `id`, `createdAt`, `updatedAt`) +- Detection handlers implement `DetectionResultHandlerInterface` +- Handlers are auto-tagged and injected via tagged iterator in `services.yaml` +- Async processing uses Symfony Messenger with AMQP transport +- Environment-specific config goes in `.env.local` (not committed) +- API specs (`public/api-spec-v1.yaml` and `.json`) must be regenerated and committed when API changes diff --git a/composer.json b/composer.json index 57405df..81d744f 100644 --- a/composer.json +++ b/composer.json @@ -7,66 +7,61 @@ "php": ">=8.4", "ext-ctype": "*", "ext-iconv": "*", - "ext-mbstring": "*", - "api-platform/core": "~4.1.25", - "composer/semver": "^3.4.4", - "doctrine/dbal": "^4.3.4", - "doctrine/doctrine-bundle": "^2.18", - "doctrine/doctrine-migrations-bundle": "^3.5", - "doctrine/orm": "^3.5.2", - "easycorp/easyadmin-bundle": "^4.26.3", - "itk-dev/openid-connect-bundle": "^4.0.1", - "itk-dev/vault-bundle": "^0.1.2", - "nelmio/cors-bundle": "^2.5", - "ocramius/doctrine-batch-utils": "^2.11", - "phpdocumentor/reflection-docblock": "^5.6.3", - "phpstan/phpdoc-parser": "^2.3", - "symfony/amqp-messenger": "^7.3.2", - "symfony/asset": "^7.3.0", - "symfony/asset-mapper": "^7.3.4", - "symfony/browser-kit": "^7.3.2", - "symfony/console": "^7.3.4", - "symfony/doctrine-messenger": "^7.3.4", - "symfony/dotenv": "^7.3.2", - "symfony/expression-language": "^7.3.2", - "symfony/flex": "^2.8.2", - "symfony/framework-bundle": "^7.3.4", - "symfony/http-client": "^7.3.4", - "symfony/messenger": "^7.3.3", - "symfony/monolog-bundle": "^3.10", - "symfony/object-mapper": "^7.3.0", - "symfony/property-access": "^7.3.3", - "symfony/property-info": "^7.3.4", - "symfony/runtime": "^7.3.4", - "symfony/security-bundle": "^7.3.4", - "symfony/serializer": "^7.3.4", - "symfony/twig-bundle": "^7.3.4", - "symfony/uid": "^7.3.1", - "symfony/validator": "^7.3.4", - "symfony/webpack-encore-bundle": "^2.3", - "symfony/yaml": "^7.3.3", - "twig/extra-bundle": "^2.12 || ^3.21", - "twig/twig": "^2.12 || ^3.21.1" + "api-platform/core": "^4.0", + "composer/semver": "^3.4", + "doctrine/dbal": "^4.0", + "doctrine/doctrine-bundle": "^3.2", + "doctrine/doctrine-migrations-bundle": "^4.0", + "doctrine/orm": "^3.0", + "easycorp/easyadmin-bundle": "^5.0", + "itk-dev/openid-connect-bundle": "^4.0", + "itk-dev/vault-bundle": "^1.0.0", + "nelmio/cors-bundle": "^2.2", + "ocramius/doctrine-batch-utils": "^2.8", + "phpstan/phpdoc-parser": "^2.0", + "symfony/amqp-messenger": "^8.0", + "symfony/asset": "^8.0", + "symfony/asset-mapper": "8.0.*", + "symfony/browser-kit": "^8.0", + "symfony/console": "^8.0", + "symfony/doctrine-messenger": "^8.0", + "symfony/dotenv": "^8.0", + "symfony/expression-language": "^8.0", + "symfony/flex": "^2", + "symfony/framework-bundle": "^8.0", + "symfony/http-client": "^8.0", + "symfony/messenger": "^8.0", + "symfony/monolog-bundle": "^4.0", + "symfony/property-access": "^8.0", + "symfony/property-info": "^8.0", + "symfony/runtime": "^8.0", + "symfony/security-bundle": "^8.0", + "symfony/serializer": "^8.0", + "symfony/twig-bundle": "^8.0", + "symfony/uid": "^8.0", + "symfony/validator": "^8.0", + "symfony/webpack-encore-bundle": "^2.0", + "symfony/yaml": "^8.0" }, "require-dev": { - "ergebnis/composer-normalize": "^2.48.2", - "friendsofphp/php-cs-fixer": "^3.88.2", - "hautelook/alice-bundle": "^2.15.1", - "justinrainbow/json-schema": "^6.6", - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.31", - "phpstan/phpstan-doctrine": "^2.0.10", - "phpstan/phpstan-phpunit": "^2.0.7", - "phpstan/phpstan-symfony": "^2.0.8", - "phpunit/phpunit": "^12.4.1", - "rector/rector": "^2.2.3", - "symfony/css-selector": "^7.3", - "symfony/debug-bundle": "^7.3.4", - "symfony/maker-bundle": "^1.64", - "symfony/stopwatch": "^7.3", - "symfony/var-dumper": "^7.3.4", - "symfony/web-profiler-bundle": "^7.3.4", - "vincentlanglet/twig-cs-fixer": "^3.10" + "ergebnis/composer-normalize": "^2.23", + "friendsofphp/php-cs-fixer": "^3.6", + "hautelook/alice-bundle": "^2.14", + "justinrainbow/json-schema": "^6.0", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-doctrine": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-symfony": "^2.0", + "phpunit/phpunit": "^13.0", + "rector/rector": "^2.0", + "symfony/css-selector": "^8.0", + "symfony/debug-bundle": "^8.0", + "symfony/maker-bundle": "^1.37", + "symfony/stopwatch": "^8.0", + "symfony/var-dumper": "^8.0", + "symfony/web-profiler-bundle": "^8.0", + "vincentlanglet/twig-cs-fixer": "^3.14" }, "replace": { "symfony/polyfill-ctype": "*", @@ -104,8 +99,8 @@ }, "extra": { "symfony": { - "allow-contrib": true, - "require": "7.3.*" + "allow-contrib": false, + "require": "8.0.*" } }, "scripts": { diff --git a/composer.lock b/composer.lock index c700676..d938250 100644 --- a/composer.lock +++ b/composer.lock @@ -4,48 +4,50 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1e514f2fb03ce2e04966c86e6b5b2cc2", + "content-hash": "b8846cfbafdf8854299f8b6dc8761bc9", "packages": [ { "name": "api-platform/core", - "version": "v4.1.25", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/api-platform/core.git", - "reference": "5c25cd2876740cd25e24efdbbb349359d5c98fc1" + "reference": "ad027fbbe1b64294174b962c9d9f02d0a3bca7a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/core/zipball/5c25cd2876740cd25e24efdbbb349359d5c98fc1", - "reference": "5c25cd2876740cd25e24efdbbb349359d5c98fc1", + "url": "https://api.github.com/repos/api-platform/core/zipball/ad027fbbe1b64294174b962c9d9f02d0a3bca7a7", + "reference": "ad027fbbe1b64294174b962c9d9f02d0a3bca7a7", "shasum": "" }, "require": { + "composer/semver": "^3.4", "doctrine/inflector": "^2.0", "php": ">=8.2", "psr/cache": "^1.0 || ^2.0 || ^3.0", "psr/container": "^1.0 || ^2.0", "symfony/deprecation-contracts": "^3.1", - "symfony/http-foundation": "^6.4 || ^7.0", - "symfony/http-kernel": "^6.4 || ^7.0", - "symfony/property-access": "^6.4 || ^7.0", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.0", + "symfony/http-foundation": "^6.4.14 || ^7.0 || ^8.0", + "symfony/http-kernel": "^6.4.13 || ^7.0 || ^8.0", + "symfony/property-access": "^6.4 || ^7.0 || ^8.0", + "symfony/property-info": "^6.4 || ^7.1 || ^8.0", + "symfony/serializer": "^6.4 || ^7.0 || ^8.0", "symfony/translation-contracts": "^3.3", - "symfony/type-info": "^7.2", - "symfony/validator": "^6.4 || ^7.1", - "symfony/web-link": "^6.4 || ^7.1", + "symfony/type-info": "^7.4 || ^8.0", + "symfony/validator": "^6.4.11 || ^7.1 || ^8.0", + "symfony/web-link": "^6.4 || ^7.1 || ^8.0", "willdurand/negotiation": "^3.1" }, "conflict": { "doctrine/common": "<3.2.2", "doctrine/dbal": "<2.10", "doctrine/mongodb-odm": "<2.4", - "doctrine/orm": "<2.14.0", + "doctrine/orm": "<2.14.0 || 3.0.0", "doctrine/persistence": "<1.3", "phpspec/prophecy": "<1.15", "phpunit/phpunit": "<9.5", "symfony/framework-bundle": "6.4.6 || 7.0.6", + "symfony/object-mapper": "<7.3.4", "symfony/var-exporter": "<6.1.1" }, "replace": { @@ -55,13 +57,14 @@ "api-platform/documentation": "self.version", "api-platform/elasticsearch": "self.version", "api-platform/graphql": "self.version", + "api-platform/hal": "self.version", "api-platform/http-cache": "self.version", "api-platform/hydra": "self.version", "api-platform/json-api": "self.version", - "api-platform/json-hal": "self.version", "api-platform/json-schema": "self.version", "api-platform/jsonld": "self.version", "api-platform/laravel": "self.version", + "api-platform/mcp": "self.version", "api-platform/metadata": "self.version", "api-platform/openapi": "self.version", "api-platform/parameter-validator": "self.version", @@ -74,80 +77,82 @@ "require-dev": { "behat/behat": "^3.11", "behat/mink": "^1.9", - "doctrine/cache": "^1.11 || ^2.1", "doctrine/common": "^3.2.2", "doctrine/dbal": "^4.0", - "doctrine/doctrine-bundle": "^2.11", - "doctrine/mongodb-odm": "^2.10", - "doctrine/mongodb-odm-bundle": "^5.0", + "doctrine/doctrine-bundle": "^2.11 || ^3.1", "doctrine/orm": "^2.17 || ^3.0", "elasticsearch/elasticsearch": "^7.17 || ^8.4 || ^9.0", "friends-of-behat/mink-browserkit-driver": "^1.3.1", "friends-of-behat/mink-extension": "^2.2", "friends-of-behat/symfony-extension": "^2.1", + "friendsofphp/php-cs-fixer": "^3.93", "guzzlehttp/guzzle": "^6.0 || ^7.0", - "illuminate/config": "^11.0 || ^12.0", - "illuminate/contracts": "^11.0 || ^12.0", - "illuminate/database": "^11.0 || ^12.0", - "illuminate/http": "^11.0 || ^12.0", - "illuminate/pagination": "^11.0 || ^12.0", - "illuminate/routing": "^11.0 || ^12.0", - "illuminate/support": "^11.0 || ^12.0", - "jangregor/phpstan-prophecy": "^1.0", - "justinrainbow/json-schema": "^5.2.11", - "laravel/framework": "^11.0 || ^12.0", - "orchestra/testbench": "^9.1", + "illuminate/config": "^11.0 || ^12.0 || ^13.0", + "illuminate/contracts": "^11.0 || ^12.0 || ^13.0", + "illuminate/database": "^11.0 || ^12.0 || ^13.0", + "illuminate/http": "^11.0 || ^12.0 || ^13.0", + "illuminate/pagination": "^11.0 || ^12.0 || ^13.0", + "illuminate/routing": "^11.0 || ^12.0 || ^13.0", + "illuminate/support": "^11.0 || ^12.0 || ^13.0", + "jangregor/phpstan-prophecy": "^2.1.11", + "justinrainbow/json-schema": "^6.5.2", + "laravel/framework": "^11.0 || ^12.0 || ^13.0", + "mcp/sdk": ">=0.4 <1.0", + "orchestra/testbench": "^10.9 || ^11.0", "phpspec/prophecy-phpunit": "^2.2", "phpstan/extension-installer": "^1.1", "phpstan/phpdoc-parser": "^1.29 || ^2.0", - "phpstan/phpstan": "^1.10", - "phpstan/phpstan-doctrine": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", - "phpstan/phpstan-symfony": "^1.0", - "phpunit/phpunit": "11.5.x-dev", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-doctrine": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-symfony": "^2.0", + "phpunit/phpunit": "^11.5 || ^12.2", "psr/log": "^1.0 || ^2.0 || ^3.0", "ramsey/uuid": "^4.7", "ramsey/uuid-doctrine": "^2.0", "soyuka/contexts": "^3.3.10", "soyuka/pmu": "^0.2.0", "soyuka/stubs-mongodb": "^1.0", - "symfony/asset": "^6.4 || ^7.0", - "symfony/browser-kit": "^6.4 || ^7.0", - "symfony/cache": "^6.4 || ^7.0", - "symfony/config": "^6.4 || ^7.0", - "symfony/console": "^6.4 || ^7.0", - "symfony/css-selector": "^6.4 || ^7.0", - "symfony/dependency-injection": "^6.4 || ^7.0", - "symfony/doctrine-bridge": "^6.4.2 || ^7.0.2", - "symfony/dom-crawler": "^6.4 || ^7.0", - "symfony/error-handler": "^6.4 || ^7.0", - "symfony/event-dispatcher": "^6.4 || ^7.0", - "symfony/expression-language": "^6.4 || ^7.0", - "symfony/finder": "^6.4 || ^7.0", - "symfony/form": "^6.4 || ^7.0", - "symfony/framework-bundle": "^6.4 || ^7.0", - "symfony/http-client": "^6.4 || ^7.0", - "symfony/intl": "^6.4 || ^7.0", + "symfony/asset": "^6.4 || ^7.0 || ^8.0", + "symfony/browser-kit": "^6.4 || ^7.0 || ^8.0", + "symfony/cache": "^6.4 || ^7.0 || ^8.0", + "symfony/config": "^6.4 || ^7.0 || ^8.0", + "symfony/console": "^6.4 || ^7.0 || ^8.0", + "symfony/css-selector": "^6.4 || ^7.0 || ^8.0", + "symfony/dependency-injection": "^6.4 || ^7.0 || ^8.0", + "symfony/doctrine-bridge": "^6.4.2 || ^7.1 || ^8.0", + "symfony/dom-crawler": "^6.4 || ^7.0 || ^8.0", + "symfony/error-handler": "^6.4 || ^7.0 || ^8.0", + "symfony/event-dispatcher": "^6.4 || ^7.0 || ^8.0", + "symfony/expression-language": "^6.4 || ^7.0 || ^8.0", + "symfony/finder": "^6.4 || ^7.0 || ^8.0", + "symfony/form": "^6.4 || ^7.0 || ^8.0", + "symfony/framework-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/http-client": "^6.4 || ^7.0 || ^8.0", + "symfony/intl": "^6.4 || ^7.0 || ^8.0", + "symfony/json-streamer": "^7.4 || ^8.0", "symfony/maker-bundle": "^1.24", + "symfony/mcp-bundle": "dev-main", "symfony/mercure-bundle": "*", - "symfony/messenger": "^6.4 || ^7.0", - "symfony/routing": "^6.4 || ^7.0", - "symfony/security-bundle": "^6.4 || ^7.0", - "symfony/security-core": "^6.4 || ^7.0", - "symfony/stopwatch": "^6.4 || ^7.0", - "symfony/string": "^6.4 || ^7.0", - "symfony/twig-bundle": "^6.4 || ^7.0", - "symfony/uid": "^6.4 || ^7.0", - "symfony/validator": "^6.4 || ^7.0", - "symfony/web-profiler-bundle": "^6.4 || ^7.0", - "symfony/yaml": "^6.4 || ^7.0", + "symfony/messenger": "^6.4 || ^7.0 || ^8.0", + "symfony/object-mapper": "^7.4 || ^8.0", + "symfony/routing": "^6.4 || ^7.0 || ^8.0", + "symfony/security-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/security-core": "^6.4 || ^7.0 || ^8.0", + "symfony/stopwatch": "^6.4 || ^7.0 || ^8.0", + "symfony/string": "^6.4 || ^7.0 || ^8.0", + "symfony/twig-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/uid": "^6.4 || ^7.0 || ^8.0", + "symfony/var-exporter": "^7.4 || ^8.0", + "symfony/web-profiler-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/yaml": "^6.4 || ^7.0 || ^8.0", "twig/twig": "^1.42.3 || ^2.12 || ^3.0", "webonyx/graphql-php": "^15.0" }, "suggest": { "doctrine/mongodb-odm-bundle": "To support MongoDB. Only versions 4.0 and later are supported.", "elasticsearch/elasticsearch": "To support Elasticsearch.", - "ocramius/package-versions": "To display the API Platform's version in the debug bar.", + "opensearch-project/opensearch-php": "To support OpenSearch (^2.5).", "phpstan/phpdoc-parser": "To support extracting metadata from PHPDoc.", "psr/cache-implementation": "To use metadata caching.", "ramsey/uuid": "To support Ramsey's UUID identifiers.", @@ -155,6 +160,7 @@ "symfony/config": "To load XML configuration files.", "symfony/expression-language": "To use authorization features.", "symfony/http-client": "To use the HTTP cache invalidation system.", + "symfony/json-streamer": "To use the JSON Streamer component.", "symfony/messenger": "To support messenger integration.", "symfony/security": "To use authorization features.", "symfony/twig-bundle": "To use the Swagger UI integration.", @@ -175,12 +181,13 @@ "name": "api-platform/api-platform" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.1 || ^8.0" }, "branch-alias": { "dev-3.4": "3.4.x-dev", "dev-4.1": "4.1.x-dev", - "dev-main": "4.2.x-dev" + "dev-4.2": "4.2.x-dev", + "dev-main": "4.4.x-dev" } }, "autoload": { @@ -219,9 +226,9 @@ ], "support": { "issues": "https://github.com/api-platform/core/issues", - "source": "https://github.com/api-platform/core/tree/v4.1.25" + "source": "https://github.com/api-platform/core/tree/v4.3.5" }, - "time": "2025-09-26T15:30:38+00:00" + "time": "2026-05-11T12:20:22+00:00" }, { "name": "composer/semver", @@ -302,16 +309,16 @@ }, { "name": "doctrine/collections", - "version": "2.3.0", + "version": "2.6.0", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "2eb07e5953eed811ce1b309a7478a3b236f2273d" + "reference": "7713da39d8e237f28411d6a616a3dce5e20d5de2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/2eb07e5953eed811ce1b309a7478a3b236f2273d", - "reference": "2eb07e5953eed811ce1b309a7478a3b236f2273d", + "url": "https://api.github.com/repos/doctrine/collections/zipball/7713da39d8e237f28411d6a616a3dce5e20d5de2", + "reference": "7713da39d8e237f28411d6a616a3dce5e20d5de2", "shasum": "" }, "require": { @@ -320,11 +327,11 @@ "symfony/polyfill-php84": "^1.30" }, "require-dev": { - "doctrine/coding-standard": "^12", + "doctrine/coding-standard": "^14", "ext-json": "*", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^10.5" + "phpstan/phpstan": "^2.1.30", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpunit/phpunit": "^10.5.58 || ^11.5.42 || ^12.4" }, "type": "library", "autoload": { @@ -368,7 +375,7 @@ ], "support": { "issues": "https://github.com/doctrine/collections/issues", - "source": "https://github.com/doctrine/collections/tree/2.3.0" + "source": "https://github.com/doctrine/collections/tree/2.6.0" }, "funding": [ { @@ -384,20 +391,20 @@ "type": "tidelift" } ], - "time": "2025-03-22T10:17:19+00:00" + "time": "2026-01-15T10:01:58+00:00" }, { "name": "doctrine/dbal", - "version": "4.3.4", + "version": "4.4.3", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "1a2fbd0e93b8dec7c3d1ac2b6396a7b929b130dc" + "reference": "61e730f1658814821a85f2402c945f3883407dec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/1a2fbd0e93b8dec7c3d1ac2b6396a7b929b130dc", - "reference": "1a2fbd0e93b8dec7c3d1ac2b6396a7b929b130dc", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/61e730f1658814821a85f2402c945f3883407dec", + "reference": "61e730f1658814821a85f2402c945f3883407dec", "shasum": "" }, "require": { @@ -413,11 +420,11 @@ "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", - "symfony/console": "^5.4|^6.3|^7.0" + "phpunit/phpunit": "11.5.50", + "slevomat/coding-standard": "8.27.1", + "squizlabs/php_codesniffer": "4.0.1", + "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." @@ -474,7 +481,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/4.3.4" + "source": "https://github.com/doctrine/dbal/tree/4.4.3" }, "funding": [ { @@ -490,33 +497,33 @@ "type": "tidelift" } ], - "time": "2025-10-09T09:11:36+00:00" + "time": "2026-03-20T08:52:12+00:00" }, { "name": "doctrine/deprecations", - "version": "1.1.5", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -536,69 +543,63 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { "name": "doctrine/doctrine-bundle", - "version": "2.18.0", + "version": "3.2.2", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineBundle.git", - "reference": "cd5d4da6a5f7cf3d8708e17211234657b5eb4e95" + "reference": "af84173db6978c3d2688ea3bcf3a91720b0704ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/cd5d4da6a5f7cf3d8708e17211234657b5eb4e95", - "reference": "cd5d4da6a5f7cf3d8708e17211234657b5eb4e95", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/af84173db6978c3d2688ea3bcf3a91720b0704ce", + "reference": "af84173db6978c3d2688ea3bcf3a91720b0704ce", "shasum": "" }, "require": { - "doctrine/dbal": "^3.7.0 || ^4.0", + "doctrine/dbal": "^4.0", "doctrine/deprecations": "^1.0", - "doctrine/persistence": "^3.1 || ^4", + "doctrine/persistence": "^4", "doctrine/sql-formatter": "^1.0.1", - "php": "^8.1", - "symfony/cache": "^6.4 || ^7.0", - "symfony/config": "^6.4 || ^7.0", - "symfony/console": "^6.4 || ^7.0", - "symfony/dependency-injection": "^6.4 || ^7.0", - "symfony/doctrine-bridge": "^6.4.3 || ^7.0.3", - "symfony/framework-bundle": "^6.4 || ^7.0", - "symfony/service-contracts": "^2.5 || ^3" + "php": "^8.4", + "symfony/cache": "^6.4 || ^7.0 || ^8.0", + "symfony/config": "^6.4 || ^7.0 || ^8.0", + "symfony/console": "^6.4 || ^7.0 || ^8.0", + "symfony/dependency-injection": "^6.4 || ^7.0 || ^8.0", + "symfony/doctrine-bridge": "^6.4.3 || ^7.0.3 || ^8.0", + "symfony/framework-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/service-contracts": "^3" }, "conflict": { - "doctrine/annotations": ">=3.0", - "doctrine/cache": "< 1.11", - "doctrine/orm": "<2.17 || >=4.0", - "symfony/var-exporter": "< 6.4.1 || 7.0.0", - "twig/twig": "<2.13 || >=3.0 <3.0.4" + "doctrine/orm": "<3.0 || >=4.0", + "twig/twig": "<3.0.4" }, "require-dev": { - "doctrine/annotations": "^1 || ^2", - "doctrine/cache": "^1.11 || ^2.0", "doctrine/coding-standard": "^14", - "doctrine/orm": "^2.17 || ^3.1", - "friendsofphp/proxy-manager-lts": "^1.0", + "doctrine/orm": "^3.4.4", "phpstan/phpstan": "2.1.1", "phpstan/phpstan-phpunit": "2.0.3", "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "^10.5.53 || ^12.3.10", - "psr/log": "^1.1.4 || ^2.0 || ^3.0", - "symfony/doctrine-messenger": "^6.4 || ^7.0", - "symfony/expression-language": "^6.4 || ^7.0", - "symfony/messenger": "^6.4 || ^7.0", - "symfony/property-info": "^6.4 || ^7.0", - "symfony/security-bundle": "^6.4 || ^7.0", - "symfony/stopwatch": "^6.4 || ^7.0", - "symfony/string": "^6.4 || ^7.0", - "symfony/twig-bridge": "^6.4 || ^7.0", - "symfony/validator": "^6.4 || ^7.0", - "symfony/var-exporter": "^6.4.1 || ^7.0.1", - "symfony/web-profiler-bundle": "^6.4 || ^7.0", - "symfony/yaml": "^6.4 || ^7.0", - "twig/twig": "^2.14.7 || ^3.0.4" + "phpstan/phpstan-symfony": "^2.0", + "phpunit/phpunit": "^12.3.10", + "psr/log": "^3.0", + "symfony/doctrine-messenger": "^6.4 || ^7.0 || ^8.0", + "symfony/expression-language": "^6.4 || ^7.0 || ^8.0", + "symfony/messenger": "^6.4 || ^7.0 || ^8.0", + "symfony/property-info": "^6.4 || ^7.0 || ^8.0", + "symfony/security-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/stopwatch": "^6.4 || ^7.0 || ^8.0", + "symfony/string": "^6.4 || ^7.0 || ^8.0", + "symfony/twig-bridge": "^6.4 || ^7.0 || ^8.0", + "symfony/validator": "^6.4 || ^7.0 || ^8.0", + "symfony/web-profiler-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/yaml": "^6.4 || ^7.0 || ^8.0", + "twig/twig": "^3.21.1" }, "suggest": { "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", @@ -643,7 +644,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineBundle/issues", - "source": "https://github.com/doctrine/DoctrineBundle/tree/2.18.0" + "source": "https://github.com/doctrine/DoctrineBundle/tree/3.2.2" }, "funding": [ { @@ -659,41 +660,48 @@ "type": "tidelift" } ], - "time": "2025-10-11T04:43:27+00:00" + "time": "2025-12-24T12:24:29+00:00" }, { "name": "doctrine/doctrine-migrations-bundle", - "version": "3.5.0", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", - "reference": "71c81279ca0e907c3edc718418b93fd63074856c" + "reference": "20505da78735744fb4a42a3bb9a416b345ad6f7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/71c81279ca0e907c3edc718418b93fd63074856c", - "reference": "71c81279ca0e907c3edc718418b93fd63074856c", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/20505da78735744fb4a42a3bb9a416b345ad6f7c", + "reference": "20505da78735744fb4a42a3bb9a416b345ad6f7c", "shasum": "" }, "require": { - "doctrine/doctrine-bundle": "^2.4 || ^3.0", + "doctrine/dbal": "^4", + "doctrine/doctrine-bundle": "^3", "doctrine/migrations": "^3.2", - "php": "^7.2 || ^8.0", - "symfony/deprecation-contracts": "^2.1 || ^3", - "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" + "php": "^8.4", + "psr/log": "^3", + "symfony/config": "^6.4 || ^7.0 || ^8.0", + "symfony/console": "^6.4 || ^7.0 || ^8.0", + "symfony/dependency-injection": "^6.4 || ^7.0 || ^8.0", + "symfony/deprecation-contracts": "^3", + "symfony/framework-bundle": "^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/service-contracts": "^3.0" }, "require-dev": { "composer/semver": "^3.0", - "doctrine/coding-standard": "^12 || ^14", - "doctrine/orm": "^2.6 || ^3", - "phpstan/phpstan": "^1.4 || ^2", - "phpstan/phpstan-deprecation-rules": "^1 || ^2", - "phpstan/phpstan-phpunit": "^1 || ^2", - "phpstan/phpstan-strict-rules": "^1.1 || ^2", - "phpstan/phpstan-symfony": "^1.3 || ^2", - "phpunit/phpunit": "^8.5 || ^9.5", - "symfony/phpunit-bridge": "^6.3 || ^7", - "symfony/var-exporter": "^5.4 || ^6 || ^7" + "doctrine/coding-standard": "^14", + "doctrine/orm": "^3", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-phpunit": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpstan/phpstan-symfony": "^2", + "phpunit/phpunit": "^12.5", + "symfony/var-exporter": "^6.4 || ^7 || ^8" }, "type": "symfony-bundle", "autoload": { @@ -728,7 +736,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", - "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.5.0" + "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/4.0.0" }, "funding": [ { @@ -744,20 +752,20 @@ "type": "tidelift" } ], - "time": "2025-10-12T17:06:40+00:00" + "time": "2025-12-05T08:14:38+00:00" }, { "name": "doctrine/event-manager", - "version": "2.0.1", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/dda33921b198841ca8dbad2eaa5d4d34769d18cf", + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf", "shasum": "" }, "require": { @@ -767,10 +775,10 @@ "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.8.8", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "^5.24" + "doctrine/coding-standard": "^14", + "phpdocumentor/guides-cli": "^1.4", + "phpstan/phpstan": "^2.1.32", + "phpunit/phpunit": "^10.5.58" }, "type": "library", "autoload": { @@ -819,7 +827,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/2.0.1" + "source": "https://github.com/doctrine/event-manager/tree/2.1.1" }, "funding": [ { @@ -835,7 +843,7 @@ "type": "tidelift" } ], - "time": "2024-05-22T20:47:39+00:00" + "time": "2026-01-29T07:11:08+00:00" }, { "name": "doctrine/inflector", @@ -929,30 +937,29 @@ }, { "name": "doctrine/instantiator", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/23da848e1a2308728fe5fdddabf4be17ff9720c7", + "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.4" }, "require-dev": { - "doctrine/coding-standard": "^11", + "doctrine/coding-standard": "^14", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5.58" }, "type": "library", "autoload": { @@ -979,7 +986,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "source": "https://github.com/doctrine/instantiator/tree/2.1.0" }, "funding": [ { @@ -995,7 +1002,7 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2026-01-05T06:47:08+00:00" }, { "name": "doctrine/lexer", @@ -1076,16 +1083,16 @@ }, { "name": "doctrine/migrations", - "version": "3.9.4", + "version": "3.9.7", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "1b88fcb812f2cd6e77c83d16db60e3cf1e35c66c" + "reference": "96cb2a89b56c9efb0bac38e606dc0b0f13e650ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/1b88fcb812f2cd6e77c83d16db60e3cf1e35c66c", - "reference": "1b88fcb812f2cd6e77c83d16db60e3cf1e35c66c", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/96cb2a89b56c9efb0bac38e606dc0b0f13e650ec", + "reference": "96cb2a89b56c9efb0bac38e606dc0b0f13e650ec", "shasum": "" }, "require": { @@ -1095,15 +1102,15 @@ "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" + "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" }, "conflict": { "doctrine/orm": "<2.12 || >=4" }, "require-dev": { - "doctrine/coding-standard": "^13", + "doctrine/coding-standard": "^14", "doctrine/orm": "^2.13 || ^3", "doctrine/persistence": "^2 || ^3 || ^4", "doctrine/sql-formatter": "^1.0", @@ -1115,9 +1122,9 @@ "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", - "symfony/process": "^5.4 || ^6.0 || ^7.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.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" }, "suggest": { "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.", @@ -1159,7 +1166,7 @@ ], "support": { "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.9.4" + "source": "https://github.com/doctrine/migrations/tree/3.9.7" }, "funding": [ { @@ -1175,20 +1182,20 @@ "type": "tidelift" } ], - "time": "2025-08-19T06:41:07+00:00" + "time": "2026-04-23T19:33:20+00:00" }, { "name": "doctrine/orm", - "version": "3.5.2", + "version": "3.6.5", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "5a541b8b3a327ab1ea5f93b1615b4ff67a34e109" + "reference": "7e88b416153dceeb563352ca2b12465f09eea173" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/5a541b8b3a327ab1ea5f93b1615b4ff67a34e109", - "reference": "5a541b8b3a327ab1ea5f93b1615b4ff67a34e109", + "url": "https://api.github.com/repos/doctrine/orm/zipball/7e88b416153dceeb563352ca2b12465f09eea173", + "reference": "7e88b416153dceeb563352ca2b12465f09eea173", "shasum": "" }, "require": { @@ -1204,20 +1211,18 @@ "ext-ctype": "*", "php": "^8.1", "psr/cache": "^1 || ^2 || ^3", - "symfony/console": "^5.4 || ^6.0 || ^7.0", - "symfony/var-exporter": "^6.3.9 || ^7.0" + "symfony/console": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/var-exporter": "^6.3.9 || ^7.0 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^13.0", + "doctrine/coding-standard": "^14.0", "phpbench/phpbench": "^1.0", - "phpdocumentor/guides-cli": "^1.4", "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "2.0.3", + "phpstan/phpstan": "2.1.23", "phpstan/phpstan-deprecation-rules": "^2", - "phpunit/phpunit": "^10.4.0", + "phpunit/phpunit": "^10.5.0 || ^11.5", "psr/log": "^1 || ^2 || ^3", - "squizlabs/php_codesniffer": "3.12.0", - "symfony/cache": "^5.4 || ^6.2 || ^7.0" + "symfony/cache": "^5.4 || ^6.2 || ^7.0 || ^8.0" }, "suggest": { "ext-dom": "Provides support for XSD validation for XML mapping files", @@ -1263,42 +1268,43 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/3.5.2" + "source": "https://github.com/doctrine/orm/tree/3.6.5" }, - "time": "2025-08-08T17:00:40+00:00" + "time": "2026-05-11T06:47:19+00:00" }, { "name": "doctrine/persistence", - "version": "4.1.0", + "version": "4.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/persistence.git", - "reference": "dcbdfe4b211ae09478e192289cae7ab0987b29a4" + "reference": "49ab73e0d3e2ac8d1f5ecda3dd8acd5503781e8b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/persistence/zipball/dcbdfe4b211ae09478e192289cae7ab0987b29a4", - "reference": "dcbdfe4b211ae09478e192289cae7ab0987b29a4", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/49ab73e0d3e2ac8d1f5ecda3dd8acd5503781e8b", + "reference": "49ab73e0d3e2ac8d1f5ecda3dd8acd5503781e8b", "shasum": "" }, "require": { + "doctrine/deprecations": "^1", "doctrine/event-manager": "^1 || ^2", "php": "^8.1", "psr/cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "1.12.7", - "phpstan/phpstan-phpunit": "^1", - "phpstan/phpstan-strict-rules": "^1.6", - "phpunit/phpunit": "^9.6", - "symfony/cache": "^4.4 || ^5.4 || ^6.0 || ^7.0", - "symfony/finder": "^4.4 || ^5.4 || ^6.0 || ^7.0" + "doctrine/coding-standard": "^14", + "phpstan/phpstan": "2.1.30", + "phpstan/phpstan-phpunit": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.58 || ^12", + "symfony/cache": "^4.4 || ^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/finder": "^4.4 || ^5.4 || ^6.0 || ^7.0 || ^8.0" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Persistence\\": "src/Persistence" + "Doctrine\\Persistence\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1342,7 +1348,7 @@ ], "support": { "issues": "https://github.com/doctrine/persistence/issues", - "source": "https://github.com/doctrine/persistence/tree/4.1.0" + "source": "https://github.com/doctrine/persistence/tree/4.2.0" }, "funding": [ { @@ -1358,30 +1364,30 @@ "type": "tidelift" } ], - "time": "2025-08-21T16:00:31+00:00" + "time": "2026-04-26T12:12:52+00:00" }, { "name": "doctrine/sql-formatter", - "version": "1.5.2", + "version": "1.5.4", "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "d6d00aba6fd2957fe5216fe2b7673e9985db20c8" + "reference": "9563949f5cd3bd12a17d12fb980528bc141c5806" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/d6d00aba6fd2957fe5216fe2b7673e9985db20c8", - "reference": "d6d00aba6fd2957fe5216fe2b7673e9985db20c8", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/9563949f5cd3bd12a17d12fb980528bc141c5806", + "reference": "9563949f5cd3bd12a17d12fb980528bc141c5806", "shasum": "" }, "require": { "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^12", - "ergebnis/phpunit-slow-test-detector": "^2.14", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5" + "doctrine/coding-standard": "^14", + "ergebnis/phpunit-slow-test-detector": "^2.20", + "phpstan/phpstan": "^2.1.31", + "phpunit/phpunit": "^10.5.58" }, "bin": [ "bin/sql-formatter" @@ -1411,76 +1417,81 @@ ], "support": { "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.5.2" + "source": "https://github.com/doctrine/sql-formatter/tree/1.5.4" }, - "time": "2025-01-24T11:45:48+00:00" + "time": "2026-02-08T16:21:46+00:00" }, { "name": "easycorp/easyadmin-bundle", - "version": "v4.26.3", + "version": "v5.0.8", "source": { "type": "git", "url": "https://github.com/EasyCorp/EasyAdminBundle.git", - "reference": "fed89754c7532ce5592fda43921d810b59e24c4c" + "reference": "0feafc61cd7a2b990c2d7280fd7a450696a7fe92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/EasyCorp/EasyAdminBundle/zipball/fed89754c7532ce5592fda43921d810b59e24c4c", - "reference": "fed89754c7532ce5592fda43921d810b59e24c4c", + "url": "https://api.github.com/repos/EasyCorp/EasyAdminBundle/zipball/0feafc61cd7a2b990c2d7280fd7a450696a7fe92", + "reference": "0feafc61cd7a2b990c2d7280fd7a450696a7fe92", "shasum": "" }, "require": { - "doctrine/doctrine-bundle": "^2.5", - "doctrine/orm": "^2.12|^3.0", - "ext-json": "*", - "php": ">=8.1", - "symfony/asset": "^5.4|^6.0|^7.0", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "doctrine/dbal": "^3.10|^4.4", + "doctrine/doctrine-bundle": "^2.18|^3.2", + "doctrine/orm": "^2.20|^3.6", + "php": ">=8.2", + "symfony/asset": "^6.4|^7.0|^8.0", + "symfony/cache": "^6.4.33|^7.0|^8.0", + "symfony/config": "^6.4.32|^7.0|^8.0", + "symfony/dependency-injection": "^6.4.32|^7.0|^8.0", "symfony/deprecation-contracts": "^3.0", - "symfony/doctrine-bridge": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/filesystem": "^5.4|^6.0|^7.0", - "symfony/form": "^5.4|^6.0|^7.0", - "symfony/framework-bundle": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/intl": "^5.4|^6.0|^7.0", - "symfony/property-access": "^5.4|^6.0|^7.0", - "symfony/security-bundle": "^5.4|^6.0|^7.0", - "symfony/string": "^5.4|^6.0|^7.0", - "symfony/translation": "^5.4|^6.0|^7.0", - "symfony/twig-bridge": "^5.4.48|^6.4.16|^7.1.9", - "symfony/twig-bundle": "^5.4|^6.0|^7.0", - "symfony/uid": "^5.4|^6.0|^7.0", - "symfony/ux-twig-component": "^2.21", - "symfony/validator": "^5.4|^6.0|^7.0", - "twig/extra-bundle": "^3.17", - "twig/html-extra": "^3.17", - "twig/twig": "^3.20" - }, - "require-dev": { - "doctrine/doctrine-fixtures-bundle": "^3.4|3.5.x-dev", + "symfony/doctrine-bridge": "^6.4.32|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4.32|^7.0|^8.0", + "symfony/filesystem": "^6.4.30|^7.0|^8.0", + "symfony/form": "^6.4.32|^7.0|^8.0", + "symfony/framework-bundle": "^6.4.33|^7.0|^8.0", + "symfony/http-foundation": "^6.4.33|^7.0|^8.0", + "symfony/http-kernel": "^6.4.33|^7.0|^8.0", + "symfony/intl": "^6.4.32|^7.0|^8.0", + "symfony/property-access": "^6.4.32|^7.0|^8.0", + "symfony/security-bundle": "^6.4.32|^7.0|^8.0", + "symfony/string": "^6.4.30|^7.0|^8.0", + "symfony/translation": "^6.4.32|^7.0|^8.0", + "symfony/twig-bridge": "^6.4.32|^7.1.9|^7.2|^8.0", + "symfony/twig-bundle": "^6.4.32|^7.0|^8.0", + "symfony/uid": "^6.4.32|^7.0|^8.0", + "symfony/ux-twig-component": "^2.32|^3.0", + "symfony/validator": "^6.4.33|^7.0|^8.0", + "twig/extra-bundle": "^3.23", + "twig/html-extra": "^3.23", + "twig/twig": "^3.23" + }, + "require-dev": { + "dama/doctrine-test-bundle": "^8.2", + "doctrine/doctrine-fixtures-bundle": "^3.4|3.5.x-dev|^4.0", + "league/flysystem": "^3.10", + "moneyphp/money": "^4.8", "phpstan/extension-installer": "^1.4", "phpstan/phpstan": "^2.0", "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", "phpstan/phpstan-symfony": "^2.0", - "psr/log": "^1.0", - "symfony/browser-kit": "^5.4|^6.0|^7.0", - "symfony/css-selector": "^5.4|^6.0|^7.0", - "symfony/debug-bundle": "^5.4|^6.0|^7.0", - "symfony/dom-crawler": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/phpunit-bridge": "^6.1|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/web-link": "^5.4|^6.0|^7.0" + "psr/log": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^6.4.32|^7.0|^8.0", + "symfony/css-selector": "^6.4.24|^7.0|^8.0", + "symfony/debug-bundle": "^6.4.27|^7.0|^8.0", + "symfony/dom-crawler": "^6.4.32|^7.0|^8.0", + "symfony/expression-language": "^6.4.32|^7.0|^8.0", + "symfony/phpunit-bridge": "^6.4.26|^7.0|^8.0", + "symfony/process": "^6.4.33|^7.0|^8.0", + "symfony/web-link": "^6.4.32|^7.0|^8.0", + "vincentlanglet/twig-cs-fixer": "^3.10", + "zenstruck/foundry": "^2.3" }, "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-master": "4.0.x-dev" + "dev-master": "5.0.x-dev" } }, "autoload": { @@ -1507,7 +1518,7 @@ ], "support": { "issues": "https://github.com/EasyCorp/EasyAdminBundle/issues", - "source": "https://github.com/EasyCorp/EasyAdminBundle/tree/v4.26.3" + "source": "https://github.com/EasyCorp/EasyAdminBundle/tree/v5.0.8" }, "funding": [ { @@ -1515,20 +1526,20 @@ "type": "github" } ], - "time": "2025-10-08T18:21:48+00:00" + "time": "2026-05-13T17:18:33+00:00" }, { "name": "firebase/php-jwt", - "version": "v6.11.1", + "version": "v7.0.5", "source": { "type": "git", - "url": "https://github.com/firebase/php-jwt.git", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + "url": "https://github.com/googleapis/php-jwt.git", + "reference": "47ad26bab5e7c70ae8a6f08ed25ff83631121380" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/47ad26bab5e7c70ae8a6f08ed25ff83631121380", + "reference": "47ad26bab5e7c70ae8a6f08ed25ff83631121380", "shasum": "" }, "require": { @@ -1536,6 +1547,7 @@ }, "require-dev": { "guzzlehttp/guzzle": "^7.4", + "phpfastcache/phpfastcache": "^9.2", "phpspec/prophecy-phpunit": "^2.0", "phpunit/phpunit": "^9.5", "psr/cache": "^2.0||^3.0", @@ -1575,10 +1587,10 @@ "php" ], "support": { - "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + "issues": "https://github.com/googleapis/php-jwt/issues", + "source": "https://github.com/googleapis/php-jwt/tree/v7.0.5" }, - "time": "2025-04-09T20:32:01+00:00" + "time": "2026-04-01T20:38:03+00:00" }, { "name": "guzzlehttp/guzzle", @@ -1791,16 +1803,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.8.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "21dc724a0583619cd1652f673303492272778051" + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", - "reference": "21dc724a0583619cd1652f673303492272778051", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", "shasum": "" }, "require": { @@ -1816,6 +1828,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", + "jshttp/mime-db": "1.54.0.1", "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { @@ -1887,7 +1900,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.8.0" + "source": "https://github.com/guzzle/psr7/tree/2.9.0" }, "funding": [ { @@ -1903,39 +1916,39 @@ "type": "tidelift" } ], - "time": "2025-08-23T21:21:41+00:00" + "time": "2026-03-10T16:41:02+00:00" }, { "name": "itk-dev/openid-connect", - "version": "4.0.2", + "version": "4.1.2", "source": { "type": "git", "url": "https://github.com/itk-dev/openid-connect.git", - "reference": "65951585ef2680432b33391fadffb1f87d210d27" + "reference": "f19b9a39e7f1beae231d0b06e5b7a78a9efd8eb5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/itk-dev/openid-connect/zipball/65951585ef2680432b33391fadffb1f87d210d27", - "reference": "65951585ef2680432b33391fadffb1f87d210d27", + "url": "https://api.github.com/repos/itk-dev/openid-connect/zipball/f19b9a39e7f1beae231d0b06e5b7a78a9efd8eb5", + "reference": "f19b9a39e7f1beae231d0b06e5b7a78a9efd8eb5", "shasum": "" }, "require": { "ext-json": "*", "ext-openssl": "*", - "firebase/php-jwt": "^6.8", + "firebase/php-jwt": "^7.0", "league/oauth2-client": "^2.6", "php": "^8.3", "psr/cache": "^2.0 || ^3.0", "psr/http-client": "^1.0", - "robrichards/xmlseclibs": "^3.1" + "robrichards/xmlseclibs": "^3.1.5" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "escapestudios/symfony2-coding-standard": "^3.12", - "mockery/mockery": "^1.4", - "phpstan/phpstan": "^2.1", - "phpunit/php-code-coverage": "^11.0", - "phpunit/phpunit": "^11.0" + "ergebnis/composer-normalize": "^2.50", + "friendsofphp/php-cs-fixer": "^3.75", + "mockery/mockery": "^1.6.12", + "phpstan/phpstan": "^2.1.40", + "phpunit/php-code-coverage": "^12", + "phpunit/phpunit": "^12" }, "type": "library", "autoload": { @@ -1964,45 +1977,43 @@ "description": "OpenID connect configuration package", "support": { "issues": "https://github.com/itk-dev/openid-connect/issues", - "source": "https://github.com/itk-dev/openid-connect/tree/4.0.2" + "source": "https://github.com/itk-dev/openid-connect/tree/4.1.2" }, - "time": "2025-10-06T07:25:31+00:00" + "time": "2026-05-11T12:03:06+00:00" }, { "name": "itk-dev/openid-connect-bundle", - "version": "4.0.1", + "version": "4.2.0", "source": { "type": "git", "url": "https://github.com/itk-dev/openid-connect-bundle.git", - "reference": "be2dff91f114ba26926b5d59e9db3632cf809e7d" + "reference": "7964acbd630ba0a6b4a362f714bfed255ad88e05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/itk-dev/openid-connect-bundle/zipball/be2dff91f114ba26926b5d59e9db3632cf809e7d", - "reference": "be2dff91f114ba26926b5d59e9db3632cf809e7d", + "url": "https://api.github.com/repos/itk-dev/openid-connect-bundle/zipball/7964acbd630ba0a6b4a362f714bfed255ad88e05", + "reference": "7964acbd630ba0a6b4a362f714bfed255ad88e05", "shasum": "" }, "require": { - "doctrine/orm": "^2.8|^3.0", + "doctrine/orm": "^2.8 || ^3.0", "ext-json": "*", "ext-openssl": "*", "itk-dev/openid-connect": "^4.0", "php": "^8.3", - "symfony/cache": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4.13|^7.0", - "symfony/security-bundle": "^6.4.13|^7.0", - "symfony/uid": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/cache": "^6.4 || ^7.0 || ^8.0", + "symfony/framework-bundle": "^6.4.13 || ^7.0 || ^8.0", + "symfony/security-bundle": "^6.4.13 || ^7.0 || ^8.0", + "symfony/uid": "^6.4 || ^7.0 || ^8.0", + "symfony/yaml": "^6.4 || ^7.0 || ^8.0" }, "require-dev": { "ergebnis/composer-normalize": "^2.28", - "escapestudios/symfony2-coding-standard": "^3.12", "friendsofphp/php-cs-fixer": "^3.11", - "kubawerlos/php-cs-fixer-custom-fixers": "^3.11", "phpstan/phpstan": "^2.1", - "phpunit/phpunit": "^11.0", + "phpunit/phpunit": "^12.0", "rector/rector": "^2.0", - "symfony/runtime": "^6.4.13|^7.0" + "symfony/runtime": "^6.4.13 || ^7.0 || ^8.0" }, "type": "symfony-bundle", "autoload": { @@ -2027,9 +2038,9 @@ "description": "Symfony bundle for openid-connect", "support": { "issues": "https://github.com/itk-dev/openid-connect-bundle/issues", - "source": "https://github.com/itk-dev/openid-connect-bundle/tree/4.0.1" + "source": "https://github.com/itk-dev/openid-connect-bundle/tree/4.2.0" }, - "time": "2025-01-16T21:14:01+00:00" + "time": "2026-05-11T12:11:09+00:00" }, { "name": "itk-dev/vault", @@ -2089,26 +2100,26 @@ }, { "name": "itk-dev/vault-bundle", - "version": "0.1.2", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/itk-dev/vault-bundle.git", - "reference": "948e1fc069b0ee543790478dadc504f82bb1de73" + "reference": "9ce87b4098b3213f8744557593772bf6473c7fa9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/itk-dev/vault-bundle/zipball/948e1fc069b0ee543790478dadc504f82bb1de73", - "reference": "948e1fc069b0ee543790478dadc504f82bb1de73", + "url": "https://api.github.com/repos/itk-dev/vault-bundle/zipball/9ce87b4098b3213f8744557593772bf6473c7fa9", + "reference": "9ce87b4098b3213f8744557593772bf6473c7fa9", "shasum": "" }, "require": { "itk-dev/vault": "^0.1.0", "nyholm/psr7": "^1.8", "php": "^8.0", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" + "symfony/config": "^6.4 || ^7.0 || ^8.0", + "symfony/dependency-injection": "^6.4 || ^7.0 || ^8.0", + "symfony/http-client": "^6.4 || ^7.0 || ^8.0", + "symfony/http-kernel": "^6.4 || ^7.0 || ^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.64" @@ -2134,28 +2145,28 @@ "description": "Symfony bundle for HashiCorp Vault", "support": { "issues": "https://github.com/itk-dev/vault-bundle/issues", - "source": "https://github.com/itk-dev/vault-bundle/tree/0.1.2" + "source": "https://github.com/itk-dev/vault-bundle/tree/1.0.0" }, - "time": "2025-01-14T08:16:08+00:00" + "time": "2026-03-25T09:34:40+00:00" }, { "name": "league/oauth2-client", - "version": "2.8.1", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth2-client.git", - "reference": "9df2924ca644736c835fc60466a3a60390d334f9" + "reference": "26e8c5da4f3d78cede7021e09b1330a0fc093d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9", - "reference": "9df2924ca644736c835fc60466a3a60390d334f9", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/26e8c5da4f3d78cede7021e09b1330a0fc093d5e", + "reference": "26e8c5da4f3d78cede7021e09b1330a0fc093d5e", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", - "php": "^7.1 || >=8.0.0 <8.5.0" + "php": "^7.1 || >=8.0.0 <8.6.0" }, "require-dev": { "mockery/mockery": "^1.3.5", @@ -2199,89 +2210,22 @@ ], "support": { "issues": "https://github.com/thephpleague/oauth2-client/issues", - "source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1" - }, - "time": "2025-02-26T04:37:30+00:00" - }, - { - "name": "masterminds/html5", - "version": "2.10.0", - "source": { - "type": "git", - "url": "https://github.com/Masterminds/html5-php.git", - "reference": "fcf91eb64359852f00d921887b219479b4f21251" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", - "reference": "fcf91eb64359852f00d921887b219479b4f21251", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Masterminds\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matt Butcher", - "email": "technosophos@gmail.com" - }, - { - "name": "Matt Farina", - "email": "matt@mattfarina.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - } - ], - "description": "An HTML5 parser and serializer.", - "homepage": "http://masterminds.github.io/html5-php", - "keywords": [ - "HTML5", - "dom", - "html", - "parser", - "querypath", - "serializer", - "xml" - ], - "support": { - "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" + "source": "https://github.com/thephpleague/oauth2-client/tree/2.9.0" }, - "time": "2025-07-25T09:04:22+00:00" + "time": "2025-11-25T22:17:17+00:00" }, { "name": "monolog/monolog", - "version": "3.9.0", + "version": "3.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", - "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", "shasum": "" }, "require": { @@ -2299,7 +2243,7 @@ "graylog2/gelf-php": "^1.4.2 || ^2.0", "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8", + "mongodb/mongodb": "^1.8 || ^2.0", "php-amqplib/php-amqplib": "~2.4 || ^3", "php-console/php-console": "^3.1.8", "phpstan/phpstan": "^2", @@ -2359,7 +2303,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" }, "funding": [ { @@ -2371,29 +2315,32 @@ "type": "tidelift" } ], - "time": "2025-03-24T10:02:05+00:00" + "time": "2026-01-02T08:56:05+00:00" }, { "name": "nelmio/cors-bundle", - "version": "2.5.0", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/nelmio/NelmioCorsBundle.git", - "reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544" + "reference": "3d80dbcd5d1eb5f8b20ed5199e1778d44c2e4d1c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/3a526fe025cd20e04a6a11370cf5ab28dbb5a544", - "reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544", + "url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/3d80dbcd5d1eb5f8b20ed5199e1778d44c2e4d1c", + "reference": "3d80dbcd5d1eb5f8b20ed5199e1778d44c2e4d1c", "shasum": "" }, "require": { "psr/log": "^1.0 || ^2.0 || ^3.0", - "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { - "mockery/mockery": "^1.3.6", - "symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0" + "phpstan/phpstan": "^1.11.5", + "phpstan/phpstan-deprecation-rules": "^1.2.0", + "phpstan/phpstan-phpunit": "^1.4", + "phpstan/phpstan-symfony": "^1.4.4", + "phpunit/phpunit": "^8" }, "type": "symfony-bundle", "extra": { @@ -2431,9 +2378,9 @@ ], "support": { "issues": "https://github.com/nelmio/NelmioCorsBundle/issues", - "source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.5.0" + "source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.6.1" }, - "time": "2024-06-24T21:25:28+00:00" + "time": "2026-01-12T15:59:08+00:00" }, { "name": "nyholm/psr7", @@ -2515,30 +2462,30 @@ }, { "name": "ocramius/doctrine-batch-utils", - "version": "2.11.0", + "version": "2.12.0", "source": { "type": "git", "url": "https://github.com/Ocramius/DoctrineBatchUtils.git", - "reference": "35b164ea216da00ecccde6a0152f8be67bfb841f" + "reference": "a0a3a3b09f5c52088712398354b75e251cab90cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Ocramius/DoctrineBatchUtils/zipball/35b164ea216da00ecccde6a0152f8be67bfb841f", - "reference": "35b164ea216da00ecccde6a0152f8be67bfb841f", + "url": "https://api.github.com/repos/Ocramius/DoctrineBatchUtils/zipball/a0a3a3b09f5c52088712398354b75e251cab90cc", + "reference": "a0a3a3b09f5c52088712398354b75e251cab90cc", "shasum": "" }, "require": { - "doctrine/orm": "^3.3.2", - "doctrine/persistence": "^4.0.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0" + "doctrine/orm": "^3.5.8", + "doctrine/persistence": "^4.1.1", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { - "doctrine/coding-standard": "^12.0.0", - "doctrine/dbal": "^4.2.2", - "phpunit/phpunit": "^11.5.6", - "psalm/plugin-phpunit": "^0.19.2", - "roave/infection-static-analysis-plugin": "^1.36.0", - "vimeo/psalm": "^6.3.0" + "doctrine/coding-standard": "^14.0.0", + "doctrine/dbal": "^4.4.1", + "phpunit/phpunit": "^12.5.3", + "psalm/plugin-phpunit": "^0.19.5", + "roave/infection-static-analysis-plugin": "^1.42.0", + "vimeo/psalm": "^6.14.2" }, "type": "library", "autoload": { @@ -2567,7 +2514,7 @@ ], "support": { "issues": "https://github.com/Ocramius/DoctrineBatchUtils/issues", - "source": "https://github.com/Ocramius/DoctrineBatchUtils/tree/2.11.0" + "source": "https://github.com/Ocramius/DoctrineBatchUtils/tree/2.12.0" }, "funding": [ { @@ -2575,195 +2522,20 @@ "type": "github" } ], - "time": "2025-02-05T12:04:35+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.6.3", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.1", - "ext-filter": "*", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7|^2.0", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.5 || ~1.6.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-webmozart-assert": "^1.2", - "phpunit/phpunit": "^9.5", - "psalm/phar": "^5.26" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3" - }, - "time": "2025-08-01T19:43:32+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.18|^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" - }, - "time": "2024-11-09T15:12:26+00:00" + "time": "2025-12-12T14:45:53+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "2.3.0", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", "shasum": "" }, "require": { @@ -2795,9 +2567,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2025-08-30T15:50:23+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { "name": "psr/cache", @@ -3362,16 +3134,16 @@ }, { "name": "robrichards/xmlseclibs", - "version": "3.1.3", + "version": "3.1.5", "source": { "type": "git", "url": "https://github.com/robrichards/xmlseclibs.git", - "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07" + "reference": "03062be78178cbb5e8f605cd255dc32a14981f92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/2bdfd742624d739dfadbd415f00181b4a77aaf07", - "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07", + "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/03062be78178cbb5e8f605cd255dc32a14981f92", + "reference": "03062be78178cbb5e8f605cd255dc32a14981f92", "shasum": "" }, "require": { @@ -3398,34 +3170,34 @@ ], "support": { "issues": "https://github.com/robrichards/xmlseclibs/issues", - "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.3" + "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.5" }, - "time": "2024-11-20T21:13:56+00:00" + "time": "2026-03-13T10:31:56+00:00" }, { "name": "symfony/amqp-messenger", - "version": "v7.3.2", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/amqp-messenger.git", - "reference": "0ed5f72c1d9bbfcfc751b3832939a00a3246fe98" + "reference": "26e1eec5f9aa5cd8dd43d02cc80de468c0fa480e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/amqp-messenger/zipball/0ed5f72c1d9bbfcfc751b3832939a00a3246fe98", - "reference": "0ed5f72c1d9bbfcfc751b3832939a00a3246fe98", + "url": "https://api.github.com/repos/symfony/amqp-messenger/zipball/26e1eec5f9aa5cd8dd43d02cc80de468c0fa480e", + "reference": "26e1eec5f9aa5cd8dd43d02cc80de468c0fa480e", "shasum": "" }, "require": { "ext-amqp": "*", - "php": ">=8.2", - "symfony/messenger": "^7.3" + "php": ">=8.4", + "symfony/messenger": "^7.4|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" }, "type": "symfony-messenger-bridge", "autoload": { @@ -3453,7 +3225,7 @@ "description": "Symfony AMQP extension Messenger Bridge", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/amqp-messenger/tree/v7.3.2" + "source": "https://github.com/symfony/amqp-messenger/tree/v8.0.11" }, "funding": [ { @@ -3473,32 +3245,29 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "symfony/asset", - "version": "v7.3.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/asset.git", - "reference": "56c4d9f759247c4e07d8549e3baf7493cb9c3e4b" + "reference": "72eca261f3af1bef741c48bb2c91a4e619dca03a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/asset/zipball/56c4d9f759247c4e07d8549e3baf7493cb9c3e4b", - "reference": "56c4d9f759247c4e07d8549e3baf7493cb9c3e4b", + "url": "https://api.github.com/repos/symfony/asset/zipball/72eca261f3af1bef741c48bb2c91a4e619dca03a", + "reference": "72eca261f3af1bef741c48bb2c91a4e619dca03a", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "conflict": { - "symfony/http-foundation": "<6.4" + "php": ">=8.4" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" + "symfony/http-client": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3526,7 +3295,7 @@ "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/asset/tree/v7.3.0" + "source": "https://github.com/symfony/asset/tree/v8.0.8" }, "funding": [ { @@ -3537,48 +3306,49 @@ "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-03-05T10:15:41+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/asset-mapper", - "version": "v7.3.4", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/asset-mapper.git", - "reference": "0c40c579e27244616cf0fe4d1759aab2ceb99a9a" + "reference": "b2c33bf6934bfe5b37a6d70d0b0f7011d0ec4a0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/asset-mapper/zipball/0c40c579e27244616cf0fe4d1759aab2ceb99a9a", - "reference": "0c40c579e27244616cf0fe4d1759aab2ceb99a9a", + "url": "https://api.github.com/repos/symfony/asset-mapper/zipball/b2c33bf6934bfe5b37a6d70d0b0f7011d0ec4a0c", + "reference": "b2c33bf6934bfe5b37a6d70d0b0f7011d0ec4a0c", "shasum": "" }, "require": { "composer/semver": "^3.0", - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^7.1", - "symfony/http-client": "^6.4|^7.0" - }, - "conflict": { - "symfony/framework-bundle": "<6.4" + "php": ">=8.4", + "symfony/filesystem": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0" }, "require-dev": { - "symfony/asset": "^6.4|^7.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", + "symfony/asset": "^7.4|^8.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", "symfony/event-dispatcher-contracts": "^3.0", - "symfony/finder": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0" + "symfony/finder": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0", + "symfony/web-link": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3606,7 +3376,7 @@ "description": "Maps directories of assets & makes them available in a public directory with versioned filenames.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/asset-mapper/tree/v7.3.4" + "source": "https://github.com/symfony/asset-mapper/tree/v8.0.11" }, "funding": [ { @@ -3626,31 +3396,31 @@ "type": "tidelift" } ], - "time": "2025-09-22T15:31:00+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "symfony/browser-kit", - "version": "v7.3.2", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "f0b889b73a845cddef1d25fe207b37fd04cb5419" + "reference": "f5a28fca785416cf489dd579011e74c831100cc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/f0b889b73a845cddef1d25fe207b37fd04cb5419", - "reference": "f0b889b73a845cddef1d25fe207b37fd04cb5419", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/f5a28fca785416cf489dd579011e74c831100cc3", + "reference": "f5a28fca785416cf489dd579011e74c831100cc3", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/dom-crawler": "^6.4|^7.0" + "php": ">=8.4", + "symfony/dom-crawler": "^7.4|^8.0" }, "require-dev": { - "symfony/css-selector": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0" + "symfony/css-selector": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3678,7 +3448,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v7.3.2" + "source": "https://github.com/symfony/browser-kit/tree/v8.0.8" }, "funding": [ { @@ -3698,36 +3468,33 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/cache", - "version": "v7.3.4", + "version": "v8.0.10", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "bf8afc8ffd4bfd3d9c373e417f041d9f1e5b863f" + "reference": "8ff96cde73684bfa32b702f5cff1eb83b1fac429" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/bf8afc8ffd4bfd3d9c373e417f041d9f1e5b863f", - "reference": "bf8afc8ffd4bfd3d9c373e417f041d9f1e5b863f", + "url": "https://api.github.com/repos/symfony/cache/zipball/8ff96cde73684bfa32b702f5cff1eb83b1fac429", + "reference": "8ff96cde73684bfa32b702f5cff1eb83b1fac429", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/cache": "^2.0|^3.0", "psr/log": "^1.1|^2|^3", "symfony/cache-contracts": "^3.6", - "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/service-contracts": "^2.5|^3", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^7.4|^8.0" }, "conflict": { - "doctrine/dbal": "<3.6", - "symfony/dependency-injection": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/var-dumper": "<6.4" + "ext-redis": "<6.1", + "ext-relay": "<0.12.1" }, "provide": { "psr/cache-implementation": "2.0|3.0", @@ -3736,16 +3503,16 @@ }, "require-dev": { "cache/integration-tests": "dev-master", - "doctrine/dbal": "^3.6|^4", + "doctrine/dbal": "^4.3", "predis/predis": "^1.1|^2.0", "psr/simple-cache": "^1.0|^2.0|^3.0", - "symfony/clock": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/filesystem": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/filesystem": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3780,7 +3547,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.3.4" + "source": "https://github.com/symfony/cache/tree/v8.0.10" }, "funding": [ { @@ -3800,20 +3567,20 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-05-05T08:24:00+00:00" }, { "name": "symfony/cache-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868" + "reference": "225e8a254166bd3442e370c6f50145465db63831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/5d68a57d66910405e5c0b63d6f0af941e66fc868", - "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/225e8a254166bd3442e370c6f50145465db63831", + "reference": "225e8a254166bd3442e370c6f50145465db63831", "shasum": "" }, "require": { @@ -3827,7 +3594,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -3860,7 +3627,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/cache-contracts/tree/v3.7.0" }, "funding": [ { @@ -3871,31 +3638,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-03-13T15:25:07+00:00" + "time": "2026-05-05T15:33:14+00:00" }, { "name": "symfony/clock", - "version": "v7.3.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "url": "https://api.github.com/repos/symfony/clock/zipball/b55a638b189a6faa875e0ccdb00908fb87af95b3", + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/clock": "^1.0", - "symfony/polyfill-php83": "^1.28" + "php": ">=8.4", + "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -3934,7 +3704,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.3.0" + "source": "https://github.com/symfony/clock/tree/v8.0.8" }, "funding": [ { @@ -3945,43 +3715,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": "2024-09-25T14:21:43+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/config", - "version": "v7.3.4", + "version": "v8.0.10", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "8a09223170046d2cfda3d2e11af01df2c641e961" + "reference": "de665e669412ec2effe004d90298dbbdaf6e7e8b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/8a09223170046d2cfda3d2e11af01df2c641e961", - "reference": "8a09223170046d2cfda3d2e11af01df2c641e961", + "url": "https://api.github.com/repos/symfony/config/zipball/de665e669412ec2effe004d90298dbbdaf6e7e8b", + "reference": "de665e669412ec2effe004d90298dbbdaf6e7e8b", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^7.1", - "symfony/polyfill-ctype": "~1.8" + "symfony/filesystem": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/finder": "<6.4", "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4009,7 +3782,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.3.4" + "source": "https://github.com/symfony/config/tree/v8.0.10" }, "funding": [ { @@ -4029,51 +3802,43 @@ "type": "tidelift" } ], - "time": "2025-09-22T12:46:16+00:00" + "time": "2026-05-04T13:41:39+00:00" }, { "name": "symfony/console", - "version": "v7.3.4", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" + "reference": "3156577f46a38aa1b9323aad223de7a9cd426782" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", - "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "url": "https://api.github.com/repos/symfony/console/zipball/3156577f46a38aa1b9323aad223de7a9cd426782", + "reference": "3156577f46a38aa1b9323aad223de7a9cd426782", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "symfony/string": "^7.4|^8.0" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4107,7 +3872,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.4" + "source": "https://github.com/symfony/console/tree/v8.0.11" }, "funding": [ { @@ -4127,43 +3892,40 @@ "type": "tidelift" } ], - "time": "2025-09-22T15:31:00+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.3.4", + "version": "v8.0.10", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "82119812ab0bf3425c1234d413efd1b19bb92ae4" + "reference": "6fc374dae45a7633a5865da7fc2908baf29d4900" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/82119812ab0bf3425c1234d413efd1b19bb92ae4", - "reference": "82119812ab0bf3425c1234d413efd1b19bb92ae4", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6fc374dae45a7633a5865da7fc2908baf29d4900", + "reference": "6fc374dae45a7633a5865da7fc2908baf29d4900", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/service-contracts": "^3.5", - "symfony/var-exporter": "^6.4.20|^7.2.5" + "symfony/service-contracts": "^3.6", + "symfony/var-exporter": "^7.4|^8.0" }, "conflict": { - "ext-psr": "<1.1|>=2", - "symfony/config": "<6.4", - "symfony/finder": "<6.4", - "symfony/yaml": "<6.4" + "ext-psr": "<1.1|>=2" }, "provide": { "psr/container-implementation": "1.1|2.0", "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/config": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4191,7 +3953,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.3.4" + "source": "https://github.com/symfony/dependency-injection/tree/v8.0.10" }, "funding": [ { @@ -4211,20 +3973,20 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-05-06T11:55:35+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" }, "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/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", "shasum": "" }, "require": { @@ -4237,7 +3999,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -4262,7 +4024,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.7.0" }, "funding": [ { @@ -4273,77 +4035,70 @@ "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-25T14:21:43+00:00" + "time": "2026-04-13T15:52:40+00:00" }, { "name": "symfony/doctrine-bridge", - "version": "v7.3.4", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "21cd48c34a47a0d0e303a590a67c3450fde55888" + "reference": "dfe3dddc9c22756b9b145785fb5fd4b0445cd06e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/21cd48c34a47a0d0e303a590a67c3450fde55888", - "reference": "21cd48c34a47a0d0e303a590a67c3450fde55888", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/dfe3dddc9c22756b9b145785fb5fd4b0445cd06e", + "reference": "dfe3dddc9c22756b9b145785fb5fd4b0445cd06e", "shasum": "" }, "require": { "doctrine/event-manager": "^2", "doctrine/persistence": "^3.1|^4", - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { "doctrine/collections": "<1.8", - "doctrine/dbal": "<3.6", + "doctrine/dbal": "<4.3", "doctrine/lexer": "<1.1", - "doctrine/orm": "<2.15", - "symfony/cache": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/form": "<6.4.6|>=7,<7.0.6", - "symfony/http-foundation": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/lock": "<6.4", - "symfony/messenger": "<6.4", - "symfony/property-info": "<6.4", - "symfony/security-bundle": "<6.4", - "symfony/security-core": "<6.4", - "symfony/validator": "<6.4" + "doctrine/orm": "<3.4", + "symfony/property-info": "<8.0" }, "require-dev": { "doctrine/collections": "^1.8|^2.0", "doctrine/data-fixtures": "^1.1|^2", - "doctrine/dbal": "^3.6|^4", - "doctrine/orm": "^2.15|^3", + "doctrine/dbal": "^4.3", + "doctrine/orm": "^3.4", "psr/log": "^1|^2|^3", - "symfony/cache": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/doctrine-messenger": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/form": "^6.4.6|^7.0.6", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/type-info": "^7.1.8", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/cache": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/doctrine-messenger": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/form": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/property-info": "^8.0", + "symfony/security-core": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/type-info": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "symfony-bridge", "autoload": { @@ -4371,7 +4126,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v7.3.4" + "source": "https://github.com/symfony/doctrine-bridge/tree/v8.0.9" }, "funding": [ { @@ -4391,26 +4146,26 @@ "type": "tidelift" } ], - "time": "2025-09-24T09:56:23+00:00" + "time": "2026-04-29T15:02:55+00:00" }, { "name": "symfony/doctrine-messenger", - "version": "v7.3.4", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-messenger.git", - "reference": "064159484ab330590b7b477f6c8835812f2e340f" + "reference": "88329a3faba5023cfb569b3fc5b8a771336c4a88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/064159484ab330590b7b477f6c8835812f2e340f", - "reference": "064159484ab330590b7b477f6c8835812f2e340f", + "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/88329a3faba5023cfb569b3fc5b8a771336c4a88", + "reference": "88329a3faba5023cfb569b3fc5b8a771336c4a88", "shasum": "" }, "require": { - "doctrine/dbal": "^3.6|^4", - "php": ">=8.2", - "symfony/messenger": "^7.2", + "doctrine/dbal": "^4.3", + "php": ">=8.4", + "symfony/messenger": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -4418,8 +4173,8 @@ }, "require-dev": { "doctrine/persistence": "^1.3|^2|^3", - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" }, "type": "symfony-messenger-bridge", "autoload": { @@ -4447,7 +4202,7 @@ "description": "Symfony Doctrine Messenger Bridge", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-messenger/tree/v7.3.4" + "source": "https://github.com/symfony/doctrine-messenger/tree/v8.0.6" }, "funding": [ { @@ -4467,30 +4222,29 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-02-20T07:51:53+00:00" }, { "name": "symfony/dom-crawler", - "version": "v7.3.3", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "efa076ea0eeff504383ff0dcf827ea5ce15690ba" + "reference": "284ace90732b445b027728b5e0eec6418a17a364" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/efa076ea0eeff504383ff0dcf827ea5ce15690ba", - "reference": "efa076ea0eeff504383ff0dcf827ea5ce15690ba", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/284ace90732b445b027728b5e0eec6418a17a364", + "reference": "284ace90732b445b027728b5e0eec6418a17a364", "shasum": "" }, "require": { - "masterminds/html5": "^2.6", - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.0" }, "require-dev": { - "symfony/css-selector": "^6.4|^7.0" + "symfony/css-selector": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4518,7 +4272,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.3.3" + "source": "https://github.com/symfony/dom-crawler/tree/v8.0.8" }, "funding": [ { @@ -4538,32 +4292,28 @@ "type": "tidelift" } ], - "time": "2025-08-06T20:13:54+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/dotenv", - "version": "v7.3.2", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "2192790a11f9e22cbcf9dc705a3ff22a5503923a" + "reference": "82e1d8f888896a215bb6673e6d1f6d5ca47a9dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/2192790a11f9e22cbcf9dc705a3ff22a5503923a", - "reference": "2192790a11f9e22cbcf9dc705a3ff22a5503923a", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/82e1d8f888896a215bb6673e6d1f6d5ca47a9dfe", + "reference": "82e1d8f888896a215bb6673e6d1f6d5ca47a9dfe", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "conflict": { - "symfony/console": "<6.4", - "symfony/process": "<6.4" + "php": ">=8.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0" + "symfony/console": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4596,7 +4346,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v7.3.2" + "source": "https://github.com/symfony/dotenv/tree/v8.0.11" }, "funding": [ { @@ -4616,36 +4366,36 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:29:33+00:00" + "time": "2026-05-11T13:06:45+00:00" }, { "name": "symfony/error-handler", - "version": "v7.3.4", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4" + "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/99f81bc944ab8e5dae4f21b4ca9972698bbad0e4", - "reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c1119fe8dcfc3825ec74ec061b96ef0c8f281517", + "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^7.4|^8.0" }, "conflict": { - "symfony/deprecation-contracts": "<2.5", - "symfony/http-kernel": "<6.4" + "symfony/deprecation-contracts": "<2.5" }, "require-dev": { - "symfony/console": "^6.4|^7.0", + "symfony/console": "^7.4|^8.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ @@ -4677,7 +4427,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.4" + "source": "https://github.com/symfony/error-handler/tree/v8.0.8" }, "funding": [ { @@ -4697,28 +4447,28 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.3.3", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", - "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0c3c1a17604c4dbbec4b93fe162c538482096e1f", + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/security-http": "<7.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -4727,13 +4477,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "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/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4761,7 +4512,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.3.3" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.9" }, "funding": [ { @@ -4781,20 +4532,20 @@ "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" }, "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/ccba7060602b7fed0b03c85bf025257f76d9ef32", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", "shasum": "" }, "require": { @@ -4808,7 +4559,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -4841,7 +4592,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.7.0" }, "funding": [ { @@ -4852,31 +4603,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": "2024-09-25T14:21:43+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/expression-language", - "version": "v7.3.2", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/expression-language.git", - "reference": "32d2d19c62e58767e6552166c32fb259975d2b23" + "reference": "b2a5fd3b7331ae10cc0ed75a28d64b25b67d2c7b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/32d2d19c62e58767e6552166c32fb259975d2b23", - "reference": "32d2d19c62e58767e6552166c32fb259975d2b23", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/b2a5fd3b7331ae10cc0ed75a28d64b25b67d2c7b", + "reference": "b2a5fd3b7331ae10cc0ed75a28d64b25b67d2c7b", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/cache": "^6.4|^7.0", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", + "symfony/cache": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, "type": "library", @@ -4905,7 +4659,7 @@ "description": "Provides an engine that can compile and evaluate expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/expression-language/tree/v7.3.2" + "source": "https://github.com/symfony/expression-language/tree/v8.0.8" }, "funding": [ { @@ -4925,29 +4679,29 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:29:33+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/filesystem", - "version": "v7.3.2", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" + "reference": "224db910898ce1317b892a9a1338f1f8f17eb7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", - "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/224db910898ce1317b892a9a1338f1f8f17eb7c7", + "reference": "224db910898ce1317b892a9a1338f1f8f17eb7c7", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^6.4|^7.0" + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4975,7 +4729,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.2" + "source": "https://github.com/symfony/filesystem/tree/v8.0.11" }, "funding": [ { @@ -4995,27 +4749,27 @@ "type": "tidelift" } ], - "time": "2025-07-07T08:17:47+00:00" + "time": "2026-05-11T16:39:47+00:00" }, { "name": "symfony/finder", - "version": "v7.3.2", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" + "reference": "8da41214757b87d97f181e3d14a4179286151007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", - "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", + "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", + "reference": "8da41214757b87d97f181e3d14a4179286151007", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5043,7 +4797,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.2" + "source": "https://github.com/symfony/finder/tree/v8.0.8" }, "funding": [ { @@ -5063,35 +4817,36 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/flex", - "version": "v2.8.2", + "version": "v2.10.0", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "f356aa35f3cf3d2f46c31d344c1098eb2d260426" + "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/f356aa35f3cf3d2f46c31d344c1098eb2d260426", - "reference": "f356aa35f3cf3d2f46c31d344c1098eb2d260426", + "url": "https://api.github.com/repos/symfony/flex/zipball/9cd384775973eabbf6e8b05784dda279fc67c28d", + "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d", "shasum": "" }, "require": { "composer-plugin-api": "^2.1", - "php": ">=8.0" + "php": ">=8.1" }, "conflict": { - "composer/semver": "<1.7.2" + "composer/semver": "<1.7.2", + "symfony/dotenv": "<5.4" }, "require-dev": { "composer/composer": "^2.1", - "symfony/dotenv": "^5.4|^6.0", - "symfony/filesystem": "^5.4|^6.0", - "symfony/phpunit-bridge": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0" + "symfony/dotenv": "^6.4|^7.4|^8.0", + "symfony/filesystem": "^6.4|^7.4|^8.0", + "symfony/phpunit-bridge": "^6.4|^7.4|^8.0", + "symfony/process": "^6.4|^7.4|^8.0" }, "type": "composer-plugin", "extra": { @@ -5115,7 +4870,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.8.2" + "source": "https://github.com/symfony/flex/tree/v2.10.0" }, "funding": [ { @@ -5135,60 +4890,54 @@ "type": "tidelift" } ], - "time": "2025-08-22T07:17:23+00:00" + "time": "2025-11-16T09:38:19+00:00" }, { "name": "symfony/form", - "version": "v7.3.4", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/form.git", - "reference": "7b3eee0f4d4dfd1ff1be70a27474197330c61736" + "reference": "dd9f73dd3b92e657c97aeeca1f47e981c635ea91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/7b3eee0f4d4dfd1ff1be70a27474197330c61736", - "reference": "7b3eee0f4d4dfd1ff1be70a27474197330c61736", + "url": "https://api.github.com/repos/symfony/form/zipball/dd9f73dd3b92e657c97aeeca1f47e981c635ea91", + "reference": "dd9f73dd3b92e657c97aeeca1f47e981c635ea91", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/options-resolver": "^7.3", - "symfony/polyfill-ctype": "~1.8", + "php": ">=8.4", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/options-resolver": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-intl-icu": "^1.21", - "symfony/polyfill-mbstring": "~1.0", - "symfony/property-access": "^6.4|^7.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/property-access": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<6.4", - "symfony/error-handler": "<6.4", - "symfony/framework-bundle": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/translation": "<6.4.3|>=7.0,<7.0.3", + "symfony/intl": "<7.4", "symfony/translation-contracts": "<2.5", - "symfony/twig-bridge": "<6.4" + "symfony/validator": "<7.4" }, "require-dev": { "doctrine/collections": "^1.0|^2.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/html-sanitizer": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0.3", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/html-sanitizer": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/security-core": "^7.4|^8.0", + "symfony/security-csrf": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5216,7 +4965,7 @@ "description": "Allows to easily create, process and reuse HTML forms", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/form/tree/v7.3.4" + "source": "https://github.com/symfony/form/tree/v8.0.9" }, "funding": [ { @@ -5236,117 +4985,100 @@ "type": "tidelift" } ], - "time": "2025-09-22T15:31:00+00:00" + "time": "2026-04-29T15:02:55+00:00" }, { "name": "symfony/framework-bundle", - "version": "v7.3.4", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "b13e7cec5a144c8dba6f4233a2c53c00bc29e140" + "reference": "c0d53dba8de800f5dd1e9dac79683d8c59934d34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/b13e7cec5a144c8dba6f4233a2c53c00bc29e140", - "reference": "b13e7cec5a144c8dba6f4233a2c53c00bc29e140", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/c0d53dba8de800f5dd1e9dac79683d8c59934d34", + "reference": "c0d53dba8de800f5dd1e9dac79683d8c59934d34", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", "ext-xml": "*", - "php": ">=8.2", - "symfony/cache": "^6.4|^7.0", - "symfony/config": "^7.3", - "symfony/dependency-injection": "^7.2", + "php": ">=8.4", + "symfony/cache": "^7.4|^8.0", + "symfony/config": "^7.4.4|^8.0.4", + "symfony/dependency-injection": "^7.4.4|^8.0.4", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^7.3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/filesystem": "^7.1", - "symfony/finder": "^6.4|^7.0", - "symfony/http-foundation": "^7.3", - "symfony/http-kernel": "^7.2", - "symfony/polyfill-mbstring": "~1.0", - "symfony/routing": "^6.4|^7.0" + "symfony/error-handler": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/filesystem": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php85": "^1.32", + "symfony/routing": "^7.4|^8.0" }, "conflict": { "doctrine/persistence": "<1.3", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/asset": "<6.4", - "symfony/asset-mapper": "<6.4", - "symfony/clock": "<6.4", - "symfony/console": "<6.4", - "symfony/dom-crawler": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/form": "<6.4", - "symfony/http-client": "<6.4", - "symfony/json-streamer": ">=7.4", - "symfony/lock": "<6.4", - "symfony/mailer": "<6.4", - "symfony/messenger": "<6.4", - "symfony/mime": "<6.4", - "symfony/object-mapper": ">=7.4", - "symfony/property-access": "<6.4", - "symfony/property-info": "<6.4", - "symfony/runtime": "<6.4.13|>=7.0,<7.1.6", - "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", - "symfony/security-core": "<6.4", - "symfony/security-csrf": "<7.2", - "symfony/serializer": "<7.2.5", - "symfony/stopwatch": "<6.4", - "symfony/translation": "<7.3", - "symfony/twig-bridge": "<6.4", - "symfony/twig-bundle": "<6.4", - "symfony/validator": "<6.4", - "symfony/web-profiler-bundle": "<6.4", - "symfony/webhook": "<7.2", - "symfony/workflow": "<7.3.0-beta2" + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/console": "<7.4", + "symfony/form": "<7.4", + "symfony/json-streamer": "<7.4", + "symfony/messenger": "<7.4", + "symfony/mime": "<7.4.9|>=8.0,<8.0.9", + "symfony/security-csrf": "<7.4", + "symfony/serializer": "<7.4", + "symfony/translation": "<7.4", + "symfony/webhook": "<7.4", + "symfony/workflow": "<7.4" }, "require-dev": { "doctrine/persistence": "^1.3|^2|^3", "dragonmantank/cron-expression": "^3.1", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "phpstan/phpdoc-parser": "^1.0|^2.0", "seld/jsonlint": "^1.10", - "symfony/asset": "^6.4|^7.0", - "symfony/asset-mapper": "^6.4|^7.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/dom-crawler": "^6.4|^7.0", - "symfony/dotenv": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/html-sanitizer": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/json-streamer": "7.3.*", - "symfony/lock": "^6.4|^7.0", - "symfony/mailer": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/notifier": "^6.4|^7.0", - "symfony/object-mapper": "^v7.3.0-beta2", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/scheduler": "^6.4.4|^7.0.4", - "symfony/security-bundle": "^6.4|^7.0", - "symfony/semaphore": "^6.4|^7.0", - "symfony/serializer": "^7.2.5", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/string": "^6.4|^7.0", - "symfony/translation": "^7.3", - "symfony/twig-bundle": "^6.4|^7.0", - "symfony/type-info": "^7.1.8", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0", - "symfony/webhook": "^7.2", - "symfony/workflow": "^7.3", - "symfony/yaml": "^6.4|^7.0", - "twig/twig": "^3.12" + "symfony/asset": "^7.4|^8.0", + "symfony/asset-mapper": "^7.4|^8.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/dotenv": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/form": "^7.4|^8.0", + "symfony/html-sanitizer": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/json-streamer": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/mailer": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4.9|^8.0.9", + "symfony/notifier": "^7.4|^8.0", + "symfony/object-mapper": "^7.4.9|^8.0.9", + "symfony/polyfill-intl-icu": "^1.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0", + "symfony/scheduler": "^7.4|^8.0", + "symfony/security-bundle": "^7.4|^8.0", + "symfony/semaphore": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/string": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/twig-bundle": "^7.4|^8.0", + "symfony/type-info": "^7.4.1|^8.0.1", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/web-link": "^7.4|^8.0", + "symfony/webhook": "^7.4|^8.0", + "symfony/workflow": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "symfony-bundle", "autoload": { @@ -5374,7 +5106,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.3.4" + "source": "https://github.com/symfony/framework-bundle/tree/v8.0.11" }, "funding": [ { @@ -5394,35 +5126,31 @@ "type": "tidelift" } ], - "time": "2025-09-17T05:51:54+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "symfony/http-client", - "version": "v7.3.4", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62" + "reference": "537c7f164078975b800f3f1c56810791024e4c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62", - "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62", + "url": "https://api.github.com/repos/symfony/http-client/zipball/537c7f164078975b800f3f1c56810791024e4c77", + "reference": "537c7f164078975b800f3f1c56810791024e4c77", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "~3.4.4|^3.5.2", - "symfony/polyfill-php83": "^1.29", "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "amphp/amp": "<2.5", - "amphp/socket": "<1.1", - "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.4" + "amphp/amp": "<3", + "php-http/discovery": "<1.15" }, "provide": { "php-http/async-client-implementation": "*", @@ -5431,19 +5159,19 @@ "symfony/http-client-implementation": "3.0" }, "require-dev": { - "amphp/http-client": "^4.2.1|^5.0", - "amphp/http-tunnel": "^1.0|^2.0", + "amphp/http-client": "^5.3.2", + "amphp/http-tunnel": "^2.0", "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", - "symfony/amphp-http-client-meta": "^1.0|^2.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/cache": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5474,7 +5202,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.4" + "source": "https://github.com/symfony/http-client/tree/v8.0.9" }, "funding": [ { @@ -5494,20 +5222,20 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-04-29T15:02:55+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "75d7043853a42837e68111812f4d964b01e5101c" + "reference": "4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c", - "reference": "75d7043853a42837e68111812f4d964b01e5101c", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d", + "reference": "4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d", "shasum": "" }, "require": { @@ -5520,7 +5248,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -5556,7 +5284,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.7.0" }, "funding": [ { @@ -5567,47 +5295,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-04-29T11:18:49+00:00" + "time": "2026-03-06T13:17:50+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.3.4", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "c061c7c18918b1b64268771aad04b40be41dd2e6" + "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c061c7c18918b1b64268771aad04b40be41dd2e6", - "reference": "c061c7c18918b1b64268771aad04b40be41dd2e6", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/02656f7ebeae5c155d659e946f6b3a33df24051b", + "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php83": "^1.27" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.1" }, "conflict": { - "doctrine/dbal": "<3.6", - "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + "doctrine/dbal": "<4.3" }, "require-dev": { - "doctrine/dbal": "^3.6|^4", + "doctrine/dbal": "^4.3", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5", - "symfony/clock": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0" + "symfony/cache": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5635,7 +5364,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.4" + "source": "https://github.com/symfony/http-foundation/tree/v8.0.8" }, "funding": [ { @@ -5655,77 +5384,63 @@ "type": "tidelift" } ], - "time": "2025-09-16T08:38:17+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.4", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "b796dffea7821f035047235e076b60ca2446e3cf" + "reference": "20d3680373f4b791903c09e74b45402b4aeda71c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b796dffea7821f035047235e076b60ca2446e3cf", - "reference": "b796dffea7821f035047235e076b60ca2446e3cf", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/20d3680373f4b791903c09e74b45402b4aeda71c", + "reference": "20d3680373f4b791903c09e74b45402b4aeda71c", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0", - "symfony/event-dispatcher": "^7.3", - "symfony/http-foundation": "^7.3", + "symfony/error-handler": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/browser-kit": "<6.4", - "symfony/cache": "<6.4", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<6.4", - "symfony/form": "<6.4", - "symfony/http-client": "<6.4", + "symfony/flex": "<2.10", "symfony/http-client-contracts": "<2.5", - "symfony/mailer": "<6.4", - "symfony/messenger": "<6.4", - "symfony/translation": "<6.4", "symfony/translation-contracts": "<2.5", - "symfony/twig-bridge": "<6.4", - "symfony/validator": "<6.4", - "symfony/var-dumper": "<6.4", - "twig/twig": "<3.12" + "twig/twig": "<3.21" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/dom-crawler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^7.1", - "symfony/routing": "^6.4|^7.0", - "symfony/serializer": "^7.1", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0", - "twig/twig": "^3.12" + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0", + "twig/twig": "^3.21" }, "type": "library", "autoload": { @@ -5753,7 +5468,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.4" + "source": "https://github.com/symfony/http-kernel/tree/v8.0.11" }, "funding": [ { @@ -5773,32 +5488,31 @@ "type": "tidelift" } ], - "time": "2025-09-27T12:32:17+00:00" + "time": "2026-05-13T18:07:14+00:00" }, { "name": "symfony/intl", - "version": "v7.3.4", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/intl.git", - "reference": "e6db84864655885d9dac676a9d7dde0d904fda54" + "reference": "604a1dbbd67471e885e93274379cadd80dc33535" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/e6db84864655885d9dac676a9d7dde0d904fda54", - "reference": "e6db84864655885d9dac676a9d7dde0d904fda54", + "url": "https://api.github.com/repos/symfony/intl/zipball/604a1dbbd67471e885e93274379cadd80dc33535", + "reference": "604a1dbbd67471e885e93274379cadd80dc33535", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" + "php": ">=8.4" }, "conflict": { - "symfony/string": "<7.1" + "symfony/string": "<7.4" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/filesystem": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5843,7 +5557,7 @@ "localization" ], "support": { - "source": "https://github.com/symfony/intl/tree/v7.3.4" + "source": "https://github.com/symfony/intl/tree/v8.0.8" }, "funding": [ { @@ -5863,52 +5577,49 @@ "type": "tidelift" } ], - "time": "2025-09-08T14:11:30+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/messenger", - "version": "v7.3.3", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "d9e04339404ba2dcd04c24172125516dc0e06c35" + "reference": "c451c175724fc781c777783aaec3b7999ceb0621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/d9e04339404ba2dcd04c24172125516dc0e06c35", - "reference": "d9e04339404ba2dcd04c24172125516dc0e06c35", + "url": "https://api.github.com/repos/symfony/messenger/zipball/c451c175724fc781c777783aaec3b7999ceb0621", + "reference": "c451c175724fc781c777783aaec3b7999ceb0621", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", - "symfony/clock": "^6.4|^7.0", - "symfony/deprecation-contracts": "^2.5|^3" + "symfony/clock": "^7.4|^8.0" }, "conflict": { - "symfony/console": "<7.2", - "symfony/event-dispatcher": "<6.4", + "symfony/console": "<7.4", "symfony/event-dispatcher-contracts": "<2.5", - "symfony/framework-bundle": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/lock": "<6.4", - "symfony/serializer": "<6.4" + "symfony/lock": "<7.4", + "symfony/serializer": "<7.4.4|>=8.0,<8.0.4" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/console": "^7.2", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/serializer": "^7.4.4|^8.0.4", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0" + "symfony/stopwatch": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5936,7 +5647,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v7.3.3" + "source": "https://github.com/symfony/messenger/tree/v8.0.11" }, "funding": [ { @@ -5956,43 +5667,41 @@ "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "symfony/mime", - "version": "v7.3.4", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35" + "reference": "a9fcb293650c054b62a5b406f4e92e7b711ea333" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/b1b828f69cbaf887fa835a091869e55df91d0e35", - "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35", + "url": "https://api.github.com/repos/symfony/mime/zipball/a9fcb293650c054b62a5b406f4e92e7b711ea333", + "reference": "a9fcb293650c054b62a5b406f4e92e7b711ea333", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "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", - "symfony/mailer": "<6.4", - "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1" }, "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": "^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" + "phpdocumentor/reflection-docblock": "^5.2|^6.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" }, "type": "library", "autoload": { @@ -6024,7 +5733,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.3.4" + "source": "https://github.com/symfony/mime/tree/v8.0.9" }, "funding": [ { @@ -6044,41 +5753,36 @@ "type": "tidelift" } ], - "time": "2025-09-16T08:38:17+00:00" + "time": "2026-04-29T15:02:55+00:00" }, { "name": "symfony/monolog-bridge", - "version": "v7.3.4", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bridge.git", - "reference": "7acf2abe23e5019451399ba69fc8ed3d61d4d8f0" + "reference": "4b7249b1520773ad325e99231b08443017729297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/7acf2abe23e5019451399ba69fc8ed3d61d4d8f0", - "reference": "7acf2abe23e5019451399ba69fc8ed3d61d4d8f0", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/4b7249b1520773ad325e99231b08443017729297", + "reference": "4b7249b1520773ad325e99231b08443017729297", "shasum": "" }, "require": { "monolog/monolog": "^3", - "php": ">=8.2", - "symfony/http-kernel": "^6.4|^7.0", + "php": ">=8.4", + "symfony/http-kernel": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, - "conflict": { - "symfony/console": "<6.4", - "symfony/http-foundation": "<6.4", - "symfony/security-core": "<6.4" - }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/mailer": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/console": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/mailer": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/security-core": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "symfony-bridge", "autoload": { @@ -6106,7 +5810,7 @@ "description": "Provides integration for Monolog with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/monolog-bridge/tree/v7.3.4" + "source": "https://github.com/symfony/monolog-bridge/tree/v8.0.9" }, "funding": [ { @@ -6126,48 +5830,42 @@ "type": "tidelift" } ], - "time": "2025-09-24T16:45:39+00:00" + "time": "2026-04-29T15:02:55+00:00" }, { "name": "symfony/monolog-bundle", - "version": "v3.10.0", + "version": "v4.0.2", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bundle.git", - "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181" + "reference": "c012c6aba13129eb02aa7dd61e66e720911d8598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", - "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/c012c6aba13129eb02aa7dd61e66e720911d8598", + "reference": "c012c6aba13129eb02aa7dd61e66e720911d8598", "shasum": "" }, "require": { - "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", - "php": ">=7.2.5", - "symfony/config": "^5.4 || ^6.0 || ^7.0", - "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", - "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", - "symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0" + "composer-runtime-api": "^2.0", + "monolog/monolog": "^3.5", + "php": ">=8.2", + "symfony/config": "^7.3 || ^8.0", + "symfony/dependency-injection": "^7.3 || ^8.0", + "symfony/http-kernel": "^7.3 || ^8.0", + "symfony/monolog-bridge": "^7.3 || ^8.0", + "symfony/polyfill-php84": "^1.30" }, "require-dev": { - "symfony/console": "^5.4 || ^6.0 || ^7.0", - "symfony/phpunit-bridge": "^6.3 || ^7.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + "phpunit/phpunit": "^11.5.41 || ^12.3", + "symfony/console": "^7.3 || ^8.0", + "symfony/yaml": "^7.3 || ^8.0" }, "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Bundle\\MonologBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Bundle\\MonologBundle\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6191,76 +5889,7 @@ ], "support": { "issues": "https://github.com/symfony/monolog-bundle/issues", - "source": "https://github.com/symfony/monolog-bundle/tree/v3.10.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-11-06T17:08:13+00:00" - }, - { - "name": "symfony/object-mapper", - "version": "v7.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/object-mapper.git", - "reference": "62752b89178d7654bb7ec97d3fa3dbd67e417dbe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/object-mapper/zipball/62752b89178d7654bb7ec97d3fa3dbd67e417dbe", - "reference": "62752b89178d7654bb7ec97d3fa3dbd67e417dbe", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "psr/container": "^2.0" - }, - "conflict": { - "symfony/property-access": "<7.2" - }, - "require-dev": { - "symfony/property-access": "^7.2", - "symfony/var-exporter": "^7.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\ObjectMapper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a way to map an object to another object", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/object-mapper/tree/v7.3.4" + "source": "https://github.com/symfony/monolog-bundle/tree/v4.0.2" }, "funding": [ { @@ -6280,24 +5909,24 @@ "type": "tidelift" } ], - "time": "2025-09-20T14:49:40+00:00" + "time": "2026-04-02T18:27:21+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.3.3", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d" + "reference": "b48bce0a70b914f6953dafbd10474df232ed4de8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d", - "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b48bce0a70b914f6953dafbd10474df232ed4de8", + "reference": "b48bce0a70b914f6953dafbd10474df232ed4de8", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -6331,7 +5960,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.3" + "source": "https://github.com/symfony/options-resolver/tree/v8.0.8" }, "funding": [ { @@ -6351,31 +5980,28 @@ "type": "tidelift" } ], - "time": "2025-08-05T10:16:07+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/password-hasher", - "version": "v7.3.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/password-hasher.git", - "reference": "31fbe66af859582a20b803f38be96be8accdf2c3" + "reference": "57ee968d3c38301ed3e5b838f850a10f2d06a7f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/password-hasher/zipball/31fbe66af859582a20b803f38be96be8accdf2c3", - "reference": "31fbe66af859582a20b803f38be96be8accdf2c3", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/57ee968d3c38301ed3e5b838f850a10f2d06a7f6", + "reference": "57ee968d3c38301ed3e5b838f850a10f2d06a7f6", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "conflict": { - "symfony/security-core": "<6.4" + "php": ">=8.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0" + "symfony/console": "^7.4|^8.0", + "symfony/security-core": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -6407,7 +6033,7 @@ "password" ], "support": { - "source": "https://github.com/symfony/password-hasher/tree/v7.3.0" + "source": "https://github.com/symfony/password-hasher/tree/v8.0.8" }, "funding": [ { @@ -6418,25 +6044,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-02-04T08:22:58+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" }, "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/4864388bfbd3001ce88e234fab652acd91fdc57e", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", "shasum": "" }, "require": { @@ -6485,7 +6115,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.37.0" }, "funding": [ { @@ -6505,20 +6135,20 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-04-26T13:13:48+00:00" }, { "name": "symfony/polyfill-intl-icu", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-icu.git", - "reference": "bfc8fa13dbaf21d69114b0efcd72ab700fb04d0c" + "reference": "3510b63d07376b04e57e27e82607d468bb134f78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/bfc8fa13dbaf21d69114b0efcd72ab700fb04d0c", - "reference": "bfc8fa13dbaf21d69114b0efcd72ab700fb04d0c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/3510b63d07376b04e57e27e82607d468bb134f78", + "reference": "3510b63d07376b04e57e27e82607d468bb134f78", "shasum": "" }, "require": { @@ -6573,7 +6203,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.37.0" }, "funding": [ { @@ -6593,11 +6223,11 @@ "type": "tidelift" } ], - "time": "2025-06-20T22:24:30+00:00" + "time": "2026-04-10T16:50:15+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -6660,7 +6290,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.37.0" }, "funding": [ { @@ -6684,7 +6314,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -6745,7 +6375,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.37.0" }, "funding": [ { @@ -6769,16 +6399,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" }, "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/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", "shasum": "" }, "require": { @@ -6830,7 +6460,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" }, "funding": [ { @@ -6850,20 +6480,20 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-04-10T17:25:58+00:00" }, { - "name": "symfony/polyfill-php83", - "version": "v1.33.0", + "name": "symfony/polyfill-php84", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/88486db2c389b290bf87ff1de7ebc1e13e42bb06", + "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06", "shasum": "" }, "require": { @@ -6881,7 +6511,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php83\\": "" + "Symfony\\Polyfill\\Php84\\": "" }, "classmap": [ "Resources/stubs" @@ -6901,7 +6531,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -6910,7 +6540,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0" }, "funding": [ { @@ -6930,20 +6560,20 @@ "type": "tidelift" } ], - "time": "2025-07-08T02:45:35+00:00" + "time": "2026-04-10T18:47:49+00:00" }, { - "name": "symfony/polyfill-php84", - "version": "v1.33.0", + "name": "symfony/polyfill-php85", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/fcfa4973a9917cef23f2e38774da74a2b7d115ee", + "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee", "shasum": "" }, "require": { @@ -6961,7 +6591,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php84\\": "" + "Symfony\\Polyfill\\Php85\\": "" }, "classmap": [ "Resources/stubs" @@ -6981,7 +6611,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -6990,7 +6620,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.37.0" }, "funding": [ { @@ -7010,20 +6640,20 @@ "type": "tidelift" } ], - "time": "2025-06-24T13:30:11+00:00" + "time": "2026-04-26T13:10:57+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", "shasum": "" }, "require": { @@ -7073,7 +6703,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0" }, "funding": [ { @@ -7093,28 +6723,29 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/property-access", - "version": "v7.3.3", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "4a4389e5c8bd1d0320d80a23caa6a1ac71cb81a7" + "reference": "704c7808116fcdd67327db7b17de56b8ef6169e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/4a4389e5c8bd1d0320d80a23caa6a1ac71cb81a7", - "reference": "4a4389e5c8bd1d0320d80a23caa6a1ac71cb81a7", + "url": "https://api.github.com/repos/symfony/property-access/zipball/704c7808116fcdd67327db7b17de56b8ef6169e4", + "reference": "704c7808116fcdd67327db7b17de56b8ef6169e4", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/property-info": "^6.4|^7.0" + "php": ">=8.4", + "symfony/property-info": "^7.4.4|^8.0.4" }, "require-dev": { - "symfony/cache": "^6.4|^7.0" + "symfony/cache": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7153,7 +6784,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v7.3.3" + "source": "https://github.com/symfony/property-access/tree/v8.0.8" }, "funding": [ { @@ -7173,41 +6804,37 @@ "type": "tidelift" } ], - "time": "2025-08-04T15:15:28+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/property-info", - "version": "v7.3.4", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "7b6db23f23d13ada41e1cb484748a8ec028fbace" + "reference": "c21711980653360d6ef5c26d0f9ca6f58a1135c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/7b6db23f23d13ada41e1cb484748a8ec028fbace", - "reference": "7b6db23f23d13ada41e1cb484748a8ec028fbace", + "url": "https://api.github.com/repos/symfony/property-info/zipball/c21711980653360d6ef5c26d0f9ca6f58a1135c6", + "reference": "c21711980653360d6ef5c26d0f9ca6f58a1135c6", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0", - "symfony/type-info": "~7.2.8|^7.3.1" + "php": ">=8.4", + "symfony/string": "^7.4|^8.0", + "symfony/type-info": "^7.4.7|^8.0.7" }, "conflict": { - "phpdocumentor/reflection-docblock": "<5.2", - "phpdocumentor/type-resolver": "<1.5.1", - "symfony/cache": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/serializer": "<6.4" + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1" }, "require-dev": { - "phpdocumentor/reflection-docblock": "^5.2", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", "phpstan/phpdoc-parser": "^1.0|^2.0", - "symfony/cache": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/cache": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7243,7 +6870,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.3.4" + "source": "https://github.com/symfony/property-info/tree/v8.0.8" }, "funding": [ { @@ -7263,38 +6890,33 @@ "type": "tidelift" } ], - "time": "2025-09-15T13:55:54+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/routing", - "version": "v7.3.4", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c" + "reference": "75d1bd8e5da3424e4db2fc3ff0222cb4d0c73038" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/8dc648e159e9bac02b703b9fbd937f19ba13d07c", - "reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c", + "url": "https://api.github.com/repos/symfony/routing/zipball/75d1bd8e5da3424e4db2fc3ff0222cb4d0c73038", + "reference": "75d1bd8e5da3424e4db2fc3ff0222cb4d0c73038", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3" }, - "conflict": { - "symfony/config": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/yaml": "<6.4" - }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7328,7 +6950,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.3.4" + "source": "https://github.com/symfony/routing/tree/v8.0.9" }, "funding": [ { @@ -7348,35 +6970,35 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-04-29T15:02:55+00:00" }, { "name": "symfony/runtime", - "version": "v7.3.4", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "3550e2711e30bfa5d808514781cd52d1cc1d9e9f" + "reference": "30884f00e017a26100fcd9aa281082ebf9a87dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/3550e2711e30bfa5d808514781cd52d1cc1d9e9f", - "reference": "3550e2711e30bfa5d808514781cd52d1cc1d9e9f", + "url": "https://api.github.com/repos/symfony/runtime/zipball/30884f00e017a26100fcd9aa281082ebf9a87dce", + "reference": "30884f00e017a26100fcd9aa281082ebf9a87dce", "shasum": "" }, "require": { "composer-plugin-api": "^1.0|^2.0", - "php": ">=8.2" + "php": ">=8.4" }, "conflict": { - "symfony/dotenv": "<6.4" + "symfony/error-handler": "<7.4" }, "require-dev": { "composer/composer": "^2.6", - "symfony/console": "^6.4|^7.0", - "symfony/dotenv": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" + "symfony/console": "^7.4|^8.0", + "symfony/dotenv": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0" }, "type": "composer-plugin", "extra": { @@ -7411,7 +7033,7 @@ "runtime" ], "support": { - "source": "https://github.com/symfony/runtime/tree/v7.3.4" + "source": "https://github.com/symfony/runtime/tree/v8.0.8" }, "funding": [ { @@ -7431,68 +7053,58 @@ "type": "tidelift" } ], - "time": "2025-09-11T15:31:28+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/security-bundle", - "version": "v7.3.4", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "f750d9abccbeaa433c56f6a4eb2073166476a75a" + "reference": "c7ae56efdc872704101a55b86f950b111aea9a06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/f750d9abccbeaa433c56f6a4eb2073166476a75a", - "reference": "f750d9abccbeaa433c56f6a4eb2073166476a75a", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/c7ae56efdc872704101a55b86f950b111aea9a06", + "reference": "c7ae56efdc872704101a55b86f950b111aea9a06", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", "ext-xml": "*", - "php": ">=8.2", - "symfony/clock": "^6.4|^7.0", - "symfony/config": "^7.3", - "symfony/dependency-injection": "^6.4.11|^7.1.4", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/password-hasher": "^6.4|^7.0", - "symfony/security-core": "^7.3", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/security-http": "^7.3", + "php": ">=8.4", + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/password-hasher": "^7.4|^8.0", + "symfony/security-core": "^7.4|^8.0", + "symfony/security-csrf": "^7.4|^8.0", + "symfony/security-http": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, - "conflict": { - "symfony/browser-kit": "<6.4", - "symfony/console": "<6.4", - "symfony/framework-bundle": "<6.4", - "symfony/http-client": "<6.4", - "symfony/ldap": "<6.4", - "symfony/serializer": "<6.4", - "symfony/twig-bundle": "<6.4", - "symfony/validator": "<6.4" - }, - "require-dev": { - "symfony/asset": "^6.4|^7.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/dom-crawler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/ldap": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0", - "symfony/twig-bundle": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", - "twig/twig": "^3.12", + "require-dev": { + "symfony/asset": "^7.4|^8.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/form": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/ldap": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/twig-bridge": "^7.4|^8.0", + "symfony/twig-bundle": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0", "web-token/jwt-library": "^3.3.2|^4.0" }, "type": "symfony-bundle", @@ -7521,7 +7133,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v7.3.4" + "source": "https://github.com/symfony/security-bundle/tree/v8.0.11" }, "funding": [ { @@ -7541,50 +7153,41 @@ "type": "tidelift" } ], - "time": "2025-09-22T15:31:00+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "symfony/security-core", - "version": "v7.3.4", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "68b9d3ca57615afde6152a1e1441fa035bea43f8" + "reference": "7aa47b511c07734bbb3490046ca8cdff1bf4fbef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/68b9d3ca57615afde6152a1e1441fa035bea43f8", - "reference": "68b9d3ca57615afde6152a1e1441fa035bea43f8", + "url": "https://api.github.com/repos/symfony/security-core/zipball/7aa47b511c07734bbb3490046ca8cdff1bf4fbef", + "reference": "7aa47b511c07734bbb3490046ca8cdff1bf4fbef", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", "symfony/event-dispatcher-contracts": "^2.5|^3", - "symfony/password-hasher": "^6.4|^7.0", + "symfony/password-hasher": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/http-foundation": "<6.4", - "symfony/ldap": "<6.4", - "symfony/translation": "<6.4.3|>=7.0,<7.0.3", - "symfony/validator": "<6.4" - }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", "psr/container": "^1.1|^2.0", "psr/log": "^1|^2|^3", - "symfony/cache": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/ldap": "^6.4|^7.0", - "symfony/string": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0.3", - "symfony/validator": "^6.4|^7.0" + "symfony/cache": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/ldap": "^7.4|^8.0", + "symfony/string": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7612,7 +7215,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v7.3.4" + "source": "https://github.com/symfony/security-core/tree/v8.0.11" }, "funding": [ { @@ -7632,33 +7235,30 @@ "type": "tidelift" } ], - "time": "2025-09-24T14:32:13+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "symfony/security-csrf", - "version": "v7.3.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/security-csrf.git", - "reference": "2b4b0c46c901729e4e90719eacd980381f53e0a3" + "reference": "83c8f60ef8d385c05ea863093c9efabe74800883" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-csrf/zipball/2b4b0c46c901729e4e90719eacd980381f53e0a3", - "reference": "2b4b0c46c901729e4e90719eacd980381f53e0a3", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/83c8f60ef8d385c05ea863093c9efabe74800883", + "reference": "83c8f60ef8d385c05ea863093c9efabe74800883", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/security-core": "^6.4|^7.0" - }, - "conflict": { - "symfony/http-foundation": "<6.4" + "php": ">=8.4", + "symfony/security-core": "^7.4|^8.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7686,7 +7286,7 @@ "description": "Symfony Security Component - CSRF Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-csrf/tree/v7.3.0" + "source": "https://github.com/symfony/security-csrf/tree/v8.0.8" }, "funding": [ { @@ -7697,55 +7297,54 @@ "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-01-02T18:42:10+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/security-http", - "version": "v7.3.4", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "1cf54d0648ebab23bf9b8972617b79f1995e13a9" + "reference": "3c29d0118c6bc5919ce6f2ef4e9ead24503ca819" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/1cf54d0648ebab23bf9b8972617b79f1995e13a9", - "reference": "1cf54d0648ebab23bf9b8972617b79f1995e13a9", + "url": "https://api.github.com/repos/symfony/security-http/zipball/3c29d0118c6bc5919ce6f2ef4e9ead24503ca819", + "reference": "3c29d0118c6bc5919ce6f2ef4e9ead24503ca819", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/security-core": "^7.3", + "php": ">=8.4", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/security-core": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/clock": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/http-client-contracts": "<3.0", - "symfony/security-bundle": "<6.4", - "symfony/security-csrf": "<6.4" + "symfony/http-client-contracts": "<3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/cache": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", + "symfony/cache": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", "symfony/http-client-contracts": "^3.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", + "symfony/rate-limiter": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/security-csrf": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", "web-token/jwt-library": "^3.3.2|^4.0" }, "type": "library", @@ -7774,7 +7373,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v7.3.4" + "source": "https://github.com/symfony/security-http/tree/v8.0.11" }, "funding": [ { @@ -7794,62 +7393,57 @@ "type": "tidelift" } ], - "time": "2025-09-09T17:06:44+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "symfony/serializer", - "version": "v7.3.4", + "version": "v8.0.10", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "0df5af266c6fe9a855af7db4fea86e13b9ca3ab1" + "reference": "72ed7e1475790714f07c3a59bd01fd32cd022fdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/0df5af266c6fe9a855af7db4fea86e13b9ca3ab1", - "reference": "0df5af266c6fe9a855af7db4fea86e13b9ca3ab1", + "url": "https://api.github.com/repos/symfony/serializer/zipball/72ed7e1475790714f07c3a59bd01fd32cd022fdf", + "reference": "72ed7e1475790714f07c3a59bd01fd32cd022fdf", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php84": "^1.30" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/dependency-injection": "<6.4", - "symfony/property-access": "<6.4", - "symfony/property-info": "<6.4", - "symfony/uid": "<6.4", - "symfony/validator": "<6.4", - "symfony/yaml": "<6.4" + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/property-access": "<7.4.2|>=8.0,<8.0.2", + "symfony/property-info": "<7.4", + "symfony/type-info": "<7.4" }, "require-dev": { - "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", "phpstan/phpdoc-parser": "^1.0|^2.0", "seld/jsonlint": "^1.10", - "symfony/cache": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^7.2", - "symfony/error-handler": "^6.4|^7.0", - "symfony/filesystem": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", + "symfony/cache": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/filesystem": "^7.4|^8.0", + "symfony/form": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/property-access": "^7.4.2|^8.0.2", + "symfony/property-info": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/type-info": "^7.1.8", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/type-info": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7877,7 +7471,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v7.3.4" + "source": "https://github.com/symfony/serializer/tree/v8.0.10" }, "funding": [ { @@ -7897,20 +7491,20 @@ "type": "tidelift" } ], - "time": "2025-09-15T13:39:02+00:00" + "time": "2026-05-04T13:41:39+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { @@ -7928,7 +7522,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -7964,7 +7558,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -7975,29 +7569,33 @@ "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-04-25T09:37:31+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { "name": "symfony/stopwatch", - "version": "v7.3.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd" + "reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", - "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/85954ed72d5440ea4dc9a10b7e49e01df766ffa3", + "reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/service-contracts": "^2.5|^3" }, "type": "library", @@ -8026,7 +7624,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.3.0" + "source": "https://github.com/symfony/stopwatch/tree/v8.0.8" }, "funding": [ { @@ -8037,43 +7635,47 @@ "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-02-24T10:49:57+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/string", - "version": "v7.3.4", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f96476035142921000338bad71e5247fbc138872" + "reference": "39be2ad058a3c0bd558edca23e65f009865d75ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", - "reference": "f96476035142921000338bad71e5247fbc138872", + "url": "https://api.github.com/repos/symfony/string/zipball/39be2ad058a3c0bd558edca23e65f009865d75ff", + "reference": "39be2ad058a3c0bd558edca23e65f009865d75ff", "shasum": "" }, "require": { - "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" + "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.1", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "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": "^6.4|^7.0" + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8112,7 +7714,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.4" + "source": "https://github.com/symfony/string/tree/v8.0.11" }, "funding": [ { @@ -8132,38 +7734,31 @@ "type": "tidelift" } ], - "time": "2025-09-11T14:36:48+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "symfony/translation", - "version": "v7.3.4", + "version": "v8.0.10", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "ec25870502d0c7072d086e8ffba1420c85965174" + "reference": "f63e9342e12646a57c91ef8a366a4f9d8e557b67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/ec25870502d0c7072d086e8ffba1420c85965174", - "reference": "ec25870502d0c7072d086e8ffba1420c85965174", + "url": "https://api.github.com/repos/symfony/translation/zipball/f63e9342e12646a57c91ef8a366a4f9d8e557b67", + "reference": "f63e9342e12646a57c91ef8a366a4f9d8e557b67", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5|^3.0" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation-contracts": "^3.6.1" }, "conflict": { "nikic/php-parser": "<5.0", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/service-contracts": "<2.5", - "symfony/twig-bundle": "<6.4", - "symfony/yaml": "<6.4" + "symfony/service-contracts": "<2.5" }, "provide": { "symfony/translation-implementation": "2.3|3.0" @@ -8171,17 +7766,17 @@ "require-dev": { "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^6.4|^7.0", + "symfony/routing": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8212,7 +7807,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.4" + "source": "https://github.com/symfony/translation/tree/v8.0.10" }, "funding": [ { @@ -8232,20 +7827,20 @@ "type": "tidelift" } ], - "time": "2025-09-07T11:39:36+00:00" + "time": "2026-05-06T11:30:54+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/0ab302977a952b42fd51475c4ebac81f8da0a95d", + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d", "shasum": "" }, "require": { @@ -8258,7 +7853,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -8294,7 +7889,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.0" }, "funding": [ { @@ -8305,76 +7900,73 @@ "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-27T08:32:26+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/twig-bridge", - "version": "v7.3.3", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "33558f013b7f6ed72805527c8405cae0062e47c5" + "reference": "a892d0b7f3d5d51b35895467e48aafbd1f2612a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/33558f013b7f6ed72805527c8405cae0062e47c5", - "reference": "33558f013b7f6ed72805527c8405cae0062e47c5", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/a892d0b7f3d5d51b35895467e48aafbd1f2612a0", + "reference": "a892d0b7f3d5d51b35895467e48aafbd1f2612a0", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", "symfony/translation-contracts": "^2.5|^3", "twig/twig": "^3.21" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/console": "<6.4", - "symfony/form": "<6.4", - "symfony/http-foundation": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/mime": "<6.4", - "symfony/serializer": "<6.4", - "symfony/translation": "<6.4", - "symfony/workflow": "<6.4" + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/form": "<7.4.4|>8.0,<8.0.4", + "symfony/mime": "<7.4.8|>8.0,<8.0.8" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3|^4", "league/html-to-markdown": "^5.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/asset": "^6.4|^7.0", - "symfony/asset-mapper": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/emoji": "^7.1", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/form": "^6.4.20|^7.2.5", - "symfony/html-sanitizer": "^6.4|^7.0", - "symfony/http-foundation": "^7.3", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "symfony/asset": "^7.4|^8.0", + "symfony/asset-mapper": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/form": "^7.4.4|^8.0.4", + "symfony/html-sanitizer": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/mime": "^7.4.8|^8.0.8", + "symfony/polyfill-intl-icu": "^1.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", "symfony/security-acl": "^2.8|^3.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/security-http": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0", - "symfony/workflow": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", + "symfony/security-core": "^7.4|^8.0", + "symfony/security-csrf": "^7.4|^8.0", + "symfony/security-http": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/web-link": "^7.4|^8.0", + "symfony/workflow": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0", "twig/cssinliner-extra": "^3", "twig/inky-extra": "^3", "twig/markdown-extra": "^3" @@ -8405,7 +7997,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v7.3.3" + "source": "https://github.com/symfony/twig-bridge/tree/v8.0.8" }, "funding": [ { @@ -8425,47 +8017,43 @@ "type": "tidelift" } ], - "time": "2025-08-18T13:10:53+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/twig-bundle", - "version": "v7.3.4", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "da5c778a8416fcce5318737c4d944f6fa2bb3f81" + "reference": "f83767b78e2580ca9fe9a2cf6fcff19cd5389bc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/da5c778a8416fcce5318737c4d944f6fa2bb3f81", - "reference": "da5c778a8416fcce5318737c4d944f6fa2bb3f81", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/f83767b78e2580ca9fe9a2cf6fcff19cd5389bc1", + "reference": "f83767b78e2580ca9fe9a2cf6fcff19cd5389bc1", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", - "php": ">=8.2", - "symfony/config": "^7.3", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/twig-bridge": "^7.3", - "twig/twig": "^3.12" - }, - "conflict": { - "symfony/framework-bundle": "<6.4", - "symfony/translation": "<6.4" - }, - "require-dev": { - "symfony/asset": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "php": ">=8.4", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/twig-bridge": "^7.4|^8.0" + }, + "require-dev": { + "symfony/asset": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/form": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/web-link": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "symfony-bundle", "autoload": { @@ -8493,7 +8081,7 @@ "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v7.3.4" + "source": "https://github.com/symfony/twig-bundle/tree/v8.0.8" }, "funding": [ { @@ -8513,26 +8101,25 @@ "type": "tidelift" } ], - "time": "2025-09-10T12:00:31+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/type-info", - "version": "v7.3.4", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "d34eaeb57f39c8a9c97eb72a977c423207dfa35b" + "reference": "08723aceb8c3271e8cb3db8b2565728b0c88e866" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/d34eaeb57f39c8a9c97eb72a977c423207dfa35b", - "reference": "d34eaeb57f39c8a9c97eb72a977c423207dfa35b", + "url": "https://api.github.com/repos/symfony/type-info/zipball/08723aceb8c3271e8cb3db8b2565728b0c88e866", + "reference": "08723aceb8c3271e8cb3db8b2565728b0c88e866", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" + "php": ">=8.4", + "psr/container": "^1.1|^2.0" }, "conflict": { "phpstan/phpdoc-parser": "<1.30" @@ -8576,7 +8163,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v7.3.4" + "source": "https://github.com/symfony/type-info/tree/v8.0.9" }, "funding": [ { @@ -8596,28 +8183,28 @@ "type": "tidelift" } ], - "time": "2025-09-11T15:33:27+00:00" + "time": "2026-04-29T15:02:55+00:00" }, { "name": "symfony/uid", - "version": "v7.3.1", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" + "reference": "4d9d6510bbe88ebb4608b7200d18606cdf80825c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", - "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", + "url": "https://api.github.com/repos/symfony/uid/zipball/4d9d6510bbe88ebb4608b7200d18606cdf80825c", + "reference": "4d9d6510bbe88ebb4608b7200d18606cdf80825c", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8654,7 +8241,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.3.1" + "source": "https://github.com/symfony/uid/tree/v8.0.9" }, "funding": [ { @@ -8665,47 +8252,52 @@ "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-27T19:55:54+00:00" + "time": "2026-04-30T16:10:06+00:00" }, { "name": "symfony/ux-twig-component", - "version": "v2.30.0", + "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/symfony/ux-twig-component.git", - "reference": "2f445efda4d4400d4d1911ddf9710c366f339614" + "reference": "dea344e320238234c94f63b152d65821c80cd103" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-twig-component/zipball/2f445efda4d4400d4d1911ddf9710c366f339614", - "reference": "2f445efda4d4400d4d1911ddf9710c366f339614", + "url": "https://api.github.com/repos/symfony/ux-twig-component/zipball/dea344e320238234c94f63b152d65821c80cd103", + "reference": "dea344e320238234c94f63b152d65821c80cd103", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/dependency-injection": "^5.4|^6.0|^7.0|^8.0", + "php": ">=8.4", + "symfony/dependency-injection": "^7.4|^8.0", "symfony/deprecation-contracts": "^2.2|^3.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0|^8.0", - "symfony/property-access": "^5.4|^6.0|^7.0|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", "twig/twig": "^3.10.3" }, "conflict": { - "symfony/config": "<5.4.0" + "symfony/config": "<6.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0|^8.0", - "symfony/css-selector": "^5.4|^6.0|^7.0|^8.0", - "symfony/dom-crawler": "^5.4|^6.0|^7.0|^8.0", - "symfony/framework-bundle": "^5.4|^6.0|^7.0|^8.0", - "symfony/phpunit-bridge": "^6.0|^7.0|^8.0", - "symfony/stimulus-bundle": "^2.9.1", - "symfony/twig-bundle": "^5.4|^6.0|^7.0|^8.0", - "symfony/webpack-encore-bundle": "^1.15|^2.3.0" + "phpunit/phpunit": "^11.1|^12.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/stimulus-bundle": "^2.9.1|^3.0", + "symfony/twig-bundle": "^7.4|^8.0", + "twig/extra-bundle": "^3.10.3", + "twig/html-extra": "^3.10.3" }, "type": "symfony-bundle", "extra": { @@ -8737,7 +8329,7 @@ "twig" ], "support": { - "source": "https://github.com/symfony/ux-twig-component/tree/v2.30.0" + "source": "https://github.com/symfony/ux-twig-component/tree/v3.0.0" }, "funding": [ { @@ -8757,60 +8349,53 @@ "type": "tidelift" } ], - "time": "2025-08-27T15:25:48+00:00" + "time": "2026-04-09T22:56:44+00:00" }, { "name": "symfony/validator", - "version": "v7.3.4", + "version": "v8.0.10", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "5e29a348b5fac2227b6938a54db006d673bb813a" + "reference": "12bb4be483a8626bd1b2f46f5d44c9449cf4361f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/5e29a348b5fac2227b6938a54db006d673bb813a", - "reference": "5e29a348b5fac2227b6938a54db006d673bb813a", + "url": "https://api.github.com/repos/symfony/validator/zipball/12bb4be483a8626bd1b2f46f5d44c9449cf4361f", + "reference": "12bb4be483a8626bd1b2f46f5d44c9449cf4361f", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php83": "^1.27", + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.0", "symfony/translation-contracts": "^2.5|^3" }, "conflict": { "doctrine/lexer": "<1.1", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<7.0", - "symfony/expression-language": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/intl": "<6.4", - "symfony/property-info": "<6.4", - "symfony/translation": "<6.4.3|>=7.0,<7.0.3", - "symfony/yaml": "<6.4" + "symfony/doctrine-bridge": "<7.4", + "symfony/expression-language": "<7.4" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3|^4", - "symfony/cache": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/string": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0.3", - "symfony/type-info": "^7.1.8", - "symfony/yaml": "^6.4|^7.0" + "symfony/cache": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/mime": "^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/string": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/type-info": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8839,7 +8424,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v7.3.4" + "source": "https://github.com/symfony/validator/tree/v8.0.10" }, "funding": [ { @@ -8859,35 +8444,35 @@ "type": "tidelift" } ], - "time": "2025-09-24T06:32:27+00:00" + "time": "2026-05-05T16:03:11+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.3.4", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb" + "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", - "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", + "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<7.4", + "symfony/error-handler": "<7.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0", + "symfony/console": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", "twig/twig": "^3.12" }, "bin": [ @@ -8926,7 +8511,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.4" + "source": "https://github.com/symfony/var-dumper/tree/v8.0.8" }, "funding": [ { @@ -8946,30 +8531,29 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-03-31T07:15:36+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.3.4", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "0f020b544a30a7fe8ba972e53ee48a74c0bc87f4" + "reference": "24cf67be4dd0926e4413635418682f4fff831412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/0f020b544a30a7fe8ba972e53ee48a74c0bc87f4", - "reference": "0f020b544a30a7fe8ba972e53ee48a74c0bc87f4", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/24cf67be4dd0926e4413635418682f4fff831412", + "reference": "24cf67be4dd0926e4413635418682f4fff831412", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" + "php": ">=8.4" }, "require-dev": { - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -9007,7 +8591,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.3.4" + "source": "https://github.com/symfony/var-exporter/tree/v8.0.9" }, "funding": [ { @@ -9027,34 +8611,31 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { "name": "symfony/web-link", - "version": "v7.3.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/web-link.git", - "reference": "7697f74fce67555665339423ce453cc8216a98ff" + "reference": "f76065fd8d59284332c8beaf2d7e1449f8c532c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-link/zipball/7697f74fce67555665339423ce453cc8216a98ff", - "reference": "7697f74fce67555665339423ce453cc8216a98ff", + "url": "https://api.github.com/repos/symfony/web-link/zipball/f76065fd8d59284332c8beaf2d7e1449f8c532c3", + "reference": "f76065fd8d59284332c8beaf2d7e1449f8c532c3", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/link": "^1.1|^2.0" }, - "conflict": { - "symfony/http-kernel": "<6.4" - }, "provide": { "psr/link-implementation": "1.0|2.0" }, "require-dev": { - "symfony/http-kernel": "^6.4|^7.0" + "symfony/http-kernel": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -9094,7 +8675,7 @@ "push" ], "support": { - "source": "https://github.com/symfony/web-link/tree/v7.3.0" + "source": "https://github.com/symfony/web-link/tree/v8.0.8" }, "funding": [ { @@ -9105,25 +8686,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-05-19T13:28:18+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/webpack-encore-bundle", - "version": "v2.3.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/symfony/webpack-encore-bundle.git", - "reference": "7ae70d44c24c3b913f308af8396169b5c6d9e0f5" + "reference": "5b932e0feddd81aaf0ecd7d5fcd2e450e5a7817e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/webpack-encore-bundle/zipball/7ae70d44c24c3b913f308af8396169b5c6d9e0f5", - "reference": "7ae70d44c24c3b913f308af8396169b5c6d9e0f5", + "url": "https://api.github.com/repos/symfony/webpack-encore-bundle/zipball/5b932e0feddd81aaf0ecd7d5fcd2e450e5a7817e", + "reference": "5b932e0feddd81aaf0ecd7d5fcd2e450e5a7817e", "shasum": "" }, "require": { @@ -9166,7 +8751,7 @@ "description": "Integration of your Symfony app with Webpack Encore", "support": { "issues": "https://github.com/symfony/webpack-encore-bundle/issues", - "source": "https://github.com/symfony/webpack-encore-bundle/tree/v2.3.0" + "source": "https://github.com/symfony/webpack-encore-bundle/tree/v2.4.0" }, "funding": [ { @@ -9186,32 +8771,31 @@ "type": "tidelift" } ], - "time": "2025-08-05T11:43:32+00:00" + "time": "2025-11-27T13:41:46+00:00" }, { "name": "symfony/yaml", - "version": "v7.3.3", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" + "reference": "48046fbd5567bd1717f278eaa2cfc3131f489984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", - "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/48046fbd5567bd1717f278eaa2cfc3131f489984", + "reference": "48046fbd5567bd1717f278eaa2cfc3131f489984", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", + "php": ">=8.4", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<7.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^7.4|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -9242,7 +8826,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.3" + "source": "https://github.com/symfony/yaml/tree/v8.0.11" }, "funding": [ { @@ -9262,30 +8846,30 @@ "type": "tidelift" } ], - "time": "2025-08-27T11:34:33+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "twig/extra-bundle", - "version": "v3.21.0", + "version": "v3.24.0", "source": { "type": "git", "url": "https://github.com/twigphp/twig-extra-bundle.git", - "reference": "62d1cf47a1aa009cbd07b21045b97d3d5cb79896" + "reference": "6a621fcb1f28aa9ea7b34a99047ae0cdf5b834c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/62d1cf47a1aa009cbd07b21045b97d3d5cb79896", - "reference": "62d1cf47a1aa009cbd07b21045b97d3d5cb79896", + "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/6a621fcb1f28aa9ea7b34a99047ae0cdf5b834c9", + "reference": "6a621fcb1f28aa9ea7b34a99047ae0cdf5b834c9", "shasum": "" }, "require": { "php": ">=8.1.0", - "symfony/framework-bundle": "^5.4|^6.4|^7.0", - "symfony/twig-bundle": "^5.4|^6.4|^7.0", + "symfony/framework-bundle": "^5.4|^6.4|^7.0|^8.0", + "symfony/twig-bundle": "^5.4|^6.4|^7.0|^8.0", "twig/twig": "^3.2|^4.0" }, "require-dev": { - "league/commonmark": "^1.0|^2.0", + "league/commonmark": "^2.7", "symfony/phpunit-bridge": "^6.4|^7.0", "twig/cache-extra": "^3.0", "twig/cssinliner-extra": "^3.0", @@ -9324,7 +8908,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.21.0" + "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.24.0" }, "funding": [ { @@ -9336,26 +8920,26 @@ "type": "tidelift" } ], - "time": "2025-02-19T14:29:33+00:00" + "time": "2026-02-07T08:07:38+00:00" }, { "name": "twig/html-extra", - "version": "v3.21.0", + "version": "v3.24.0", "source": { "type": "git", "url": "https://github.com/twigphp/html-extra.git", - "reference": "5442dd707601c83b8cd4233e37bb10ab8489a90f" + "reference": "313900fb98b371b006a55b1a29241a192634be13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/html-extra/zipball/5442dd707601c83b8cd4233e37bb10ab8489a90f", - "reference": "5442dd707601c83b8cd4233e37bb10ab8489a90f", + "url": "https://api.github.com/repos/twigphp/html-extra/zipball/313900fb98b371b006a55b1a29241a192634be13", + "reference": "313900fb98b371b006a55b1a29241a192634be13", "shasum": "" }, "require": { "php": ">=8.1.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/mime": "^5.4|^6.4|^7.0", + "symfony/mime": "^5.4|^6.4|^7.0|^8.0", "twig/twig": "^3.13|^4.0" }, "require-dev": { @@ -9392,7 +8976,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/html-extra/tree/v3.21.0" + "source": "https://github.com/twigphp/html-extra/tree/v3.24.0" }, "funding": [ { @@ -9404,20 +8988,20 @@ "type": "tidelift" } ], - "time": "2025-02-19T14:29:33+00:00" + "time": "2026-03-17T07:24:08+00:00" }, { "name": "twig/twig", - "version": "v3.21.1", + "version": "v3.25.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d" + "reference": "0dade995be754556af4dcbf8721d45cb3271f9b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/285123877d4dd97dd7c11842ac5fb7e86e60d81d", - "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/0dade995be754556af4dcbf8721d45cb3271f9b4", + "reference": "0dade995be754556af4dcbf8721d45cb3271f9b4", "shasum": "" }, "require": { @@ -9427,7 +9011,8 @@ "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { - "phpstan/phpstan": "^2.0", + "php-cs-fixer/shim": "^3.0@stable", + "phpstan/phpstan": "^2.0@stable", "psr/container": "^1.0|^2.0", "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, @@ -9471,7 +9056,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.21.1" + "source": "https://github.com/twigphp/Twig/tree/v3.25.0" }, "funding": [ { @@ -9483,65 +9068,7 @@ "type": "tidelift" } ], - "time": "2025-05-03T07:21:55+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2026-05-17T07:41:26+00:00" }, { "name": "willdurand/negotiation", @@ -9812,16 +9339,16 @@ }, { "name": "doctrine/data-fixtures", - "version": "2.1.0", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/doctrine/data-fixtures.git", - "reference": "f161e20f04ba5440a09330e156b40f04dd70d47f" + "reference": "bf7ac3a050b54b261cedfb3d0a44733819062275" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/f161e20f04ba5440a09330e156b40f04dd70d47f", - "reference": "f161e20f04ba5440a09330e156b40f04dd70d47f", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/bf7ac3a050b54b261cedfb3d0a44733819062275", + "reference": "bf7ac3a050b54b261cedfb3d0a44733819062275", "shasum": "" }, "require": { @@ -9835,16 +9362,18 @@ "doctrine/phpcr-odm": "<1.3.0" }, "require-dev": { - "doctrine/coding-standard": "^13", + "doctrine/coding-standard": "^14", "doctrine/dbal": "^3.5 || ^4", "doctrine/mongodb-odm": "^1.3.0 || ^2.0.0", "doctrine/orm": "^2.14 || ^3", + "doctrine/phpcr-odm": "^1.8 || ^2.0", "ext-sqlite3": "*", "fig/log-test": "^1", - "phpstan/phpstan": "2.1.17", - "phpunit/phpunit": "10.5.45", - "symfony/cache": "^6.4 || ^7", - "symfony/var-exporter": "^6.4 || ^7" + "jackalope/jackalope-fs": "*", + "phpstan/phpstan": "2.1.46", + "phpunit/phpunit": "10.5.63 || 12.5.12", + "symfony/cache": "^6.4 || ^7 || ^8", + "symfony/var-exporter": "^6.4 || ^7 || ^8" }, "suggest": { "alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)", @@ -9875,7 +9404,7 @@ ], "support": { "issues": "https://github.com/doctrine/data-fixtures/issues", - "source": "https://github.com/doctrine/data-fixtures/tree/2.1.0" + "source": "https://github.com/doctrine/data-fixtures/tree/2.2.1" }, "funding": [ { @@ -9891,26 +9420,95 @@ "type": "tidelift" } ], - "time": "2025-07-08T17:48:20+00:00" + "time": "2026-04-01T13:56:01+00:00" }, { - "name": "ergebnis/composer-normalize", - "version": "2.48.2", + "name": "ergebnis/agent-detector", + "version": "1.2.0", "source": { "type": "git", - "url": "https://github.com/ergebnis/composer-normalize.git", - "reference": "86dc9731b8320f49e9be9ad6d8e4de9b8b0e9b8b" + "url": "https://github.com/ergebnis/agent-detector.git", + "reference": "e211f17928c8b95a51e06040792d57f5462fb271" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/composer-normalize/zipball/86dc9731b8320f49e9be9ad6d8e4de9b8b0e9b8b", - "reference": "86dc9731b8320f49e9be9ad6d8e4de9b8b0e9b8b", + "url": "https://api.github.com/repos/ergebnis/agent-detector/zipball/e211f17928c8b95a51e06040792d57f5462fb271", + "reference": "e211f17928c8b95a51e06040792d57f5462fb271", "shasum": "" }, "require": { - "composer-plugin-api": "^2.0.0", - "ergebnis/json": "^1.4.0", - "ergebnis/json-normalizer": "^4.9.0", + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.51.0", + "ergebnis/license": "^2.7.0", + "ergebnis/php-cs-fixer-config": "^6.60.2", + "ergebnis/phpstan-rules": "^2.13.1", + "ergebnis/phpunit-slow-test-detector": "^2.24.0", + "ergebnis/rector-rules": "^1.18.1", + "fakerphp/faker": "^1.24.1", + "infection/infection": "^0.26.6", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.54", + "phpstan/phpstan-deprecation-rules": "^2.0.4", + "phpstan/phpstan-phpunit": "^2.0.16", + "phpstan/phpstan-strict-rules": "^2.0.10", + "phpunit/phpunit": "^9.6.34", + "rector/rector": "^2.4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + }, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\AgentDetector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Provides a detector for detecting the presence of an agent.", + "homepage": "https://github.com/ergebnis/agent-detector", + "support": { + "issues": "https://github.com/ergebnis/agent-detector/issues", + "security": "https://github.com/ergebnis/agent-detector/blob/main/.github/SECURITY.md", + "source": "https://github.com/ergebnis/agent-detector" + }, + "time": "2026-05-07T08:19:07+00:00" + }, + { + "name": "ergebnis/composer-normalize", + "version": "2.52.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/composer-normalize.git", + "reference": "988f83f5e51a42cdd2337e5fcd935432f8dfa33c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/composer-normalize/zipball/988f83f5e51a42cdd2337e5fcd935432f8dfa33c", + "reference": "988f83f5e51a42cdd2337e5fcd935432f8dfa33c", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "ergebnis/json": "^1.4.0", + "ergebnis/json-normalizer": "^4.9.0", "ergebnis/json-printer": "^3.7.0", "ext-json": "*", "justinrainbow/json-schema": "^5.2.12 || ^6.0.0", @@ -9918,27 +9516,27 @@ "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { - "composer/composer": "^2.8.3", + "composer/composer": "^2.9.8", "ergebnis/license": "^2.7.0", - "ergebnis/php-cs-fixer-config": "^6.53.0", - "ergebnis/phpstan-rules": "^2.11.0", - "ergebnis/phpunit-slow-test-detector": "^2.20.0", + "ergebnis/php-cs-fixer-config": "^6.62.1", + "ergebnis/phpstan-rules": "^2.13.1", + "ergebnis/phpunit-slow-test-detector": "^2.24.0", + "ergebnis/rector-rules": "^1.18.1", "fakerphp/faker": "^1.24.1", - "infection/infection": "~0.26.6", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.17", - "phpstan/phpstan-deprecation-rules": "^2.0.3", - "phpstan/phpstan-phpunit": "^2.0.7", - "phpstan/phpstan-strict-rules": "^2.0.6", - "phpunit/phpunit": "^9.6.20", - "rector/rector": "^2.1.4", + "phpstan/phpstan": "^2.1.54", + "phpstan/phpstan-deprecation-rules": "^2.0.4", + "phpstan/phpstan-phpunit": "^2.0.16", + "phpstan/phpstan-strict-rules": "^2.0.11", + "phpunit/phpunit": "^9.6.33", + "rector/rector": "^2.4.3", "symfony/filesystem": "^5.4.41" }, "type": "composer-plugin", "extra": { "class": "Ergebnis\\Composer\\Normalize\\NormalizePlugin", "branch-alias": { - "dev-main": "2.49-dev" + "dev-main": "2.52-dev" }, "plugin-optional": true, "composer-normalize": { @@ -9975,7 +9573,7 @@ "security": "https://github.com/ergebnis/composer-normalize/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/composer-normalize" }, - "time": "2025-09-06T11:42:34+00:00" + "time": "2026-05-15T15:39:24+00:00" }, { "name": "ergebnis/json", @@ -10134,36 +9732,38 @@ }, { "name": "ergebnis/json-pointer", - "version": "3.7.1", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/ergebnis/json-pointer.git", - "reference": "43bef355184e9542635e35dd2705910a3df4c236" + "reference": "b58c3c468a7ff109fdf9a255f17de29ecbe5276c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json-pointer/zipball/43bef355184e9542635e35dd2705910a3df4c236", - "reference": "43bef355184e9542635e35dd2705910a3df4c236", + "url": "https://api.github.com/repos/ergebnis/json-pointer/zipball/b58c3c468a7ff109fdf9a255f17de29ecbe5276c", + "reference": "b58c3c468a7ff109fdf9a255f17de29ecbe5276c", "shasum": "" }, "require": { "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { - "ergebnis/composer-normalize": "^2.43.0", - "ergebnis/data-provider": "^3.2.0", - "ergebnis/license": "^2.4.0", - "ergebnis/php-cs-fixer-config": "^6.32.0", - "ergebnis/phpunit-slow-test-detector": "^2.15.0", - "fakerphp/faker": "^1.23.1", + "ergebnis/composer-normalize": "^2.50.0", + "ergebnis/data-provider": "^3.6.0", + "ergebnis/license": "^2.7.0", + "ergebnis/php-cs-fixer-config": "^6.60.2", + "ergebnis/phpstan-rules": "^2.13.1", + "ergebnis/phpunit-slow-test-detector": "^2.24.0", + "ergebnis/rector-rules": "^1.16.0", + "fakerphp/faker": "^1.24.1", "infection/infection": "~0.26.6", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^1.12.10", - "phpstan/phpstan-deprecation-rules": "^1.2.1", - "phpstan/phpstan-phpunit": "^1.4.0", - "phpstan/phpstan-strict-rules": "^1.6.1", - "phpunit/phpunit": "^9.6.19", - "rector/rector": "^1.2.10" + "phpstan/phpstan": "^2.1.46", + "phpstan/phpstan-deprecation-rules": "^2.0.4", + "phpstan/phpstan-phpunit": "^2.0.16", + "phpstan/phpstan-strict-rules": "^2.0.10", + "phpunit/phpunit": "^9.6.34", + "rector/rector": "^2.4.0" }, "type": "library", "extra": { @@ -10203,7 +9803,7 @@ "security": "https://github.com/ergebnis/json-pointer/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json-pointer" }, - "time": "2025-09-06T09:28:19+00:00" + "time": "2026-04-07T14:52:13+00:00" }, { "name": "ergebnis/json-printer", @@ -10530,22 +10130,23 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.88.2", + "version": "v3.95.2", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99" + "reference": "a28d88a5e172b27e78d0816992b15a9df3da20f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a8d15584bafb0f0d9d938827840060fd4a3ebc99", - "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a28d88a5e172b27e78d0816992b15a9df3da20f1", + "reference": "a28d88a5e172b27e78d0816992b15a9df3da20f1", "shasum": "" }, "require": { "clue/ndjson-react": "^1.3", "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.5", + "ergebnis/agent-detector": "^1.1.1", "ext-filter": "*", "ext-hash": "*", "ext-json": "*", @@ -10554,34 +10155,34 @@ "php": "^7.4 || ^8.0", "react/child-process": "^0.6.6", "react/event-loop": "^1.5", - "react/promise": "^3.3", "react/socket": "^1.16", "react/stream": "^1.4", - "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", - "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0", - "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0", - "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0", - "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0", - "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0", + "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0", + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", "symfony/polyfill-mbstring": "^1.33", "symfony/polyfill-php80": "^1.33", "symfony/polyfill-php81": "^1.33", "symfony/polyfill-php84": "^1.33", - "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2", - "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0" + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.7", - "infection/infection": "^0.31.0", - "justinrainbow/json-schema": "^6.5", - "keradus/cli-executor": "^2.2", + "facile-it/paraunit": "^1.3.1 || ^2.11.0", + "infection/infection": "^0.32.7", + "justinrainbow/json-schema": "^6.8.0", + "keradus/cli-executor": "^2.3", "mikey179/vfsstream": "^1.6.12", - "php-coveralls/php-coveralls": "^2.8", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", - "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2", - "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2" + "php-coveralls/php-coveralls": "^2.9.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.8", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.8", + "phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.55", + "symfony/polyfill-php85": "^1.33", + "symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.8", + "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.8" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -10596,7 +10197,7 @@ "PhpCsFixer\\": "src/" }, "exclude-from-classmap": [ - "src/Fixer/Internal/*" + "src/**/Internal/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -10622,7 +10223,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.2" }, "funding": [ { @@ -10630,39 +10231,41 @@ "type": "github" } ], - "time": "2025-09-27T00:24:15+00:00" + "time": "2026-05-15T09:20:44+00:00" }, { "name": "hautelook/alice-bundle", - "version": "2.15.1", + "version": "2.17.3", "source": { "type": "git", "url": "https://github.com/theofidry/AliceBundle.git", - "reference": "e33d72e4df48b846dd5fb99039dbcfc5401daf2c" + "reference": "3f3ec5248587d0b59b753f90f3f5a0891dd89d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/AliceBundle/zipball/e33d72e4df48b846dd5fb99039dbcfc5401daf2c", - "reference": "e33d72e4df48b846dd5fb99039dbcfc5401daf2c", + "url": "https://api.github.com/repos/theofidry/AliceBundle/zipball/3f3ec5248587d0b59b753f90f3f5a0891dd89d71", + "reference": "3f3ec5248587d0b59b753f90f3f5a0891dd89d71", "shasum": "" }, "require": { "doctrine/data-fixtures": "^1.7 || ^2.0", - "doctrine/doctrine-bundle": "^2.11.3", + "doctrine/doctrine-bundle": "^2.18.2 || ^3.0", "doctrine/orm": "^3.1", "doctrine/persistence": "^3.3.1 || ^4.0", - "php": "^8.2", + "php": "^8.4", "psr/log": "^1.0 || ^2.0 || ^3.0", - "symfony/finder": "^6.4 || ^7.0", - "symfony/framework-bundle": "^6.4 || ^7.0", - "theofidry/alice-data-fixtures": "^1.7" + "symfony/doctrine-bridge": "^7.4 || ^8.0", + "symfony/finder": "^7.4 || ^8.0", + "symfony/framework-bundle": "^7.4 || ^8.0", + "theofidry/alice-data-fixtures": "^1.10" }, "require-dev": { "monolog/monolog": "^3.5", + "php-cs-fixer/shim": "^3.94", "phpspec/prophecy": "^1.14.0", "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.6.17", - "symfony/phpunit-bridge": "^6.4.4 || ^7.0" + "phpunit/phpunit": "^11.5.46", + "symfony/runtime": "*" }, "type": "symfony-bundle", "extra": { @@ -10702,32 +10305,32 @@ ], "support": { "issues": "https://github.com/theofidry/AliceBundle/issues", - "source": "https://github.com/theofidry/AliceBundle/tree/2.15.1" + "source": "https://github.com/theofidry/AliceBundle/tree/2.17.3" }, - "time": "2025-03-27T09:27:25+00:00" + "time": "2026-03-21T21:21:40+00:00" }, { "name": "justinrainbow/json-schema", - "version": "6.6.0", + "version": "6.8.2", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "68ba7677532803cc0c5900dd5a4d730537f2b2f3" + "reference": "2c89ebb95ca9cedc9347f780333f7b25792dcb76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/68ba7677532803cc0c5900dd5a4d730537f2b2f3", - "reference": "68ba7677532803cc0c5900dd5a4d730537f2b2f3", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/2c89ebb95ca9cedc9347f780333f7b25792dcb76", + "reference": "2c89ebb95ca9cedc9347f780333f7b25792dcb76", "shasum": "" }, "require": { "ext-json": "*", - "marc-mabe/php-enum": "^4.0", + "marc-mabe/php-enum": "^4.4", "php": "^7.2 || ^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "3.3.0", - "json-schema/json-schema-test-suite": "^23.2", + "json-schema/json-schema-test-suite": "dev-main", "marc-mabe/php-enum-phpstan": "^2.0", "phpspec/prophecy": "^1.19", "phpstan/phpstan": "^1.12", @@ -10777,9 +10380,9 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.0" + "source": "https://github.com/jsonrainbow/json-schema/tree/6.8.2" }, - "time": "2025-10-10T11:34:09+00:00" + "time": "2026-05-05T05:39:01+00:00" }, { "name": "localheinz/diff", @@ -10971,41 +10574,40 @@ }, { "name": "nelmio/alice", - "version": "3.14.2", + "version": "3.17.0", "source": { "type": "git", "url": "https://github.com/nelmio/alice.git", - "reference": "f353866956ac4760514e24e8d51902d261d50489" + "reference": "eb4db8bc50b0a5a9177aadd9eef4a930b6eaee95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nelmio/alice/zipball/f353866956ac4760514e24e8d51902d261d50489", - "reference": "f353866956ac4760514e24e8d51902d261d50489", + "url": "https://api.github.com/repos/nelmio/alice/zipball/eb4db8bc50b0a5a9177aadd9eef4a930b6eaee95", + "reference": "eb4db8bc50b0a5a9177aadd9eef4a930b6eaee95", "shasum": "" }, "require": { "fakerphp/faker": "^1.10", "myclabs/deep-copy": "^1.10", - "php": "^8.1", - "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "php": "^8.2", + "sebastian/comparator": "^6.0 || ^7.0 || ^8.0", "symfony/polyfill-php84": "^1.31", - "symfony/property-access": "^6.4 || ^7.0", - "symfony/yaml": "^6.0 || ^7.0" + "symfony/property-access": "^7.4 || ^8.0", + "symfony/yaml": "^7.4 || ^8.0" }, "conflict": { - "symfony/framework-bundle": "<6.4.0" + "symfony/framework-bundle": "<7.4.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.1", "phpspec/prophecy": "^1.6", "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.3", - "symfony/config": "^6.4 || ^7.0", - "symfony/dependency-injection": "^6.4 || ^7.0", - "symfony/finder": "^6.4 || ^7.0", - "symfony/http-kernel": "^6.4 || ^7.0", - "symfony/phpunit-bridge": "^6.4 || ^7.0", - "symfony/var-dumper": "^6.4 || ^7.0" + "phpunit/phpunit": "^11 || ^12.5 || ^13", + "symfony/config": "^7.4 || ^8.0", + "symfony/dependency-injection": "^7.4 || ^8.0", + "symfony/finder": "^7.4 || ^8.0", + "symfony/http-kernel": "^7.4 || ^8.0", + "symfony/var-dumper": "^7.4 || ^8.0" }, "suggest": { "theofidry/alice-data-fixtures": "Wrapper for Alice to provide a persistence layer." @@ -11055,7 +10657,7 @@ ], "support": { "issues": "https://github.com/nelmio/alice/issues", - "source": "https://github.com/nelmio/alice/tree/3.14.2" + "source": "https://github.com/nelmio/alice/tree/3.17.0" }, "funding": [ { @@ -11063,20 +10665,20 @@ "type": "github" } ], - "time": "2025-02-26T09:01:07+00:00" + "time": "2026-02-24T16:58:28+00:00" }, { "name": "nikic/php-parser", - "version": "v5.6.1", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -11119,9 +10721,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-08-13T20:13:15+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "phar-io/manifest", @@ -11291,11 +10893,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.31", + "version": "2.1.54", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ead89849d879fe203ce9292c6ef5e7e76f867b96", - "reference": "ead89849d879fe203ce9292c6ef5e7e76f867b96", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8be50c3992107dc837b17da4d140fbbdf9a5c5bd", + "reference": "8be50c3992107dc837b17da4d140fbbdf9a5c5bd", "shasum": "" }, "require": { @@ -11340,25 +10942,25 @@ "type": "github" } ], - "time": "2025-10-10T14:14:11+00:00" + "time": "2026-04-29T13:31:09+00:00" }, { "name": "phpstan/phpstan-doctrine", - "version": "2.0.10", + "version": "2.0.22", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "5eaf37b87288474051469aee9f937fc9d862f330" + "reference": "e87516b034749432d51653c0147e053e476e8c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/5eaf37b87288474051469aee9f937fc9d862f330", - "reference": "5eaf37b87288474051469aee9f937fc9d862f330", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/e87516b034749432d51653c0147e053e476e8c53", + "reference": "e87516b034749432d51653c0147e053e476e8c53", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.13" + "phpstan/phpstan": "^2.1.34" }, "conflict": { "doctrine/collections": "<1.0", @@ -11378,15 +10980,16 @@ "doctrine/lexer": "^2.0 || ^3.0", "doctrine/mongodb-odm": "^2.4.3", "doctrine/orm": "^2.16.0", - "doctrine/persistence": "^2.2.1 || ^3.2", + "doctrine/persistence": "^2.2.1 || ^3.4.3", "gedmo/doctrine-extensions": "^3.8", "nesbot/carbon": "^2.49", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/phpstan-deprecation-rules": "^2.0.2", - "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-phpunit": "^2.0.8", "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^9.6.20", "ramsey/uuid": "^4.2", + "shipmonk/name-collision-detector": "^2.1", "symfony/cache": "^5.4", "symfony/uid": "^5.4 || ^6.4 || ^7.3" }, @@ -11409,29 +11012,32 @@ "MIT" ], "description": "Doctrine extensions for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.10" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.22" }, - "time": "2025-10-06T10:01:02+00:00" + "time": "2026-05-09T08:10:48+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "2.0.7", + "version": "2.0.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "9a9b161baee88a5f5c58d816943cff354ff233dc" + "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/9a9b161baee88a5f5c58d816943cff354ff233dc", - "reference": "9a9b161baee88a5f5c58d816943cff354ff233dc", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6ab598e1bc106e6827fd346ae4a12b4a5d634c32", + "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.18" + "phpstan/phpstan": "^2.1.32" }, "conflict": { "phpunit/phpunit": "<7.0" @@ -11462,24 +11068,27 @@ "MIT" ], "description": "PHPUnit extensions and rules for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.7" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.16" }, - "time": "2025-07-13T11:31:46+00:00" + "time": "2026-02-14T09:05:21+00:00" }, { "name": "phpstan/phpstan-symfony", - "version": "2.0.8", + "version": "2.0.17", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "8820c22d785c235f69bb48da3d41e688bc8a1796" + "reference": "fdd0cb5f08d1980c612d6f259d825ea644ed03f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/8820c22d785c235f69bb48da3d41e688bc8a1796", - "reference": "8820c22d785c235f69bb48da3d41e688bc8a1796", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/fdd0cb5f08d1980c612d6f259d825ea644ed03f4", + "reference": "fdd0cb5f08d1980c612d6f259d825ea644ed03f4", "shasum": "" }, "require": { @@ -11492,7 +11101,7 @@ }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-phpunit": "^2.0.8", "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^9.6", "psr/container": "1.1.2", @@ -11533,42 +11142,46 @@ } ], "description": "Symfony Framework extensions and rules for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.8" + "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.17" }, - "time": "2025-09-07T06:55:50+00:00" + "time": "2026-05-10T08:14:07+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "12.4.0", + "version": "14.1.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c" + "reference": "655533a65696bbc4231cd8027af150dadc40ec88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c", - "reference": "67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/655533a65696bbc4231cd8027af150dadc40ec88", + "reference": "655533a65696bbc4231cd8027af150dadc40ec88", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", + "ext-mbstring": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^5.6.1", - "php": ">=8.3", - "phpunit/php-file-iterator": "^6.0", - "phpunit/php-text-template": "^5.0", - "sebastian/complexity": "^5.0", - "sebastian/environment": "^8.0.3", - "sebastian/lines-of-code": "^4.0", - "sebastian/version": "^6.0", - "theseer/tokenizer": "^1.2.3" + "nikic/php-parser": "^5.7.0", + "php": ">=8.4", + "phpunit/php-text-template": "^6.0", + "sebastian/complexity": "^6.0", + "sebastian/environment": "^9.2", + "sebastian/git-state": "^1.0", + "sebastian/lines-of-code": "^5.0", + "sebastian/version": "^7.0", + "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^12.3.7" + "phpunit/phpunit": "^13.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -11577,7 +11190,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.4.x-dev" + "dev-main": "14.1.x-dev" } }, "autoload": { @@ -11606,7 +11219,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/12.4.0" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/14.1.9" }, "funding": [ { @@ -11626,32 +11239,32 @@ "type": "tidelift" } ], - "time": "2025-09-24T13:44:41+00:00" + "time": "2026-05-16T05:16:14+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "6.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "961bc913d42fe24a257bfff826a5068079ac7782" + "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782", - "reference": "961bc913d42fe24a257bfff826a5068079ac7782", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", + "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -11679,36 +11292,48 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/7.0.0" }, "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/phpunit/php-file-iterator", + "type": "tidelift" } ], - "time": "2025-02-07T04:58:37+00:00" + "time": "2026-02-06T04:33:26+00:00" }, { "name": "phpunit/php-invoker", - "version": "6.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", - "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "suggest": { "ext-pcntl": "*" @@ -11716,7 +11341,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -11743,40 +11368,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" + "source": "https://github.com/sebastianbergmann/php-invoker/tree/7.0.0" }, "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/phpunit/php-invoker", + "type": "tidelift" } ], - "time": "2025-02-07T04:58:58+00:00" + "time": "2026-02-06T04:34:47+00:00" }, { "name": "phpunit/php-text-template", - "version": "5.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" + "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", - "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/a47af19f93f76aa3368303d752aa5272ca3299f4", + "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -11803,40 +11440,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/6.0.0" }, "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/phpunit/php-text-template", + "type": "tidelift" } ], - "time": "2025-02-07T04:59:16+00:00" + "time": "2026-02-06T04:36:37+00:00" }, { "name": "phpunit/php-timer", - "version": "8.0.0", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", - "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/a0e12065831f6ab0d83120dc61513eb8d9a966f6", + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -11863,28 +11512,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "security": "https://github.com/sebastianbergmann/php-timer/security/policy", - "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" + "source": "https://github.com/sebastianbergmann/php-timer/tree/9.0.0" }, "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/phpunit/php-timer", + "type": "tidelift" } ], - "time": "2025-02-07T04:59:38+00:00" + "time": "2026-02-06T04:37:53+00:00" }, { "name": "phpunit/phpunit", - "version": "12.4.1", + "version": "13.1.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fc5413a2e6d240d2f6d9317bdf7f0a24e73de194" + "reference": "38959098d3c10660a189afaa35a94290c1de67bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc5413a2e6d240d2f6d9317bdf7f0a24e73de194", - "reference": "fc5413a2e6d240d2f6d9317bdf7f0a24e73de194", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38959098d3c10660a189afaa35a94290c1de67bb", + "reference": "38959098d3c10660a189afaa35a94290c1de67bb", "shasum": "" }, "require": { @@ -11897,21 +11558,23 @@ "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=8.3", - "phpunit/php-code-coverage": "^12.4.0", - "phpunit/php-file-iterator": "^6.0.0", - "phpunit/php-invoker": "^6.0.0", - "phpunit/php-text-template": "^5.0.0", - "phpunit/php-timer": "^8.0.0", - "sebastian/cli-parser": "^4.2.0", - "sebastian/comparator": "^7.1.3", - "sebastian/diff": "^7.0.0", - "sebastian/environment": "^8.0.3", - "sebastian/exporter": "^7.0.2", - "sebastian/global-state": "^8.0.2", - "sebastian/object-enumerator": "^7.0.0", - "sebastian/type": "^6.0.3", - "sebastian/version": "^6.0.0", + "php": ">=8.4.1", + "phpunit/php-code-coverage": "^14.1.8", + "phpunit/php-file-iterator": "^7.0.0", + "phpunit/php-invoker": "^7.0.0", + "phpunit/php-text-template": "^6.0.0", + "phpunit/php-timer": "^9.0.0", + "sebastian/cli-parser": "^5.0.0", + "sebastian/comparator": "^8.1.3", + "sebastian/diff": "^8.3.0", + "sebastian/environment": "^9.3.0", + "sebastian/exporter": "^8.0.2", + "sebastian/git-state": "^1.0", + "sebastian/global-state": "^9.0.0", + "sebastian/object-enumerator": "^8.0.0", + "sebastian/recursion-context": "^8.0.0", + "sebastian/type": "^7.0.0", + "sebastian/version": "^7.0.0", "staabm/side-effects-detector": "^1.0.5" }, "bin": [ @@ -11920,7 +11583,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.4-dev" + "dev-main": "13.1-dev" } }, "autoload": { @@ -11952,31 +11615,15 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.1" + "source": "https://github.com/sebastianbergmann/phpunit/tree/13.1.10" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "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/phpunit/phpunit", - "type": "tidelift" + "url": "https://phpunit.de/sponsoring.html", + "type": "other" } ], - "time": "2025-10-09T14:08:29+00:00" + "time": "2026-05-15T08:03:56+00:00" }, { "name": "react/cache", @@ -12052,16 +11699,16 @@ }, { "name": "react/child-process", - "version": "v0.6.6", + "version": "v0.6.7", "source": { "type": "git", "url": "https://github.com/reactphp/child-process.git", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/970f0e71945556422ee4570ccbabaedc3cf04ad3", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3", "shasum": "" }, "require": { @@ -12115,7 +11762,7 @@ ], "support": { "issues": "https://github.com/reactphp/child-process/issues", - "source": "https://github.com/reactphp/child-process/tree/v0.6.6" + "source": "https://github.com/reactphp/child-process/tree/v0.6.7" }, "funding": [ { @@ -12123,20 +11770,20 @@ "type": "open_collective" } ], - "time": "2025-01-01T16:37:48+00:00" + "time": "2025-12-23T15:25:20+00:00" }, { "name": "react/dns", - "version": "v1.13.0", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", "shasum": "" }, "require": { @@ -12191,7 +11838,7 @@ ], "support": { "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.13.0" + "source": "https://github.com/reactphp/dns/tree/v1.14.0" }, "funding": [ { @@ -12199,20 +11846,20 @@ "type": "open_collective" } ], - "time": "2024-06-13T14:18:03+00:00" + "time": "2025-11-18T19:34:28+00:00" }, { "name": "react/event-loop", - "version": "v1.5.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, "require": { @@ -12263,7 +11910,7 @@ ], "support": { "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, "funding": [ { @@ -12271,7 +11918,7 @@ "type": "open_collective" } ], - "time": "2023-11-13T13:48:05+00:00" + "time": "2025-11-17T20:46:25+00:00" }, { "name": "react/promise", @@ -12348,16 +11995,16 @@ }, { "name": "react/socket", - "version": "v1.16.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/reactphp/socket.git", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", "shasum": "" }, "require": { @@ -12416,7 +12063,7 @@ ], "support": { "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.16.0" + "source": "https://github.com/reactphp/socket/tree/v1.17.0" }, "funding": [ { @@ -12424,7 +12071,7 @@ "type": "open_collective" } ], - "time": "2024-07-26T10:38:09+00:00" + "time": "2025-11-19T20:47:34+00:00" }, { "name": "react/stream", @@ -12506,21 +12153,21 @@ }, { "name": "rector/rector", - "version": "2.2.3", + "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "d27f976a332a87b5d03553c2e6f04adbe5da034f" + "reference": "891824c6c59f02a56a5dd58ea8edc44e6c0ece29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/d27f976a332a87b5d03553c2e6f04adbe5da034f", - "reference": "d27f976a332a87b5d03553c2e6f04adbe5da034f", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/891824c6c59f02a56a5dd58ea8edc44e6c0ece29", + "reference": "891824c6c59f02a56a5dd58ea8edc44e6c0ece29", "shasum": "" }, "require": { "php": "^7.4|^8.0", - "phpstan/phpstan": "^2.1.26" + "phpstan/phpstan": "^2.1.48" }, "conflict": { "rector/rector-doctrine": "*", @@ -12554,7 +12201,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/2.2.3" + "source": "https://github.com/rectorphp/rector/tree/2.4.3" }, "funding": [ { @@ -12562,32 +12209,32 @@ "type": "github" } ], - "time": "2025-10-11T21:50:23+00:00" + "time": "2026-05-12T11:17:24+00:00" }, { "name": "sebastian/cli-parser", - "version": "4.2.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04" + "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04", - "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/48a4654fa5e48c1c81214e9930048a572d4b23ca", + "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.2-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -12611,7 +12258,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/5.0.0" }, "funding": [ { @@ -12631,31 +12278,31 @@ "type": "tidelift" } ], - "time": "2025-09-14T09:36:45+00:00" + "time": "2026-02-06T04:39:44+00:00" }, { "name": "sebastian/comparator", - "version": "7.1.3", + "version": "8.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148" + "reference": "1edd557042bf4ff9978ec125d8131b147d5c8224" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dc904b4bb3ab070865fa4068cd84f3da8b945148", - "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1edd557042bf4ff9978ec125d8131b147d5c8224", + "reference": "1edd557042bf4ff9978ec125d8131b147d5c8224", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "php": ">=8.3", - "sebastian/diff": "^7.0", - "sebastian/exporter": "^7.0" + "php": ">=8.4", + "sebastian/diff": "^8.3", + "sebastian/exporter": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^12.2" + "phpunit/phpunit": "^13.0" }, "suggest": { "ext-bcmath": "For comparing BcMath\\Number objects" @@ -12663,7 +12310,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "7.1-dev" + "dev-main": "8.1-dev" } }, "autoload": { @@ -12703,7 +12350,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/8.1.4" }, "funding": [ { @@ -12723,33 +12370,33 @@ "type": "tidelift" } ], - "time": "2025-08-20T11:27:00+00:00" + "time": "2026-05-15T08:30:51+00:00" }, { "name": "sebastian/complexity", - "version": "5.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" + "reference": "c5651c795c98093480df79350cb050813fc7a2f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", - "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c5651c795c98093480df79350cb050813fc7a2f3", + "reference": "c5651c795c98093480df79350cb050813fc7a2f3", "shasum": "" }, "require": { "nikic/php-parser": "^5.0", - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -12773,41 +12420,53 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/6.0.0" }, "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/complexity", + "type": "tidelift" } ], - "time": "2025-02-07T04:55:25+00:00" + "time": "2026-02-06T04:41:32+00:00" }, { "name": "sebastian/diff", - "version": "7.0.0", + "version": "8.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + "reference": "b36d33b6e796513de7cb7df053afb3f55eefcd47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", - "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b36d33b6e796513de7cb7df053afb3f55eefcd47", + "reference": "b36d33b6e796513de7cb7df053afb3f55eefcd47", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0", + "phpunit/phpunit": "^13.0", "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.3-dev" } }, "autoload": { @@ -12840,35 +12499,47 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/diff/tree/8.3.0" }, "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/diff", + "type": "tidelift" } ], - "time": "2025-02-07T04:55:46+00:00" + "time": "2026-05-15T04:58:09+00:00" }, { "name": "sebastian/environment", - "version": "8.0.3", + "version": "9.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68" + "reference": "6767059a30e4277ac95ee034809e793528464768" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/24a711b5c916efc6d6e62aa65aa2ec98fef77f68", - "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6767059a30e4277ac95ee034809e793528464768", + "reference": "6767059a30e4277ac95ee034809e793528464768", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "suggest": { "ext-posix": "*" @@ -12876,7 +12547,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "9.3-dev" } }, "autoload": { @@ -12904,7 +12575,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/8.0.3" + "source": "https://github.com/sebastianbergmann/environment/tree/9.3.0" }, "funding": [ { @@ -12924,34 +12595,34 @@ "type": "tidelift" } ], - "time": "2025-08-12T14:11:56+00:00" + "time": "2026-04-15T12:14:03+00:00" }, { "name": "sebastian/exporter", - "version": "7.0.2", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "016951ae10980765e4e7aee491eb288c64e505b7" + "reference": "9cee180ebe62259e3ed48df2212d1fc8cfd971bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", - "reference": "016951ae10980765e4e7aee491eb288c64e505b7", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/9cee180ebe62259e3ed48df2212d1fc8cfd971bb", + "reference": "9cee180ebe62259e3ed48df2212d1fc8cfd971bb", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=8.3", - "sebastian/recursion-context": "^7.0" + "php": ">=8.4", + "sebastian/recursion-context": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -12994,7 +12665,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/8.0.2" }, "funding": [ { @@ -13014,35 +12685,104 @@ "type": "tidelift" } ], - "time": "2025-09-24T06:16:11+00:00" + "time": "2026-04-15T12:38:05+00:00" + }, + { + "name": "sebastian/git-state", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/git-state.git", + "reference": "792a952e0eba55b6960a48aeceb9f371aad1f76b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/git-state/zipball/792a952e0eba55b6960a48aeceb9f371aad1f76b", + "reference": "792a952e0eba55b6960a48aeceb9f371aad1f76b", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.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": "Library for describing the state of a Git checkout", + "homepage": "https://github.com/sebastianbergmann/git-state", + "support": { + "issues": "https://github.com/sebastianbergmann/git-state/issues", + "security": "https://github.com/sebastianbergmann/git-state/security/policy", + "source": "https://github.com/sebastianbergmann/git-state/tree/1.0.0" + }, + "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/git-state", + "type": "tidelift" + } + ], + "time": "2026-03-21T12:54:28+00:00" }, { "name": "sebastian/global-state", - "version": "8.0.2", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "ef1377171613d09edd25b7816f05be8313f9115d" + "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", - "reference": "ef1377171613d09edd25b7816f05be8313f9115d", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e52e3dc22441e6218c710afe72c3042f8fc41ea7", + "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7", "shasum": "" }, "require": { - "php": ">=8.3", - "sebastian/object-reflector": "^5.0", - "sebastian/recursion-context": "^7.0" + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -13068,7 +12808,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/9.0.0" }, "funding": [ { @@ -13088,33 +12828,33 @@ "type": "tidelift" } ], - "time": "2025-08-29T11:29:25+00:00" + "time": "2026-02-06T04:45:13+00:00" }, { "name": "sebastian/lines-of-code", - "version": "4.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" + "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", - "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", + "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", "shasum": "" }, "require": { "nikic/php-parser": "^5.0", - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -13138,42 +12878,54 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/5.0.0" }, "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/lines-of-code", + "type": "tidelift" } ], - "time": "2025-02-07T04:57:28+00:00" + "time": "2026-02-06T04:45:54+00:00" }, { "name": "sebastian/object-enumerator", - "version": "7.0.0", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" + "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", - "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", + "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", "shasum": "" }, "require": { - "php": ">=8.3", - "sebastian/object-reflector": "^5.0", - "sebastian/recursion-context": "^7.0" + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -13196,40 +12948,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/8.0.0" }, "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/object-enumerator", + "type": "tidelift" } ], - "time": "2025-02-07T04:57:48+00:00" + "time": "2026-02-06T04:46:36+00:00" }, { "name": "sebastian/object-reflector", - "version": "5.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "4bfa827c969c98be1e527abd576533293c634f6a" + "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", - "reference": "4bfa827c969c98be1e527abd576533293c634f6a", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", + "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -13252,40 +13016,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/6.0.0" }, "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/object-reflector", + "type": "tidelift" } ], - "time": "2025-02-07T04:58:17+00:00" + "time": "2026-02-06T04:47:13+00:00" }, { "name": "sebastian/recursion-context", - "version": "7.0.1", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" + "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", - "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/74c5af21f6a5833e91767ca068c4d3dfec15317e", + "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -13316,7 +13092,7 @@ "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/7.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/8.0.0" }, "funding": [ { @@ -13336,32 +13112,32 @@ "type": "tidelift" } ], - "time": "2025-08-13T04:44:59+00:00" + "time": "2026-02-06T04:51:28+00:00" }, { "name": "sebastian/type", - "version": "6.0.3", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" + "reference": "42412224607bd3931241bbd17f38e0f972f5a916" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", - "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/42412224607bd3931241bbd17f38e0f972f5a916", + "reference": "42412224607bd3931241bbd17f38e0f972f5a916", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -13385,7 +13161,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" + "source": "https://github.com/sebastianbergmann/type/tree/7.0.0" }, "funding": [ { @@ -13405,29 +13181,29 @@ "type": "tidelift" } ], - "time": "2025-08-09T06:57:12+00:00" + "time": "2026-02-06T04:52:09+00:00" }, { "name": "sebastian/version", - "version": "6.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", - "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ad37a5552c8e2b88572249fdc19b6da7792e021b", + "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -13451,15 +13227,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + "source": "https://github.com/sebastianbergmann/version/tree/7.0.0" }, "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/version", + "type": "tidelift" } ], - "time": "2025-02-07T05:00:38+00:00" + "time": "2026-02-06T04:52:52+00:00" }, { "name": "staabm/side-effects-detector", @@ -13515,20 +13303,20 @@ }, { "name": "symfony/css-selector", - "version": "v7.3.0", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + "reference": "3665cfade90565430909b906394c73c8739e57d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/3665cfade90565430909b906394c73c8739e57d0", + "reference": "3665cfade90565430909b906394c73c8739e57d0", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -13560,7 +13348,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.3.0" + "source": "https://github.com/symfony/css-selector/tree/v8.0.9" }, "funding": [ { @@ -13571,39 +13359,43 @@ "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-25T14:21:43+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { "name": "symfony/debug-bundle", - "version": "v7.3.4", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/debug-bundle.git", - "reference": "30f922edd53dd85238f1f26dbb68a044109f8f0e" + "reference": "d9d127d61fb2aa7f7f324a8b1928767e2211d1bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/30f922edd53dd85238f1f26dbb68a044109f8f0e", - "reference": "30f922edd53dd85238f1f26dbb68a044109f8f0e", + "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/d9d127d61fb2aa7f7f324a8b1928767e2211d1bc", + "reference": "d9d127d61fb2aa7f7f324a8b1928767e2211d1bc", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", "ext-xml": "*", - "php": ">=8.2", - "symfony/config": "^7.3", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "php": ">=8.4", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/twig-bridge": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "require-dev": { - "symfony/web-profiler-bundle": "^6.4|^7.0" + "symfony/web-profiler-bundle": "^7.4|^8.0" }, "type": "symfony-bundle", "autoload": { @@ -13631,7 +13423,7 @@ "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug-bundle/tree/v7.3.4" + "source": "https://github.com/symfony/debug-bundle/tree/v8.0.8" }, "funding": [ { @@ -13651,35 +13443,36 @@ "type": "tidelift" } ], - "time": "2025-09-10T12:00:31+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/maker-bundle", - "version": "v1.64.0", + "version": "v1.67.0", "source": { "type": "git", "url": "https://github.com/symfony/maker-bundle.git", - "reference": "c86da84640b0586e92aee2b276ee3638ef2f425a" + "reference": "6ce8b313845f16bcf385ee3cb31d8b24e30d5516" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/c86da84640b0586e92aee2b276ee3638ef2f425a", - "reference": "c86da84640b0586e92aee2b276ee3638ef2f425a", + "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/6ce8b313845f16bcf385ee3cb31d8b24e30d5516", + "reference": "6ce8b313845f16bcf385ee3cb31d8b24e30d5516", "shasum": "" }, "require": { + "composer-runtime-api": "^2.1", "doctrine/inflector": "^2.0", "nikic/php-parser": "^5.0", "php": ">=8.1", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.2|^3", - "symfony/filesystem": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0" + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0" }, "conflict": { "doctrine/doctrine-bundle": "<2.10", @@ -13687,13 +13480,14 @@ }, "require-dev": { "composer/semver": "^3.0", - "doctrine/doctrine-bundle": "^2.5.0", + "doctrine/doctrine-bundle": "^2.10|^3.0", "doctrine/orm": "^2.15|^3", - "symfony/http-client": "^6.4|^7.0", - "symfony/phpunit-bridge": "^6.4.1|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/security-http": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", + "doctrine/persistence": "^3.1|^4.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/phpunit-bridge": "^6.4.1|^7.0|^8.0", + "symfony/security-core": "^6.4|^7.0|^8.0", + "symfony/security-http": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", "twig/twig": "^3.0|^4.x-dev" }, "type": "symfony-bundle", @@ -13728,7 +13522,7 @@ ], "support": { "issues": "https://github.com/symfony/maker-bundle/issues", - "source": "https://github.com/symfony/maker-bundle/tree/v1.64.0" + "source": "https://github.com/symfony/maker-bundle/tree/v1.67.0" }, "funding": [ { @@ -13739,25 +13533,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-06-23T16:12:08+00:00" + "time": "2026-03-18T13:39:06+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { @@ -13808,7 +13606,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -13828,11 +13626,11 @@ "type": "tidelift" } ], - "time": "2025-01-02T08:10:11+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -13888,7 +13686,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.37.0" }, "funding": [ { @@ -13912,20 +13710,20 @@ }, { "name": "symfony/process", - "version": "v7.3.4", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" + "reference": "26d89e459f037d2873300605d0a07e7a8ef84db0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", + "url": "https://api.github.com/repos/symfony/process/zipball/26d89e459f037d2873300605d0a07e7a8ef84db0", + "reference": "26d89e459f037d2873300605d0a07e7a8ef84db0", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -13953,7 +13751,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.4" + "source": "https://github.com/symfony/process/tree/v8.0.11" }, "funding": [ { @@ -13973,45 +13771,41 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-05-11T16:56:32+00:00" }, { "name": "symfony/web-profiler-bundle", - "version": "v7.3.4", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "f305fa4add690bb7d6b14ab61f37c3bd061a3dd7" + "reference": "5b80ded3fc1c76c6587be3f7b74bd0f8d9e6d747" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/f305fa4add690bb7d6b14ab61f37c3bd061a3dd7", - "reference": "f305fa4add690bb7d6b14ab61f37c3bd061a3dd7", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/5b80ded3fc1c76c6587be3f7b74bd0f8d9e6d747", + "reference": "5b80ded3fc1c76c6587be3f7b74bd0f8d9e6d747", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", - "php": ">=8.2", - "symfony/config": "^7.3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/twig-bundle": "^6.4|^7.0", - "twig/twig": "^3.12" + "php": ">=8.4", + "symfony/config": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/twig-bundle": "^7.4|^8.0" }, "conflict": { - "symfony/form": "<6.4", - "symfony/mailer": "<6.4", - "symfony/messenger": "<6.4", - "symfony/serializer": "<7.2", - "symfony/workflow": "<7.3" + "symfony/serializer": "<7.4", + "symfony/workflow": "<7.4" }, "require-dev": { - "symfony/browser-kit": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/browser-kit": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0" }, "type": "symfony-bundle", "autoload": { @@ -14042,7 +13836,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.3.4" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v8.0.11" }, "funding": [ { @@ -14062,27 +13856,27 @@ "type": "tidelift" } ], - "time": "2025-09-25T08:03:55+00:00" + "time": "2026-05-11T13:06:45+00:00" }, { "name": "theofidry/alice-data-fixtures", - "version": "1.9.0", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/theofidry/AliceDataFixtures.git", - "reference": "2098f47253571fab4bce13f75cd96da9ff5139ab" + "reference": "011a482252ab416f2e4681731e954554d250695e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/AliceDataFixtures/zipball/2098f47253571fab4bce13f75cd96da9ff5139ab", - "reference": "2098f47253571fab4bce13f75cd96da9ff5139ab", + "url": "https://api.github.com/repos/theofidry/AliceDataFixtures/zipball/011a482252ab416f2e4681731e954554d250695e", + "reference": "011a482252ab416f2e4681731e954554d250695e", "shasum": "" }, "require": { "nelmio/alice": "^3.10", - "php": "^8.3", + "php": "^8.4", "psr/log": "^1 || ^2 || ^3", - "webmozart/assert": "^1.10" + "webmozart/assert": "^1.10 || ^2.0" }, "conflict": { "doctrine/data-fixtures": "<1.7.0", @@ -14094,9 +13888,9 @@ "doctrine/persistence": "<2.5.7 || >=3.0 <3.4.0", "doctrine/phpcr-bundle": "<3.0", "doctrine/phpcr-odm": "<2.0.0", - "illuminate/database": "<10.0", + "illuminate/database": "<12.0", "ocramius/proxy-manager": "<2.1", - "symfony/framework-bundle": ">=6.0 <6.4.19", + "symfony/framework-bundle": "<7.4.0", "zendframework/zend-code": "<3.3.1" }, "require-dev": { @@ -14104,10 +13898,9 @@ "phpspec/prophecy": "^1.20.0", "phpspec/prophecy-phpunit": "^2.3.0", "phpunit/phpunit": "^11.5.12", - "symfony/phpunit-bridge": "^7.2" + "symfony/runtime": "*" }, "suggest": { - "alcaeus/mongo-php-adapter": "To use Doctrine with the MongoDB flavour", "doctrine/data-fixtures": "To use Doctrine", "doctrine/dbal": "To use Doctrine with the PHPCR flavour", "doctrine/mongodb": "To use Doctrine with the MongoDB flavour", @@ -14115,8 +13908,7 @@ "doctrine/orm": "To use Doctrine ORM", "doctrine/phpcr-odm": "To use Doctrine with the PHPCR flavour", "illuminate/database": "To use Eloquent", - "jackalope/jackalope-doctrine-dbal": "To use Doctrine with the PHPCR flavour", - "ocramius/proxy-manager": "To avoid database connection on kernel boot" + "jackalope/jackalope-doctrine-dbal": "To use Doctrine with the PHPCR flavour" }, "type": "library", "extra": { @@ -14155,7 +13947,7 @@ ], "support": { "issues": "https://github.com/theofidry/AliceDataFixtures/issues", - "source": "https://github.com/theofidry/AliceDataFixtures/tree/1.9.0" + "source": "https://github.com/theofidry/AliceDataFixtures/tree/1.11.0" }, "funding": [ { @@ -14163,27 +13955,27 @@ "type": "github" } ], - "time": "2025-03-09T12:39:40+00:00" + "time": "2025-12-18T23:35:24+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.1" }, "type": "library", "autoload": { @@ -14205,7 +13997,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.2.3" + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" }, "funding": [ { @@ -14213,41 +14005,41 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-12-08T11:19:18+00:00" }, { "name": "vincentlanglet/twig-cs-fixer", - "version": "3.10.0", + "version": "3.14.0", "source": { "type": "git", "url": "https://github.com/VincentLanglet/Twig-CS-Fixer.git", - "reference": "ee9b6a31d2c2522b2773ecf31f5d29c2e26311a6" + "reference": "599f110f192c31af5deb5736d6c1a970afdf51f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/VincentLanglet/Twig-CS-Fixer/zipball/ee9b6a31d2c2522b2773ecf31f5d29c2e26311a6", - "reference": "ee9b6a31d2c2522b2773ecf31f5d29c2e26311a6", + "url": "https://api.github.com/repos/VincentLanglet/Twig-CS-Fixer/zipball/599f110f192c31af5deb5736d6c1a970afdf51f3", + "reference": "599f110f192c31af5deb5736d6c1a970afdf51f3", "shasum": "" }, "require": { "composer-runtime-api": "^2.0.0", "ext-ctype": "*", - "ext-json": "*", - "php": ">=8.0", + "php": ">=8.1", "symfony/console": "^5.4.9 || ^6.4 || ^7.0 || ^8.0", "symfony/filesystem": "^5.4 || ^6.4 || ^7.0 || ^8.0", "symfony/finder": "^5.4 || ^6.4 || ^7.0 || ^8.0", "symfony/string": "^5.4.42 || ^6.4.10 || ~7.0.10 || ^7.1.3 || ^8.0", "twig/twig": "^3.4", - "webmozart/assert": "^1.10" + "webmozart/assert": "^1.10 || ^2.0" }, "require-dev": { "composer/semver": "^3.2.0", "dereuromark/composer-prefer-lowest": "^0.1.10", "ergebnis/composer-normalize": "^2.29", "friendsofphp/php-cs-fixer": "^3.13.0", - "infection/infection": "^0.26.16 || ^0.29.14", + "infection/infection": "^0.26.16 || ^0.32.0", "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0", "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", "phpstan/phpstan-symfony": "^2.0", @@ -14282,7 +14074,7 @@ "homepage": "https://github.com/VincentLanglet/Twig-CS-Fixer", "support": { "issues": "https://github.com/VincentLanglet/Twig-CS-Fixer/issues", - "source": "https://github.com/VincentLanglet/Twig-CS-Fixer/tree/3.10.0" + "source": "https://github.com/VincentLanglet/Twig-CS-Fixer/tree/3.14.0" }, "funding": [ { @@ -14290,7 +14082,69 @@ "type": "github" } ], - "time": "2025-09-15T11:28:55+00:00" + "time": "2026-02-23T13:21:35+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/eb0d790f735ba6cff25c683a85a1da0eadeff9e4", + "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.3.0" + }, + "time": "2026-04-11T10:33:05+00:00" } ], "aliases": [], @@ -14301,9 +14155,8 @@ "platform": { "php": ">=8.4", "ext-ctype": "*", - "ext-iconv": "*", - "ext-mbstring": "*" + "ext-iconv": "*" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index e6a1b80..6651fba 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -1,4 +1,8 @@ api_platform: + title: 'ITKsites Detection API' + description: 'REST API for ingesting server detection results from the ITK sites server harvester. Detection results are processed asynchronously to track servers, sites, domains, packages, modules, Docker images, and git repositories.' + version: '1.0.0' + mapping: paths: ['%kernel.project_dir%/src/Entity'] formats: diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml index 6d5d369..a62be68 100644 --- a/config/packages/doctrine.yaml +++ b/config/packages/doctrine.yaml @@ -8,17 +8,12 @@ doctrine: #server_version: '16' profiling_collect_backtrace: '%kernel.debug%' - use_savepoints: true orm: - auto_generate_proxy_classes: true - enable_lazy_ghost_objects: true validate_xml_mapping: true naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware - # Opt-in to new mapping driver mode as of Doctrine ORM 2.16, https://github.com/doctrine/orm/pull/10455 - report_fields_where_declared: true + identity_generation_preferences: + Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity auto_mapping: true - controller_resolver: - auto_mapping: false mappings: App: type: attribute @@ -26,6 +21,8 @@ doctrine: dir: '%kernel.project_dir%/src/Entity' prefix: 'App\Entity' alias: App + controller_resolver: + auto_mapping: false when@test: doctrine: @@ -36,8 +33,6 @@ when@test: when@prod: doctrine: orm: - auto_generate_proxy_classes: false - proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' query_cache_driver: type: pool pool: doctrine.system_cache_pool diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 1ad58d7..1aa06cb 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -15,11 +15,11 @@ framework: http_client: scoped_clients: - leantime.client: - base_uri: '%env(APP_LEANTIME_URI)%' - scope: '%env(APP_LEANTIME_URI)%' + economics.client: + base_uri: '%env(APP_ECONOMICS_URI)%' + scope: '%env(APP_ECONOMICS_URI)%' headers: - x-api-key: '%env(APP_LEANTIME_API_KEY)%' + x-api-key: '%env(APP_ECONOMICS_API_KEY)%' when@test: framework: diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 25bfb56..f57e0e7 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -45,6 +45,13 @@ security: - { path: ^/admin, roles: ROLE_ADMIN } # - { path: ^/profile, roles: ROLE_USER } +# Current AAK OIDC setup doesn't support `itksites.local.itkdev.dk` +when@dev: + security: + firewalls: + main: + security: false + when@test: security: password_hashers: diff --git a/config/packages/webpack_encore.yaml b/config/packages/webpack_encore.yaml index d8b3909..0627dee 100644 --- a/config/packages/webpack_encore.yaml +++ b/config/packages/webpack_encore.yaml @@ -39,6 +39,6 @@ framework: # # Available in version 1.2 # cache: true -#when@test: -# webpack_encore: -# strict_mode: false +when@test: + webpack_encore: + strict_mode: false diff --git a/config/reference.php b/config/reference.php new file mode 100644 index 0000000..563c307 --- /dev/null +++ b/config/reference.php @@ -0,0 +1,2021 @@ + [ + * 'App\\' => [ + * 'resource' => '../src/', + * ], + * ], + * ]); + * ``` + * + * @psalm-type ImportsConfig = list + * @psalm-type ParametersConfig = array|Param|null>|Param|null> + * @psalm-type ArgumentsType = list|array + * @psalm-type CallType = array|array{0:string, 1?:ArgumentsType, 2?:bool}|array{method:string, arguments?:ArgumentsType, returns_clone?:bool} + * @psalm-type TagsType = list>> // arrays inside the list must have only one element, with the tag name as the key + * @psalm-type CallbackType = string|array{0:string|ReferenceConfigurator,1:string}|\Closure|ReferenceConfigurator|ExpressionConfigurator + * @psalm-type DeprecationType = array{package: string, version: string, message?: string} + * @psalm-type DefaultsType = array{ + * public?: bool, + * tags?: TagsType, + * resource_tags?: TagsType, + * autowire?: bool, + * autoconfigure?: bool, + * bind?: array, + * } + * @psalm-type InstanceofType = array{ + * shared?: bool, + * lazy?: bool|string, + * public?: bool, + * properties?: array, + * configurator?: CallbackType, + * calls?: list, + * tags?: TagsType, + * resource_tags?: TagsType, + * autowire?: bool, + * bind?: array, + * constructor?: string, + * } + * @psalm-type DefinitionType = array{ + * class?: string, + * file?: string, + * parent?: string, + * shared?: bool, + * synthetic?: bool, + * lazy?: bool|string, + * public?: bool, + * abstract?: bool, + * deprecated?: DeprecationType, + * factory?: CallbackType, + * configurator?: CallbackType, + * arguments?: ArgumentsType, + * properties?: array, + * calls?: list, + * tags?: TagsType, + * resource_tags?: TagsType, + * decorates?: string, + * decoration_inner_name?: string, + * decoration_priority?: int, + * decoration_on_invalid?: 'exception'|'ignore'|null, + * autowire?: bool, + * autoconfigure?: bool, + * bind?: array, + * constructor?: string, + * from_callable?: CallbackType, + * } + * @psalm-type AliasType = string|array{ + * alias: string, + * public?: bool, + * deprecated?: DeprecationType, + * } + * @psalm-type PrototypeType = array{ + * resource: string, + * namespace?: string, + * exclude?: string|list, + * parent?: string, + * shared?: bool, + * lazy?: bool|string, + * public?: bool, + * abstract?: bool, + * deprecated?: DeprecationType, + * factory?: CallbackType, + * arguments?: ArgumentsType, + * properties?: array, + * configurator?: CallbackType, + * calls?: list, + * tags?: TagsType, + * resource_tags?: TagsType, + * autowire?: bool, + * autoconfigure?: bool, + * bind?: array, + * constructor?: string, + * } + * @psalm-type StackType = array{ + * stack: list>, + * public?: bool, + * deprecated?: DeprecationType, + * } + * @psalm-type ServicesConfig = array{ + * _defaults?: DefaultsType, + * _instanceof?: InstanceofType, + * ... + * } + * @psalm-type ExtensionType = array + * @psalm-type FrameworkConfig = array{ + * secret?: scalar|Param|null, + * http_method_override?: bool|Param, // Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. // Default: false + * allowed_http_method_override?: null|list, + * trust_x_sendfile_type_header?: scalar|Param|null, // Set true to enable support for xsendfile in binary file responses. // Default: "%env(bool:default::SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER)%" + * ide?: scalar|Param|null, // Default: "%env(default::SYMFONY_IDE)%" + * test?: bool|Param, + * default_locale?: scalar|Param|null, // Default: "en" + * set_locale_from_accept_language?: bool|Param, // Whether to use the Accept-Language HTTP header to set the Request locale (only when the "_locale" request attribute is not passed). // Default: false + * set_content_language_from_locale?: bool|Param, // Whether to set the Content-Language HTTP header on the Response using the Request locale. // Default: false + * enabled_locales?: list, + * trusted_hosts?: string|list, + * trusted_proxies?: mixed, // Default: ["%env(default::SYMFONY_TRUSTED_PROXIES)%"] + * trusted_headers?: string|list, + * error_controller?: scalar|Param|null, // Default: "error_controller" + * handle_all_throwables?: bool|Param, // HttpKernel will handle all kinds of \Throwable. // Default: true + * csrf_protection?: bool|array{ + * enabled?: scalar|Param|null, // Default: null + * stateless_token_ids?: list, + * check_header?: scalar|Param|null, // Whether to check the CSRF token in a header in addition to a cookie when using stateless protection. // Default: false + * cookie_name?: scalar|Param|null, // The name of the cookie to use when using stateless protection. // Default: "csrf-token" + * }, + * form?: bool|array{ // Form configuration + * enabled?: bool|Param, // Default: true + * csrf_protection?: bool|array{ + * enabled?: scalar|Param|null, // Default: null + * token_id?: scalar|Param|null, // Default: null + * field_name?: scalar|Param|null, // Default: "_token" + * field_attr?: array, + * }, + * }, + * http_cache?: bool|array{ // HTTP cache configuration + * enabled?: bool|Param, // Default: false + * debug?: bool|Param, // Default: "%kernel.debug%" + * trace_level?: "none"|"short"|"full"|Param, + * trace_header?: scalar|Param|null, + * default_ttl?: int|Param, + * private_headers?: list, + * skip_response_headers?: list, + * allow_reload?: bool|Param, + * allow_revalidate?: bool|Param, + * stale_while_revalidate?: int|Param, + * stale_if_error?: int|Param, + * terminate_on_cache_hit?: bool|Param, + * }, + * esi?: bool|array{ // ESI configuration + * enabled?: bool|Param, // Default: false + * }, + * ssi?: bool|array{ // SSI configuration + * enabled?: bool|Param, // Default: false + * }, + * fragments?: bool|array{ // Fragments configuration + * enabled?: bool|Param, // Default: false + * hinclude_default_template?: scalar|Param|null, // Default: null + * path?: scalar|Param|null, // Default: "/_fragment" + * }, + * profiler?: bool|array{ // Profiler configuration + * enabled?: bool|Param, // Default: false + * collect?: bool|Param, // Default: true + * collect_parameter?: scalar|Param|null, // The name of the parameter to use to enable or disable collection on a per request basis. // Default: null + * only_exceptions?: bool|Param, // Default: false + * only_main_requests?: bool|Param, // Default: false + * dsn?: scalar|Param|null, // Default: "file:%kernel.cache_dir%/profiler" + * collect_serializer_data?: true|Param, // Default: true + * }, + * workflows?: bool|array{ + * enabled?: bool|Param, // Default: false + * workflows?: array, + * definition_validators?: list, + * support_strategy?: scalar|Param|null, + * initial_marking?: \BackedEnum|string|list, + * events_to_dispatch?: null|list, + * places?: string|list, + * }>, + * transitions?: list, + * to?: \BackedEnum|string|list, + * weight?: int|Param, // Default: 1 + * metadata?: array, + * }>, + * metadata?: array, + * }>, + * }, + * router?: bool|array{ // Router configuration + * enabled?: bool|Param, // Default: false + * resource?: scalar|Param|null, + * type?: scalar|Param|null, + * default_uri?: scalar|Param|null, // The default URI used to generate URLs in a non-HTTP context. // Default: null + * http_port?: scalar|Param|null, // Default: 80 + * https_port?: scalar|Param|null, // Default: 443 + * strict_requirements?: scalar|Param|null, // set to true to throw an exception when a parameter does not match the requirements set to false to disable exceptions when a parameter does not match the requirements (and return null instead) set to null to disable parameter checks against requirements 'true' is the preferred configuration in development mode, while 'false' or 'null' might be preferred in production // Default: true + * utf8?: bool|Param, // Default: true + * }, + * session?: bool|array{ // Session configuration + * enabled?: bool|Param, // Default: false + * storage_factory_id?: scalar|Param|null, // Default: "session.storage.factory.native" + * handler_id?: scalar|Param|null, // Defaults to using the native session handler, or to the native *file* session handler if "save_path" is not null. + * name?: scalar|Param|null, + * cookie_lifetime?: scalar|Param|null, + * cookie_path?: scalar|Param|null, + * cookie_domain?: scalar|Param|null, + * cookie_secure?: true|false|"auto"|Param, // Default: "auto" + * cookie_httponly?: bool|Param, // Default: true + * cookie_samesite?: null|"lax"|"strict"|"none"|Param, // Default: "lax" + * use_cookies?: bool|Param, + * gc_divisor?: scalar|Param|null, + * gc_probability?: scalar|Param|null, + * gc_maxlifetime?: scalar|Param|null, + * save_path?: scalar|Param|null, // Defaults to "%kernel.cache_dir%/sessions" if the "handler_id" option is not null. + * metadata_update_threshold?: int|Param, // Seconds to wait between 2 session metadata updates. // Default: 0 + * }, + * request?: bool|array{ // Request configuration + * enabled?: bool|Param, // Default: false + * formats?: array>, + * }, + * assets?: bool|array{ // Assets configuration + * enabled?: bool|Param, // Default: true + * strict_mode?: bool|Param, // Throw an exception if an entry is missing from the manifest.json. // Default: false + * version_strategy?: scalar|Param|null, // Default: null + * version?: scalar|Param|null, // Default: null + * version_format?: scalar|Param|null, // Default: "%%s?%%s" + * json_manifest_path?: scalar|Param|null, // Default: null + * base_path?: scalar|Param|null, // Default: "" + * base_urls?: string|list, + * packages?: array, + * }>, + * }, + * asset_mapper?: bool|array{ // Asset Mapper configuration + * enabled?: bool|Param, // Default: true + * paths?: string|array, + * excluded_patterns?: list, + * exclude_dotfiles?: bool|Param, // If true, any files starting with "." will be excluded from the asset mapper. // Default: true + * server?: bool|Param, // If true, a "dev server" will return the assets from the public directory (true in "debug" mode only by default). // Default: true + * public_prefix?: scalar|Param|null, // The public path where the assets will be written to (and served from when "server" is true). // Default: "/assets/" + * missing_import_mode?: "strict"|"warn"|"ignore"|Param, // Behavior if an asset cannot be found when imported from JavaScript or CSS files - e.g. "import './non-existent.js'". "strict" means an exception is thrown, "warn" means a warning is logged, "ignore" means the import is left as-is. // Default: "warn" + * extensions?: array, + * importmap_path?: scalar|Param|null, // The path of the importmap.php file. // Default: "%kernel.project_dir%/importmap.php" + * importmap_polyfill?: scalar|Param|null, // The importmap name that will be used to load the polyfill. Set to false to disable. // Default: "es-module-shims" + * importmap_script_attributes?: array, + * vendor_dir?: scalar|Param|null, // The directory to store JavaScript vendors. // Default: "%kernel.project_dir%/assets/vendor" + * precompress?: bool|array{ // Precompress assets with Brotli, Zstandard and gzip. + * enabled?: bool|Param, // Default: false + * formats?: list, + * extensions?: list, + * }, + * }, + * translator?: bool|array{ // Translator configuration + * enabled?: bool|Param, // Default: true + * fallbacks?: string|list, + * logging?: bool|Param, // Default: false + * formatter?: scalar|Param|null, // Default: "translator.formatter.default" + * cache_dir?: scalar|Param|null, // Default: "%kernel.cache_dir%/translations" + * default_path?: scalar|Param|null, // The default path used to load translations. // Default: "%kernel.project_dir%/translations" + * paths?: list, + * pseudo_localization?: bool|array{ + * enabled?: bool|Param, // Default: false + * accents?: bool|Param, // Default: true + * expansion_factor?: float|Param, // Default: 1.0 + * brackets?: bool|Param, // Default: true + * parse_html?: bool|Param, // Default: false + * localizable_html_attributes?: list, + * }, + * providers?: array, + * locales?: list, + * }>, + * globals?: array, + * domain?: string|Param, + * }>, + * }, + * validation?: bool|array{ // Validation configuration + * enabled?: bool|Param, // Default: true + * enable_attributes?: bool|Param, // Default: true + * static_method?: string|list, + * translation_domain?: scalar|Param|null, // Default: "validators" + * email_validation_mode?: "html5"|"html5-allow-no-tld"|"strict"|Param, // Default: "html5" + * mapping?: array{ + * paths?: list, + * }, + * not_compromised_password?: bool|array{ + * enabled?: bool|Param, // When disabled, compromised passwords will be accepted as valid. // Default: true + * endpoint?: scalar|Param|null, // API endpoint for the NotCompromisedPassword Validator. // Default: null + * }, + * disable_translation?: bool|Param, // Default: false + * auto_mapping?: array, + * }>, + * }, + * serializer?: bool|array{ // Serializer configuration + * enabled?: bool|Param, // Default: true + * enable_attributes?: bool|Param, // Default: true + * name_converter?: scalar|Param|null, + * circular_reference_handler?: scalar|Param|null, + * max_depth_handler?: scalar|Param|null, + * mapping?: array{ + * paths?: list, + * }, + * default_context?: array, + * named_serializers?: array, + * include_built_in_normalizers?: bool|Param, // Whether to include the built-in normalizers // Default: true + * include_built_in_encoders?: bool|Param, // Whether to include the built-in encoders // Default: true + * }>, + * }, + * property_access?: bool|array{ // Property access configuration + * enabled?: bool|Param, // Default: true + * magic_call?: bool|Param, // Default: false + * magic_get?: bool|Param, // Default: true + * magic_set?: bool|Param, // Default: true + * throw_exception_on_invalid_index?: bool|Param, // Default: false + * throw_exception_on_invalid_property_path?: bool|Param, // Default: true + * }, + * type_info?: bool|array{ // Type info configuration + * enabled?: bool|Param, // Default: true + * aliases?: array, + * }, + * property_info?: bool|array{ // Property info configuration + * enabled?: bool|Param, // Default: true + * with_constructor_extractor?: bool|Param, // Registers the constructor extractor. // Default: true + * }, + * cache?: array{ // Cache configuration + * prefix_seed?: scalar|Param|null, // Used to namespace cache keys when using several apps with the same shared backend. // Default: "_%kernel.project_dir%.%kernel.container_class%" + * app?: scalar|Param|null, // App related cache pools configuration. // Default: "cache.adapter.filesystem" + * system?: scalar|Param|null, // System related cache pools configuration. // Default: "cache.adapter.system" + * directory?: scalar|Param|null, // Default: "%kernel.share_dir%/pools/app" + * default_psr6_provider?: scalar|Param|null, + * default_redis_provider?: scalar|Param|null, // Default: "redis://localhost" + * default_valkey_provider?: scalar|Param|null, // Default: "valkey://localhost" + * default_memcached_provider?: scalar|Param|null, // Default: "memcached://localhost" + * default_doctrine_dbal_provider?: scalar|Param|null, // Default: "database_connection" + * default_pdo_provider?: scalar|Param|null, // Default: null + * pools?: array, + * tags?: scalar|Param|null, // Default: null + * public?: bool|Param, // Default: false + * default_lifetime?: scalar|Param|null, // Default lifetime of the pool. + * provider?: scalar|Param|null, // Overwrite the setting from the default provider for this adapter. + * early_expiration_message_bus?: scalar|Param|null, + * clearer?: scalar|Param|null, + * }>, + * }, + * php_errors?: array{ // PHP errors handling configuration + * log?: mixed, // Use the application logger instead of the PHP logger for logging PHP errors. // Default: true + * throw?: bool|Param, // Throw PHP errors as \ErrorException instances. // Default: true + * }, + * exceptions?: array, + * web_link?: bool|array{ // Web links configuration + * enabled?: bool|Param, // Default: true + * }, + * lock?: bool|string|array{ // Lock configuration + * enabled?: bool|Param, // Default: false + * resources?: string|array>, + * }, + * semaphore?: bool|string|array{ // Semaphore configuration + * enabled?: bool|Param, // Default: false + * resources?: string|array, + * }, + * messenger?: bool|array{ // Messenger configuration + * enabled?: bool|Param, // Default: true + * routing?: array, + * }>, + * serializer?: array{ + * default_serializer?: scalar|Param|null, // Service id to use as the default serializer for the transports. // Default: "messenger.transport.native_php_serializer" + * symfony_serializer?: array{ + * format?: scalar|Param|null, // Serialization format for the messenger.transport.symfony_serializer service (which is not the serializer used by default). // Default: "json" + * context?: array, + * }, + * }, + * transports?: array, + * failure_transport?: scalar|Param|null, // Transport name to send failed messages to (after all retries have failed). // Default: null + * retry_strategy?: string|array{ + * service?: scalar|Param|null, // Service id to override the retry strategy entirely. // Default: null + * max_retries?: int|Param, // Default: 3 + * delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 + * multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries)). // Default: 2 + * max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0 + * jitter?: float|Param, // Randomness to apply to the delay (between 0 and 1). // Default: 0.1 + * }, + * rate_limiter?: scalar|Param|null, // Rate limiter name to use when processing messages. // Default: null + * }>, + * failure_transport?: scalar|Param|null, // Transport name to send failed messages to (after all retries have failed). // Default: null + * stop_worker_on_signals?: int|string|list, + * default_bus?: scalar|Param|null, // Default: null + * buses?: array, + * }>, + * }>, + * }, + * scheduler?: bool|array{ // Scheduler configuration + * enabled?: bool|Param, // Default: false + * }, + * disallow_search_engine_index?: bool|Param, // Enabled by default when debug is enabled. // Default: true + * http_client?: bool|array{ // HTTP Client configuration + * enabled?: bool|Param, // Default: true + * max_host_connections?: int|Param, // The maximum number of connections to a single host. + * default_options?: array{ + * headers?: array, + * vars?: array, + * max_redirects?: int|Param, // The maximum number of redirects to follow. + * http_version?: scalar|Param|null, // The default HTTP version, typically 1.1 or 2.0, leave to null for the best version. + * resolve?: array, + * proxy?: scalar|Param|null, // The URL of the proxy to pass requests through or null for automatic detection. + * no_proxy?: scalar|Param|null, // A comma separated list of hosts that do not require a proxy to be reached. + * timeout?: float|Param, // The idle timeout, defaults to the "default_socket_timeout" ini parameter. + * max_duration?: float|Param, // The maximum execution time for the request+response as a whole. + * bindto?: scalar|Param|null, // A network interface name, IP address, a host name or a UNIX socket to bind to. + * verify_peer?: bool|Param, // Indicates if the peer should be verified in a TLS context. + * verify_host?: bool|Param, // Indicates if the host should exist as a certificate common name. + * cafile?: scalar|Param|null, // A certificate authority file. + * capath?: scalar|Param|null, // A directory that contains multiple certificate authority files. + * local_cert?: scalar|Param|null, // A PEM formatted certificate file. + * local_pk?: scalar|Param|null, // A private key file. + * passphrase?: scalar|Param|null, // The passphrase used to encrypt the "local_pk" file. + * ciphers?: scalar|Param|null, // A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...) + * peer_fingerprint?: array{ // Associative array: hashing algorithm => hash(es). + * sha1?: mixed, + * pin-sha256?: mixed, + * md5?: mixed, + * }, + * crypto_method?: scalar|Param|null, // The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants. + * extra?: array, + * rate_limiter?: scalar|Param|null, // Rate limiter name to use for throttling requests. // Default: null + * caching?: bool|array{ // Caching configuration. + * enabled?: bool|Param, // Default: false + * cache_pool?: string|Param, // The taggable cache pool to use for storing the responses. // Default: "cache.http_client" + * shared?: bool|Param, // Indicates whether the cache is shared (public) or private. // Default: true + * max_ttl?: int|Param, // The maximum TTL (in seconds) allowed for cached responses. Null means no cap. // Default: null + * }, + * retry_failed?: bool|array{ + * enabled?: bool|Param, // Default: false + * retry_strategy?: scalar|Param|null, // service id to override the retry strategy. // Default: null + * http_codes?: int|string|array, + * }>, + * max_retries?: int|Param, // Default: 3 + * delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 + * multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries). // Default: 2 + * max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0 + * jitter?: float|Param, // Randomness in percent (between 0 and 1) to apply to the delay. // Default: 0.1 + * }, + * }, + * mock_response_factory?: scalar|Param|null, // The id of the service that should generate mock responses. It should be either an invokable or an iterable. + * scoped_clients?: array, + * headers?: array, + * max_redirects?: int|Param, // The maximum number of redirects to follow. + * http_version?: scalar|Param|null, // The default HTTP version, typically 1.1 or 2.0, leave to null for the best version. + * resolve?: array, + * proxy?: scalar|Param|null, // The URL of the proxy to pass requests through or null for automatic detection. + * no_proxy?: scalar|Param|null, // A comma separated list of hosts that do not require a proxy to be reached. + * timeout?: float|Param, // The idle timeout, defaults to the "default_socket_timeout" ini parameter. + * max_duration?: float|Param, // The maximum execution time for the request+response as a whole. + * bindto?: scalar|Param|null, // A network interface name, IP address, a host name or a UNIX socket to bind to. + * verify_peer?: bool|Param, // Indicates if the peer should be verified in a TLS context. + * verify_host?: bool|Param, // Indicates if the host should exist as a certificate common name. + * cafile?: scalar|Param|null, // A certificate authority file. + * capath?: scalar|Param|null, // A directory that contains multiple certificate authority files. + * local_cert?: scalar|Param|null, // A PEM formatted certificate file. + * local_pk?: scalar|Param|null, // A private key file. + * passphrase?: scalar|Param|null, // The passphrase used to encrypt the "local_pk" file. + * ciphers?: scalar|Param|null, // A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...). + * peer_fingerprint?: array{ // Associative array: hashing algorithm => hash(es). + * sha1?: mixed, + * pin-sha256?: mixed, + * md5?: mixed, + * }, + * crypto_method?: scalar|Param|null, // The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants. + * extra?: array, + * rate_limiter?: scalar|Param|null, // Rate limiter name to use for throttling requests. // Default: null + * caching?: bool|array{ // Caching configuration. + * enabled?: bool|Param, // Default: false + * cache_pool?: string|Param, // The taggable cache pool to use for storing the responses. // Default: "cache.http_client" + * shared?: bool|Param, // Indicates whether the cache is shared (public) or private. // Default: true + * max_ttl?: int|Param, // The maximum TTL (in seconds) allowed for cached responses. Null means no cap. // Default: null + * }, + * retry_failed?: bool|array{ + * enabled?: bool|Param, // Default: false + * retry_strategy?: scalar|Param|null, // service id to override the retry strategy. // Default: null + * http_codes?: int|string|array, + * }>, + * max_retries?: int|Param, // Default: 3 + * delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 + * multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries). // Default: 2 + * max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0 + * jitter?: float|Param, // Randomness in percent (between 0 and 1) to apply to the delay. // Default: 0.1 + * }, + * }>, + * }, + * mailer?: bool|array{ // Mailer configuration + * enabled?: bool|Param, // Default: false + * message_bus?: scalar|Param|null, // The message bus to use. Defaults to the default bus if the Messenger component is installed. // Default: null + * dsn?: scalar|Param|null, // Default: null + * transports?: array, + * envelope?: array{ // Mailer Envelope configuration + * sender?: scalar|Param|null, + * recipients?: string|list, + * allowed_recipients?: string|list, + * }, + * headers?: array, + * dkim_signer?: bool|array{ // DKIM signer configuration + * enabled?: bool|Param, // Default: false + * key?: scalar|Param|null, // Key content, or path to key (in PEM format with the `file://` prefix) // Default: "" + * domain?: scalar|Param|null, // Default: "" + * select?: scalar|Param|null, // Default: "" + * passphrase?: scalar|Param|null, // The private key passphrase // Default: "" + * options?: array, + * }, + * smime_signer?: bool|array{ // S/MIME signer configuration + * enabled?: bool|Param, // Default: false + * key?: scalar|Param|null, // Path to key (in PEM format) // Default: "" + * certificate?: scalar|Param|null, // Path to certificate (in PEM format without the `file://` prefix) // Default: "" + * passphrase?: scalar|Param|null, // The private key passphrase // Default: null + * extra_certificates?: scalar|Param|null, // Default: null + * sign_options?: int|Param, // Default: null + * }, + * smime_encrypter?: bool|array{ // S/MIME encrypter configuration + * enabled?: bool|Param, // Default: false + * repository?: scalar|Param|null, // S/MIME certificate repository service. This service shall implement the `Symfony\Component\Mailer\EventListener\SmimeCertificateRepositoryInterface`. // Default: "" + * cipher?: int|Param, // A set of algorithms used to encrypt the message // Default: null + * }, + * }, + * secrets?: bool|array{ + * enabled?: bool|Param, // Default: true + * vault_directory?: scalar|Param|null, // Default: "%kernel.project_dir%/config/secrets/%kernel.runtime_environment%" + * local_dotenv_file?: scalar|Param|null, // Default: "%kernel.project_dir%/.env.%kernel.environment%.local" + * decryption_env_var?: scalar|Param|null, // Default: "base64:default::SYMFONY_DECRYPTION_SECRET" + * }, + * notifier?: bool|array{ // Notifier configuration + * enabled?: bool|Param, // Default: false + * message_bus?: scalar|Param|null, // The message bus to use. Defaults to the default bus if the Messenger component is installed. // Default: null + * chatter_transports?: array, + * texter_transports?: array, + * notification_on_failed_messages?: bool|Param, // Default: false + * channel_policy?: array>, + * admin_recipients?: list, + * }, + * rate_limiter?: bool|array{ // Rate limiter configuration + * enabled?: bool|Param, // Default: false + * limiters?: array, + * limit?: int|Param, // The maximum allowed hits in a fixed interval or burst. + * interval?: scalar|Param|null, // Configures the fixed interval if "policy" is set to "fixed_window" or "sliding_window". The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent). + * rate?: array{ // Configures the fill rate if "policy" is set to "token_bucket". + * interval?: scalar|Param|null, // Configures the rate interval. The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent). + * amount?: int|Param, // Amount of tokens to add each interval. // Default: 1 + * }, + * }>, + * }, + * uid?: bool|array{ // Uid configuration + * enabled?: bool|Param, // Default: true + * default_uuid_version?: 7|6|4|1|Param, // Default: 7 + * name_based_uuid_version?: 5|3|Param, // Default: 5 + * name_based_uuid_namespace?: scalar|Param|null, + * time_based_uuid_version?: 7|6|1|Param, // Default: 7 + * time_based_uuid_node?: scalar|Param|null, + * }, + * html_sanitizer?: bool|array{ // HtmlSanitizer configuration + * enabled?: bool|Param, // Default: false + * sanitizers?: array, + * block_elements?: string|list, + * drop_elements?: string|list, + * allow_attributes?: array, + * drop_attributes?: array, + * force_attributes?: array>, + * force_https_urls?: bool|Param, // Transforms URLs using the HTTP scheme to use the HTTPS scheme instead. // Default: false + * allowed_link_schemes?: string|list, + * allowed_link_hosts?: null|string|list, + * allow_relative_links?: bool|Param, // Allows relative URLs to be used in links href attributes. // Default: false + * allowed_media_schemes?: string|list, + * allowed_media_hosts?: null|string|list, + * allow_relative_medias?: bool|Param, // Allows relative URLs to be used in media source attributes (img, audio, video, ...). // Default: false + * with_attribute_sanitizers?: string|list, + * without_attribute_sanitizers?: string|list, + * max_input_length?: int|Param, // The maximum length allowed for the sanitized input. // Default: 0 + * }>, + * }, + * webhook?: bool|array{ // Webhook configuration + * enabled?: bool|Param, // Default: false + * message_bus?: scalar|Param|null, // The message bus to use. // Default: "messenger.default_bus" + * routing?: array, + * }, + * remote-event?: bool|array{ // RemoteEvent configuration + * enabled?: bool|Param, // Default: false + * }, + * json_streamer?: bool|array{ // JSON streamer configuration + * enabled?: bool|Param, // Default: false + * }, + * } + * @psalm-type TwigConfig = array{ + * form_themes?: list, + * globals?: array, + * autoescape_service?: scalar|Param|null, // Default: null + * autoescape_service_method?: scalar|Param|null, // Default: null + * cache?: scalar|Param|null, // Default: true + * charset?: scalar|Param|null, // Default: "%kernel.charset%" + * debug?: bool|Param, // Default: "%kernel.debug%" + * strict_variables?: bool|Param, // Default: "%kernel.debug%" + * auto_reload?: scalar|Param|null, + * optimizations?: int|Param, + * default_path?: scalar|Param|null, // The default path used to load templates. // Default: "%kernel.project_dir%/templates" + * file_name_pattern?: string|list, + * paths?: array, + * date?: array{ // The default format options used by the date filter. + * format?: scalar|Param|null, // Default: "F j, Y H:i" + * interval_format?: scalar|Param|null, // Default: "%d days" + * timezone?: scalar|Param|null, // The timezone used when formatting dates, when set to null, the timezone returned by date_default_timezone_get() is used. // Default: null + * }, + * number_format?: array{ // The default format options for the number_format filter. + * decimals?: int|Param, // Default: 0 + * decimal_point?: scalar|Param|null, // Default: "." + * thousands_separator?: scalar|Param|null, // Default: "," + * }, + * mailer?: array{ + * html_to_text_converter?: scalar|Param|null, // A service implementing the "Symfony\Component\Mime\HtmlToTextConverter\HtmlToTextConverterInterface". // Default: null + * }, + * } + * @psalm-type SecurityConfig = array{ + * access_denied_url?: scalar|Param|null, // Default: null + * session_fixation_strategy?: "none"|"migrate"|"invalidate"|Param, // Default: "migrate" + * expose_security_errors?: \Symfony\Component\Security\Http\Authentication\ExposeSecurityLevel::None|\Symfony\Component\Security\Http\Authentication\ExposeSecurityLevel::AccountStatus|\Symfony\Component\Security\Http\Authentication\ExposeSecurityLevel::All|Param, // Default: "none" + * erase_credentials?: bool|Param, // Default: true + * access_decision_manager?: array{ + * strategy?: "affirmative"|"consensus"|"unanimous"|"priority"|Param, + * service?: scalar|Param|null, + * strategy_service?: scalar|Param|null, + * allow_if_all_abstain?: bool|Param, // Default: false + * allow_if_equal_granted_denied?: bool|Param, // Default: true + * }, + * password_hashers?: array, + * hash_algorithm?: scalar|Param|null, // Name of hashing algorithm for PBKDF2 (i.e. sha256, sha512, etc..) See hash_algos() for a list of supported algorithms. // Default: "sha512" + * key_length?: scalar|Param|null, // Default: 40 + * ignore_case?: bool|Param, // Default: false + * encode_as_base64?: bool|Param, // Default: true + * iterations?: scalar|Param|null, // Default: 5000 + * cost?: int|Param, // Default: null + * memory_cost?: scalar|Param|null, // Default: null + * time_cost?: scalar|Param|null, // Default: null + * id?: scalar|Param|null, + * }>, + * providers?: array, + * }, + * memory?: array{ + * users?: array, + * }>, + * }, + * ldap?: array{ + * service?: scalar|Param|null, + * base_dn?: scalar|Param|null, + * search_dn?: scalar|Param|null, // Default: null + * search_password?: scalar|Param|null, // Default: null + * extra_fields?: list, + * default_roles?: string|list, + * role_fetcher?: scalar|Param|null, // Default: null + * uid_key?: scalar|Param|null, // Default: "sAMAccountName" + * filter?: scalar|Param|null, // Default: "({uid_key}={user_identifier})" + * password_attribute?: scalar|Param|null, // Default: null + * }, + * entity?: array{ + * class?: scalar|Param|null, // The full entity class name of your user class. + * property?: scalar|Param|null, // Default: null + * manager_name?: scalar|Param|null, // Default: null + * }, + * }>, + * firewalls?: array, + * security?: bool|Param, // Default: true + * user_checker?: scalar|Param|null, // The UserChecker to use when authenticating users in this firewall. // Default: "security.user_checker" + * request_matcher?: scalar|Param|null, + * access_denied_url?: scalar|Param|null, + * access_denied_handler?: scalar|Param|null, + * entry_point?: scalar|Param|null, // An enabled authenticator name or a service id that implements "Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface". + * provider?: scalar|Param|null, + * stateless?: bool|Param, // Default: false + * lazy?: bool|Param, // Default: false + * context?: scalar|Param|null, + * logout?: array{ + * enable_csrf?: bool|Param|null, // Default: null + * csrf_token_id?: scalar|Param|null, // Default: "logout" + * csrf_parameter?: scalar|Param|null, // Default: "_csrf_token" + * csrf_token_manager?: scalar|Param|null, + * path?: scalar|Param|null, // Default: "/logout" + * target?: scalar|Param|null, // Default: "/" + * invalidate_session?: bool|Param, // Default: true + * clear_site_data?: string|list<"*"|"cache"|"cookies"|"storage"|"executionContexts"|Param>, + * delete_cookies?: string|array, + * }, + * switch_user?: array{ + * provider?: scalar|Param|null, + * parameter?: scalar|Param|null, // Default: "_switch_user" + * role?: scalar|Param|null, // Default: "ROLE_ALLOWED_TO_SWITCH" + * target_route?: scalar|Param|null, // Default: null + * }, + * required_badges?: list, + * custom_authenticators?: list, + * login_throttling?: array{ + * limiter?: scalar|Param|null, // A service id implementing "Symfony\Component\HttpFoundation\RateLimiter\RequestRateLimiterInterface". + * max_attempts?: int|Param, // Default: 5 + * interval?: scalar|Param|null, // Default: "1 minute" + * lock_factory?: scalar|Param|null, // The service ID of the lock factory used by the login rate limiter (or null to disable locking). // Default: null + * cache_pool?: string|Param, // The cache pool to use for storing the limiter state // Default: "cache.rate_limiter" + * storage_service?: string|Param, // The service ID of a custom storage implementation, this precedes any configured "cache_pool" // Default: null + * }, + * x509?: array{ + * provider?: scalar|Param|null, + * user?: scalar|Param|null, // Default: "SSL_CLIENT_S_DN_Email" + * credentials?: scalar|Param|null, // Default: "SSL_CLIENT_S_DN" + * user_identifier?: scalar|Param|null, // Default: "emailAddress" + * }, + * remote_user?: array{ + * provider?: scalar|Param|null, + * user?: scalar|Param|null, // Default: "REMOTE_USER" + * }, + * login_link?: array{ + * check_route?: scalar|Param|null, // Route that will validate the login link - e.g. "app_login_link_verify". + * check_post_only?: scalar|Param|null, // If true, only HTTP POST requests to "check_route" will be handled by the authenticator. // Default: false + * signature_properties?: list, + * lifetime?: int|Param, // The lifetime of the login link in seconds. // Default: 600 + * max_uses?: int|Param, // Max number of times a login link can be used - null means unlimited within lifetime. // Default: null + * used_link_cache?: scalar|Param|null, // Cache service id used to expired links of max_uses is set. + * success_handler?: scalar|Param|null, // A service id that implements Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface. + * failure_handler?: scalar|Param|null, // A service id that implements Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface. + * provider?: scalar|Param|null, // The user provider to load users from. + * secret?: scalar|Param|null, // Default: "%kernel.secret%" + * always_use_default_target_path?: bool|Param, // Default: false + * default_target_path?: scalar|Param|null, // Default: "/" + * login_path?: scalar|Param|null, // Default: "/login" + * target_path_parameter?: scalar|Param|null, // Default: "_target_path" + * use_referer?: bool|Param, // Default: false + * failure_path?: scalar|Param|null, // Default: null + * failure_forward?: bool|Param, // Default: false + * failure_path_parameter?: scalar|Param|null, // Default: "_failure_path" + * }, + * form_login?: array{ + * provider?: scalar|Param|null, + * remember_me?: bool|Param, // Default: true + * success_handler?: scalar|Param|null, + * failure_handler?: scalar|Param|null, + * check_path?: scalar|Param|null, // Default: "/login_check" + * use_forward?: bool|Param, // Default: false + * login_path?: scalar|Param|null, // Default: "/login" + * username_parameter?: scalar|Param|null, // Default: "_username" + * password_parameter?: scalar|Param|null, // Default: "_password" + * csrf_parameter?: scalar|Param|null, // Default: "_csrf_token" + * csrf_token_id?: scalar|Param|null, // Default: "authenticate" + * enable_csrf?: bool|Param, // Default: false + * post_only?: bool|Param, // Default: true + * form_only?: bool|Param, // Default: false + * always_use_default_target_path?: bool|Param, // Default: false + * default_target_path?: scalar|Param|null, // Default: "/" + * target_path_parameter?: scalar|Param|null, // Default: "_target_path" + * use_referer?: bool|Param, // Default: false + * failure_path?: scalar|Param|null, // Default: null + * failure_forward?: bool|Param, // Default: false + * failure_path_parameter?: scalar|Param|null, // Default: "_failure_path" + * }, + * form_login_ldap?: array{ + * provider?: scalar|Param|null, + * remember_me?: bool|Param, // Default: true + * success_handler?: scalar|Param|null, + * failure_handler?: scalar|Param|null, + * check_path?: scalar|Param|null, // Default: "/login_check" + * use_forward?: bool|Param, // Default: false + * login_path?: scalar|Param|null, // Default: "/login" + * username_parameter?: scalar|Param|null, // Default: "_username" + * password_parameter?: scalar|Param|null, // Default: "_password" + * csrf_parameter?: scalar|Param|null, // Default: "_csrf_token" + * csrf_token_id?: scalar|Param|null, // Default: "authenticate" + * enable_csrf?: bool|Param, // Default: false + * post_only?: bool|Param, // Default: true + * form_only?: bool|Param, // Default: false + * always_use_default_target_path?: bool|Param, // Default: false + * default_target_path?: scalar|Param|null, // Default: "/" + * target_path_parameter?: scalar|Param|null, // Default: "_target_path" + * use_referer?: bool|Param, // Default: false + * failure_path?: scalar|Param|null, // Default: null + * failure_forward?: bool|Param, // Default: false + * failure_path_parameter?: scalar|Param|null, // Default: "_failure_path" + * service?: scalar|Param|null, // Default: "ldap" + * dn_string?: scalar|Param|null, // Default: "{user_identifier}" + * query_string?: scalar|Param|null, + * search_dn?: scalar|Param|null, // Default: "" + * search_password?: scalar|Param|null, // Default: "" + * }, + * json_login?: array{ + * provider?: scalar|Param|null, + * remember_me?: bool|Param, // Default: true + * success_handler?: scalar|Param|null, + * failure_handler?: scalar|Param|null, + * check_path?: scalar|Param|null, // Default: "/login_check" + * use_forward?: bool|Param, // Default: false + * login_path?: scalar|Param|null, // Default: "/login" + * username_path?: scalar|Param|null, // Default: "username" + * password_path?: scalar|Param|null, // Default: "password" + * }, + * json_login_ldap?: array{ + * provider?: scalar|Param|null, + * remember_me?: bool|Param, // Default: true + * success_handler?: scalar|Param|null, + * failure_handler?: scalar|Param|null, + * check_path?: scalar|Param|null, // Default: "/login_check" + * use_forward?: bool|Param, // Default: false + * login_path?: scalar|Param|null, // Default: "/login" + * username_path?: scalar|Param|null, // Default: "username" + * password_path?: scalar|Param|null, // Default: "password" + * service?: scalar|Param|null, // Default: "ldap" + * dn_string?: scalar|Param|null, // Default: "{user_identifier}" + * query_string?: scalar|Param|null, + * search_dn?: scalar|Param|null, // Default: "" + * search_password?: scalar|Param|null, // Default: "" + * }, + * access_token?: array{ + * provider?: scalar|Param|null, + * remember_me?: bool|Param, // Default: true + * success_handler?: scalar|Param|null, + * failure_handler?: scalar|Param|null, + * realm?: scalar|Param|null, // Default: null + * token_extractors?: string|list, + * token_handler?: string|array{ + * id?: scalar|Param|null, + * oidc_user_info?: string|array{ + * base_uri?: scalar|Param|null, // Base URI of the userinfo endpoint on the OIDC server, or the OIDC server URI to use the discovery (require "discovery" to be configured). + * discovery?: array{ // Enable the OIDC discovery. + * cache?: array{ + * id?: scalar|Param|null, // Cache service id to use to cache the OIDC discovery configuration. + * }, + * }, + * claim?: scalar|Param|null, // Claim which contains the user identifier (e.g. sub, email, etc.). // Default: "sub" + * client?: scalar|Param|null, // HttpClient service id to use to call the OIDC server. + * }, + * oidc?: array{ + * discovery?: array{ // Enable the OIDC discovery. + * base_uri?: string|list, + * cache?: array{ + * id?: scalar|Param|null, // Cache service id to use to cache the OIDC discovery configuration. + * }, + * }, + * claim?: scalar|Param|null, // Claim which contains the user identifier (e.g.: sub, email..). // Default: "sub" + * audience?: scalar|Param|null, // Audience set in the token, for validation purpose. + * issuers?: list, + * algorithms?: list, + * keyset?: scalar|Param|null, // JSON-encoded JWKSet used to sign the token (must contain a list of valid public keys). + * encryption?: bool|array{ + * enabled?: bool|Param, // Default: false + * enforce?: bool|Param, // When enabled, the token shall be encrypted. // Default: false + * algorithms?: list, + * keyset?: scalar|Param|null, // JSON-encoded JWKSet used to decrypt the token (must contain a list of valid private keys). + * }, + * }, + * cas?: array{ + * validation_url?: scalar|Param|null, // CAS server validation URL + * prefix?: scalar|Param|null, // CAS prefix // Default: "cas" + * http_client?: scalar|Param|null, // HTTP Client service // Default: null + * }, + * oauth2?: scalar|Param|null, + * }, + * }, + * http_basic?: array{ + * provider?: scalar|Param|null, + * realm?: scalar|Param|null, // Default: "Secured Area" + * }, + * http_basic_ldap?: array{ + * provider?: scalar|Param|null, + * realm?: scalar|Param|null, // Default: "Secured Area" + * service?: scalar|Param|null, // Default: "ldap" + * dn_string?: scalar|Param|null, // Default: "{user_identifier}" + * query_string?: scalar|Param|null, + * search_dn?: scalar|Param|null, // Default: "" + * search_password?: scalar|Param|null, // Default: "" + * }, + * remember_me?: array{ + * secret?: scalar|Param|null, // Default: "%kernel.secret%" + * service?: scalar|Param|null, + * user_providers?: string|list, + * catch_exceptions?: bool|Param, // Default: true + * signature_properties?: list, + * token_provider?: string|array{ + * service?: scalar|Param|null, // The service ID of a custom remember-me token provider. + * doctrine?: bool|array{ + * enabled?: bool|Param, // Default: false + * connection?: scalar|Param|null, // Default: null + * }, + * }, + * token_verifier?: scalar|Param|null, // The service ID of a custom rememberme token verifier. + * name?: scalar|Param|null, // Default: "REMEMBERME" + * lifetime?: int|Param, // Default: 31536000 + * path?: scalar|Param|null, // Default: "/" + * domain?: scalar|Param|null, // Default: null + * secure?: true|false|"auto"|Param, // Default: false + * httponly?: bool|Param, // Default: true + * samesite?: null|"lax"|"strict"|"none"|Param, // Default: null + * always_remember_me?: bool|Param, // Default: false + * remember_me_parameter?: scalar|Param|null, // Default: "_remember_me" + * }, + * }>, + * access_control?: list, + * attributes?: array, + * route?: scalar|Param|null, // Default: null + * methods?: string|list, + * allow_if?: scalar|Param|null, // Default: null + * roles?: string|list, + * }>, + * role_hierarchy?: array>, + * } + * @psalm-type DoctrineConfig = array{ + * dbal?: array{ + * default_connection?: scalar|Param|null, + * types?: array, + * driver_schemes?: array, + * connections?: array, + * mapping_types?: array, + * default_table_options?: array, + * schema_manager_factory?: scalar|Param|null, // Default: "doctrine.dbal.default_schema_manager_factory" + * result_cache?: scalar|Param|null, + * replicas?: array, + * }>, + * }, + * orm?: array{ + * default_entity_manager?: scalar|Param|null, + * enable_native_lazy_objects?: bool|Param, // Deprecated: The "enable_native_lazy_objects" option is deprecated and will be removed in DoctrineBundle 4.0, as native lazy objects are now always enabled. // Default: true + * controller_resolver?: bool|array{ + * enabled?: bool|Param, // Default: true + * auto_mapping?: bool|Param, // Deprecated: The "doctrine.orm.controller_resolver.auto_mapping.auto_mapping" option is deprecated and will be removed in DoctrineBundle 4.0, as it only accepts `false` since 3.0. // Set to true to enable using route placeholders as lookup criteria when the primary key doesn't match the argument name // Default: false + * evict_cache?: bool|Param, // Set to true to fetch the entity from the database instead of using the cache, if any // Default: false + * }, + * entity_managers?: array, + * }>, + * }>, + * }, + * connection?: scalar|Param|null, + * class_metadata_factory_name?: scalar|Param|null, // Default: "Doctrine\\ORM\\Mapping\\ClassMetadataFactory" + * default_repository_class?: scalar|Param|null, // Default: "Doctrine\\ORM\\EntityRepository" + * auto_mapping?: scalar|Param|null, // Default: false + * naming_strategy?: scalar|Param|null, // Default: "doctrine.orm.naming_strategy.default" + * quote_strategy?: scalar|Param|null, // Default: "doctrine.orm.quote_strategy.default" + * typed_field_mapper?: scalar|Param|null, // Default: "doctrine.orm.typed_field_mapper.default" + * entity_listener_resolver?: scalar|Param|null, // Default: null + * fetch_mode_subselect_batch_size?: scalar|Param|null, + * repository_factory?: scalar|Param|null, // Default: "doctrine.orm.container_repository_factory" + * schema_ignore_classes?: list, + * validate_xml_mapping?: bool|Param, // Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.14 and will be mandatory in ORM 3.0. See https://github.com/doctrine/orm/pull/6728. // Default: false + * second_level_cache?: array{ + * region_cache_driver?: string|array{ + * type?: scalar|Param|null, // Default: null + * id?: scalar|Param|null, + * pool?: scalar|Param|null, + * }, + * region_lock_lifetime?: scalar|Param|null, // Default: 60 + * log_enabled?: bool|Param, // Default: true + * region_lifetime?: scalar|Param|null, // Default: 3600 + * enabled?: bool|Param, // Default: true + * factory?: scalar|Param|null, + * regions?: array, + * loggers?: array, + * }, + * hydrators?: array, + * mappings?: array, + * dql?: array{ + * string_functions?: array, + * numeric_functions?: array, + * datetime_functions?: array, + * }, + * filters?: array, + * }>, + * identity_generation_preferences?: array, + * }>, + * resolve_target_entities?: array, + * }, + * } + * @psalm-type NelmioCorsConfig = array{ + * defaults?: array{ + * allow_credentials?: bool|Param, // Default: false + * allow_origin?: list, + * allow_headers?: list, + * allow_methods?: list, + * allow_private_network?: bool|Param, // Default: false + * expose_headers?: list, + * max_age?: scalar|Param|null, // Default: 0 + * hosts?: list, + * origin_regex?: bool|Param, // Default: false + * forced_allow_origin_value?: scalar|Param|null, // Default: null + * skip_same_as_origin?: bool|Param, // Default: true + * }, + * paths?: array, + * allow_headers?: list, + * allow_methods?: list, + * allow_private_network?: bool|Param, + * expose_headers?: list, + * max_age?: scalar|Param|null, // Default: 0 + * hosts?: list, + * origin_regex?: bool|Param, + * forced_allow_origin_value?: scalar|Param|null, // Default: null + * skip_same_as_origin?: bool|Param, + * }>, + * } + * @psalm-type ApiPlatformConfig = array{ + * title?: scalar|Param|null, // The title of the API. // Default: "" + * description?: scalar|Param|null, // The description of the API. // Default: "" + * version?: scalar|Param|null, // The version of the API. // Default: "0.0.0" + * show_webby?: bool|Param, // If true, show Webby on the documentation page // Default: true + * use_symfony_listeners?: bool|Param, // Uses Symfony event listeners instead of the ApiPlatform\Symfony\Controller\MainController. // Default: false + * name_converter?: scalar|Param|null, // Specify a name converter to use. // Default: null + * asset_package?: scalar|Param|null, // Specify an asset package name to use. // Default: null + * path_segment_name_generator?: scalar|Param|null, // Specify a path name generator to use. // Default: "api_platform.metadata.path_segment_name_generator.underscore" + * inflector?: scalar|Param|null, // Specify an inflector to use. // Default: "api_platform.metadata.inflector" + * validator?: array{ + * serialize_payload_fields?: mixed, // Set to null to serialize all payload fields when a validation error is thrown, or set the fields you want to include explicitly. // Default: [] + * query_parameter_validation?: bool|Param, // Deprecated: Will be removed in API Platform 5.0. // Default: true + * }, + * jsonapi?: array{ + * use_iri_as_id?: bool|Param, // Set to false to use entity identifiers instead of IRIs as the "id" field in JSON:API responses. // Default: true + * }, + * eager_loading?: bool|array{ + * enabled?: bool|Param, // Default: true + * fetch_partial?: bool|Param, // Fetch only partial data according to serialization groups. If enabled, Doctrine ORM entities will not work as expected if any of the other fields are used. // Default: false + * max_joins?: int|Param, // Max number of joined relations before EagerLoading throws a RuntimeException // Default: 30 + * force_eager?: bool|Param, // Force join on every relation. If disabled, it will only join relations having the EAGER fetch mode. // Default: true + * }, + * handle_symfony_errors?: bool|Param, // Allows to handle symfony exceptions. // Default: false + * enable_swagger?: bool|Param, // Enable the Swagger documentation and export. // Default: true + * enable_json_streamer?: bool|Param, // Enable json streamer. // Default: false + * enable_swagger_ui?: bool|Param, // Enable Swagger UI // Default: true + * enable_re_doc?: bool|Param, // Enable ReDoc // Default: true + * enable_scalar?: bool|Param, // Enable Scalar API Reference // Default: true + * enable_entrypoint?: bool|Param, // Enable the entrypoint // Default: true + * enable_docs?: bool|Param, // Enable the docs // Default: true + * enable_profiler?: bool|Param, // Enable the data collector and the WebProfilerBundle integration. // Default: true + * enable_phpdoc_parser?: bool|Param, // Enable resource metadata collector using PHPStan PhpDocParser. // Default: true + * enable_link_security?: bool|Param, // Deprecated: This option is always enabled and will be removed in API Platform 5.0. // Enable security for Links (sub resources). // Default: true + * collection?: array{ + * exists_parameter_name?: scalar|Param|null, // The name of the query parameter to filter on nullable field values. // Default: "exists" + * order?: scalar|Param|null, // The default order of results. // Default: "ASC" + * order_parameter_name?: scalar|Param|null, // The name of the query parameter to order results. // Default: "order" + * order_nulls_comparison?: "nulls_smallest"|"nulls_largest"|"nulls_always_first"|"nulls_always_last"|Param|null, // The nulls comparison strategy. // Default: null + * pagination?: bool|array{ + * enabled?: bool|Param, // Default: true + * page_parameter_name?: scalar|Param|null, // The default name of the parameter handling the page number. // Default: "page" + * enabled_parameter_name?: scalar|Param|null, // The name of the query parameter to enable or disable pagination. // Default: "pagination" + * items_per_page_parameter_name?: scalar|Param|null, // The name of the query parameter to set the number of items per page. // Default: "itemsPerPage" + * partial_parameter_name?: scalar|Param|null, // The name of the query parameter to enable or disable partial pagination. // Default: "partial" + * }, + * }, + * mapping?: array{ + * imports?: list, + * paths?: list, + * }, + * resource_class_directories?: list, + * serializer?: array{ + * hydra_prefix?: bool|Param, // Use the "hydra:" prefix. // Default: false + * }, + * doctrine?: bool|array{ + * enabled?: bool|Param, // Default: true + * }, + * doctrine_mongodb_odm?: bool|array{ + * enabled?: bool|Param, // Default: false + * }, + * oauth?: bool|array{ + * enabled?: bool|Param, // Default: false + * clientId?: scalar|Param|null, // The oauth client id. // Default: "" + * clientSecret?: scalar|Param|null, // The OAuth client secret. Never use this parameter in your production environment. It exposes crucial security information. This feature is intended for dev/test environments only. Enable "oauth.pkce" instead // Default: "" + * pkce?: bool|Param, // Enable the oauth PKCE. // Default: false + * type?: scalar|Param|null, // The oauth type. // Default: "oauth2" + * flow?: scalar|Param|null, // The oauth flow grant type. // Default: "application" + * tokenUrl?: scalar|Param|null, // The oauth token url. // Default: "" + * authorizationUrl?: scalar|Param|null, // The oauth authentication url. // Default: "" + * refreshUrl?: scalar|Param|null, // The oauth refresh url. // Default: "" + * scopes?: list, + * }, + * graphql?: bool|array{ + * enabled?: bool|Param, // Default: false + * default_ide?: scalar|Param|null, // Default: "graphiql" + * graphiql?: bool|array{ + * enabled?: bool|Param, // Default: false + * }, + * introspection?: bool|array{ + * enabled?: bool|Param, // Default: true + * }, + * max_query_depth?: int|Param, // Default: 20 + * graphql_playground?: bool|array{ // Deprecated: The "graphql_playground" configuration is deprecated and will be ignored. + * enabled?: bool|Param, // Default: false + * }, + * max_query_complexity?: int|Param, // Default: 500 + * nesting_separator?: scalar|Param|null, // The separator to use to filter nested fields. // Default: "_" + * collection?: array{ + * pagination?: bool|array{ + * enabled?: bool|Param, // Default: true + * }, + * }, + * }, + * swagger?: array{ + * persist_authorization?: bool|Param, // Persist the SwaggerUI Authorization in the localStorage. // Default: false + * versions?: list, + * api_keys?: array, + * http_auth?: array, + * swagger_ui_extra_configuration?: mixed, // To pass extra configuration to Swagger UI, like docExpansion or filter. // Default: [] + * }, + * http_cache?: array{ + * public?: bool|Param|null, // To make all responses public by default. // Default: null + * invalidation?: bool|array{ // Enable the tags-based cache invalidation system. + * enabled?: bool|Param, // Default: false + * varnish_urls?: list, + * urls?: list, + * scoped_clients?: list, + * max_header_length?: int|Param, // Max header length supported by the cache server. // Default: 7500 + * request_options?: mixed, // To pass options to the client charged with the request. // Default: [] + * purger?: scalar|Param|null, // Specify a purger to use (available values: "api_platform.http_cache.purger.varnish.ban", "api_platform.http_cache.purger.varnish.xkey", "api_platform.http_cache.purger.souin"). // Default: "api_platform.http_cache.purger.varnish" + * xkey?: array{ // Deprecated: The "xkey" configuration is deprecated, use your own purger to customize surrogate keys or the appropriate parameters. + * glue?: scalar|Param|null, // xkey glue between keys // Default: " " + * }, + * }, + * }, + * mercure?: bool|array{ + * enabled?: bool|Param, // Default: false + * hub_url?: scalar|Param|null, // The URL sent in the Link HTTP header. If not set, will default to the URL for MercureBundle's default hub. // Default: null + * include_type?: bool|Param, // Always include @type in updates (including delete ones). // Default: false + * }, + * messenger?: bool|array{ + * enabled?: bool|Param, // Default: true + * }, + * elasticsearch?: bool|array{ + * enabled?: bool|Param, // Default: false + * hosts?: list, + * ssl_ca_bundle?: scalar|Param|null, // Path to the SSL CA bundle file for Elasticsearch SSL verification. // Default: null + * ssl_verification?: bool|Param, // Enable or disable SSL verification for Elasticsearch connections. // Default: true + * client?: "elasticsearch"|"opensearch"|Param, // The search engine client to use: "elasticsearch" or "opensearch". // Default: "elasticsearch" + * }, + * openapi?: array{ + * contact?: array{ + * name?: scalar|Param|null, // The identifying name of the contact person/organization. // Default: null + * url?: scalar|Param|null, // The URL pointing to the contact information. MUST be in the format of a URL. // Default: null + * email?: scalar|Param|null, // The email address of the contact person/organization. MUST be in the format of an email address. // Default: null + * }, + * termsOfService?: scalar|Param|null, // A URL to the Terms of Service for the API. MUST be in the format of a URL. // Default: null + * tags?: list, + * license?: array{ + * name?: scalar|Param|null, // The license name used for the API. // Default: null + * url?: scalar|Param|null, // URL to the license used for the API. MUST be in the format of a URL. // Default: null + * identifier?: scalar|Param|null, // An SPDX license expression for the API. The identifier field is mutually exclusive of the url field. // Default: null + * }, + * swagger_ui_extra_configuration?: mixed, // To pass extra configuration to Swagger UI, like docExpansion or filter. // Default: [] + * scalar_extra_configuration?: mixed, // To pass extra configuration to Scalar API Reference, like theme or darkMode. // Default: [] + * overrideResponses?: bool|Param, // Whether API Platform adds automatic responses to the OpenAPI documentation. // Default: true + * error_resource_class?: scalar|Param|null, // The class used to represent errors in the OpenAPI documentation. // Default: null + * validation_error_resource_class?: scalar|Param|null, // The class used to represent validation errors in the OpenAPI documentation. // Default: null + * }, + * maker?: bool|array{ + * enabled?: bool|Param, // Default: true + * namespace_prefix?: scalar|Param|null, // Add a prefix to all maker generated classes. e.g set it to "Api" to set the maker namespace to "App\Api\" (if the maker.root_namespace config is App). e.g. App\Api\State\MyStateProcessor // Default: "" + * }, + * mcp?: bool|array{ + * enabled?: bool|Param, // Default: true + * format?: scalar|Param|null, // The serialization format used for MCP tool input/output. Must be a format registered in api_platform.formats (e.g. "jsonld", "json", "jsonapi"). // Default: "jsonld" + * }, + * exception_to_status?: array, + * formats?: array, + * }>, + * patch_formats?: array, + * }>, + * docs_formats?: array, + * }>, + * error_formats?: array, + * }>, + * jsonschema_formats?: list, + * defaults?: array{ + * uri_template?: mixed, + * short_name?: mixed, + * description?: mixed, + * types?: mixed, + * operations?: mixed, + * formats?: mixed, + * input_formats?: mixed, + * output_formats?: mixed, + * uri_variables?: mixed, + * route_prefix?: mixed, + * defaults?: mixed, + * requirements?: mixed, + * options?: mixed, + * stateless?: mixed, + * sunset?: mixed, + * accept_patch?: mixed, + * status?: mixed, + * host?: mixed, + * schemes?: mixed, + * condition?: mixed, + * controller?: mixed, + * class?: mixed, + * url_generation_strategy?: mixed, + * deprecation_reason?: mixed, + * headers?: mixed, + * cache_headers?: mixed, + * normalization_context?: mixed, + * denormalization_context?: mixed, + * collect_denormalization_errors?: mixed, + * hydra_context?: mixed, + * openapi?: mixed, + * validation_context?: mixed, + * filters?: mixed, + * mercure?: mixed, + * messenger?: mixed, + * input?: mixed, + * output?: mixed, + * order?: mixed, + * fetch_partial?: mixed, + * force_eager?: mixed, + * pagination_client_enabled?: mixed, + * pagination_client_items_per_page?: mixed, + * pagination_client_partial?: mixed, + * pagination_via_cursor?: mixed, + * pagination_enabled?: mixed, + * pagination_fetch_join_collection?: mixed, + * pagination_use_output_walkers?: mixed, + * pagination_items_per_page?: mixed, + * pagination_maximum_items_per_page?: mixed, + * pagination_partial?: mixed, + * pagination_type?: mixed, + * security?: mixed, + * security_message?: mixed, + * security_post_denormalize?: mixed, + * security_post_denormalize_message?: mixed, + * security_post_validation?: mixed, + * security_post_validation_message?: mixed, + * composite_identifier?: mixed, + * exception_to_status?: mixed, + * query_parameter_validation_enabled?: mixed, + * links?: mixed, + * graph_ql_operations?: mixed, + * provider?: mixed, + * processor?: mixed, + * state_options?: mixed, + * rules?: mixed, + * policy?: mixed, + * middleware?: mixed, + * parameters?: array + * }>, + * strict_query_parameter_validation?: mixed, + * hide_hydra_operation?: mixed, + * json_stream?: mixed, + * extra_properties?: mixed, + * map?: mixed, + * mcp?: mixed, + * route_name?: mixed, + * errors?: mixed, + * read?: mixed, + * deserialize?: mixed, + * validate?: mixed, + * write?: mixed, + * serialize?: mixed, + * content_negotiation?: mixed, + * priority?: mixed, + * name?: mixed, + * allow_create?: mixed, + * item_uri_template?: mixed, + * ... + * }, + * } + * @psalm-type MakerConfig = array{ + * root_namespace?: scalar|Param|null, // Default: "App" + * generate_final_classes?: bool|Param, // Default: true + * generate_final_entities?: bool|Param, // Default: false + * } + * @psalm-type WebProfilerConfig = array{ + * toolbar?: bool|array{ // Profiler toolbar configuration + * enabled?: bool|Param, // Default: false + * ajax_replace?: bool|Param, // Replace toolbar on AJAX requests // Default: false + * }, + * intercept_redirects?: bool|Param, // Default: false + * excluded_ajax_paths?: scalar|Param|null, // Default: "^/((index|app(_[\\w]+)?)\\.php/)?_wdt" + * } + * @psalm-type MonologConfig = array{ + * use_microseconds?: scalar|Param|null, // Default: true + * channels?: list, + * handlers?: array, + * }>, + * accepted_levels?: list, + * min_level?: scalar|Param|null, // Default: "DEBUG" + * max_level?: scalar|Param|null, // Default: "EMERGENCY" + * buffer_size?: scalar|Param|null, // Default: 0 + * flush_on_overflow?: bool|Param, // Default: false + * handler?: scalar|Param|null, + * url?: scalar|Param|null, + * exchange?: scalar|Param|null, + * exchange_name?: scalar|Param|null, // Default: "log" + * channel?: scalar|Param|null, // Default: null + * bot_name?: scalar|Param|null, // Default: "Monolog" + * use_attachment?: scalar|Param|null, // Default: true + * use_short_attachment?: scalar|Param|null, // Default: false + * include_extra?: scalar|Param|null, // Default: false + * icon_emoji?: scalar|Param|null, // Default: null + * webhook_url?: scalar|Param|null, + * exclude_fields?: list, + * token?: scalar|Param|null, + * region?: scalar|Param|null, + * source?: scalar|Param|null, + * use_ssl?: bool|Param, // Default: true + * user?: mixed, + * title?: scalar|Param|null, // Default: null + * host?: scalar|Param|null, // Default: null + * port?: scalar|Param|null, // Default: 514 + * config?: list, + * members?: list, + * connection_string?: scalar|Param|null, + * timeout?: scalar|Param|null, + * time?: scalar|Param|null, // Default: 60 + * deduplication_level?: scalar|Param|null, // Default: 400 + * store?: scalar|Param|null, // Default: null + * connection_timeout?: scalar|Param|null, + * persistent?: bool|Param, + * message_type?: scalar|Param|null, // Default: 0 + * parse_mode?: scalar|Param|null, // Default: null + * disable_webpage_preview?: bool|Param|null, // Default: null + * disable_notification?: bool|Param|null, // Default: null + * split_long_messages?: bool|Param, // Default: false + * delay_between_messages?: bool|Param, // Default: false + * topic?: int|Param, // Default: null + * factor?: int|Param, // Default: 1 + * tags?: string|list, + * console_formatter_options?: mixed, // Default: [] + * formatter?: scalar|Param|null, + * nested?: bool|Param, // Default: false + * publisher?: string|array{ + * id?: scalar|Param|null, + * hostname?: scalar|Param|null, + * port?: scalar|Param|null, // Default: 12201 + * chunk_size?: scalar|Param|null, // Default: 1420 + * encoder?: "json"|"compressed_json"|Param, + * }, + * mongodb?: string|array{ + * id?: scalar|Param|null, // ID of a MongoDB\Client service + * uri?: scalar|Param|null, + * username?: scalar|Param|null, + * password?: scalar|Param|null, + * database?: scalar|Param|null, // Default: "monolog" + * collection?: scalar|Param|null, // Default: "logs" + * }, + * elasticsearch?: string|array{ + * id?: scalar|Param|null, + * hosts?: list, + * host?: scalar|Param|null, + * port?: scalar|Param|null, // Default: 9200 + * transport?: scalar|Param|null, // Default: "Http" + * user?: scalar|Param|null, // Default: null + * password?: scalar|Param|null, // Default: null + * }, + * index?: scalar|Param|null, // Default: "monolog" + * document_type?: scalar|Param|null, // Default: "logs" + * ignore_error?: scalar|Param|null, // Default: false + * redis?: string|array{ + * id?: scalar|Param|null, + * host?: scalar|Param|null, + * password?: scalar|Param|null, // Default: null + * port?: scalar|Param|null, // Default: 6379 + * database?: scalar|Param|null, // Default: 0 + * key_name?: scalar|Param|null, // Default: "monolog_redis" + * }, + * predis?: string|array{ + * id?: scalar|Param|null, + * host?: scalar|Param|null, + * }, + * from_email?: scalar|Param|null, + * to_email?: string|list, + * subject?: scalar|Param|null, + * content_type?: scalar|Param|null, // Default: null + * headers?: list, + * mailer?: scalar|Param|null, // Default: null + * email_prototype?: string|array{ + * id?: scalar|Param|null, + * method?: scalar|Param|null, // Default: null + * }, + * verbosity_levels?: array{ + * VERBOSITY_QUIET?: scalar|Param|null, // Default: "ERROR" + * VERBOSITY_NORMAL?: scalar|Param|null, // Default: "WARNING" + * VERBOSITY_VERBOSE?: scalar|Param|null, // Default: "NOTICE" + * VERBOSITY_VERY_VERBOSE?: scalar|Param|null, // Default: "INFO" + * VERBOSITY_DEBUG?: scalar|Param|null, // Default: "DEBUG" + * }, + * channels?: string|array{ + * type?: scalar|Param|null, + * elements?: list, + * }, + * }>, + * } + * @psalm-type DebugConfig = array{ + * max_items?: int|Param, // Max number of displayed items past the first level, -1 means no limit. // Default: 2500 + * min_depth?: int|Param, // Minimum tree depth to clone all the items, 1 is default. // Default: 1 + * max_string_length?: int|Param, // Max length of displayed strings, -1 means no limit. // Default: -1 + * dump_destination?: scalar|Param|null, // A stream URL where dumps should be written to. // Default: null + * theme?: "dark"|"light"|Param, // Changes the color of the dump() output when rendered directly on the templating. "dark" (default) or "light". // Default: "dark" + * } + * @psalm-type NelmioAliceConfig = array{ + * locale?: scalar|Param|null, // Default locale for the Faker Generator // Default: "en_US" + * seed?: scalar|Param|null, // Value used make sure Faker generates data consistently across runs, set to null to disable. // Default: 1 + * functions_blacklist?: list, + * loading_limit?: int|Param, // Alice may do some recursion to resolve certain values. This parameter defines a limit which will stop the resolution once reached. // Default: 5 + * max_unique_values_retry?: int|Param, // Maximum number of time Alice can try to generate a unique value before stopping and failing. // Default: 150 + * } + * @psalm-type FidryAliceDataFixturesConfig = array{ + * default_purge_mode?: scalar|Param|null, // Default: "delete" + * db_drivers?: array{ // The list of enabled drivers. + * doctrine_orm?: bool|Param|null, // Default: null + * doctrine_mongodb_odm?: bool|Param|null, // Default: null + * doctrine_phpcr_odm?: bool|Param|null, // Default: null + * eloquent_orm?: bool|Param|null, // Default: null + * }, + * } + * @psalm-type HautelookAliceConfig = array{ + * fixtures_path?: list, + * root_dirs?: list, + * } + * @psalm-type ItkdevOpenidConnectConfig = array{ + * cache_options?: array{ + * cache_pool?: scalar|Param|null, // Method for caching // Default: "cache.app" + * }, + * cli_login_options?: array{ + * route?: scalar|Param|null, // Return route for CLI login + * }, + * user_provider?: scalar|Param|null, // The User Provider to inject // Default: null + * openid_providers?: list, + * allow_http?: bool|Param, // Whether to allow http or not (default: false) // Default: false + * http_client_options?: array{ // Options forwarded to the underlying Guzzle HTTP client. league/oauth2-client only forwards: timeout, proxy, verify (verify is only consulted when proxy is set). + * timeout?: float|Param, // Total request timeout in seconds + * proxy?: scalar|Param|null, // HTTP proxy URI + * verify?: bool|Param, // Verify TLS certificates (only consulted by Guzzle when proxy is set) + * }, + * }, + * }>, + * } + * @psalm-type WebpackEncoreConfig = array{ + * output_path?: scalar|Param|null, // The path where Encore is building the assets - i.e. Encore.setOutputPath() + * crossorigin?: false|"anonymous"|"use-credentials"|Param, // crossorigin value when Encore.enableIntegrityHashes() is used, can be false (default), anonymous or use-credentials // Default: false + * preload?: bool|Param, // preload all rendered script and link tags automatically via the http2 Link header. // Default: false + * cache?: bool|Param, // Enable caching of the entry point file(s) // Default: false + * strict_mode?: bool|Param, // Throw an exception if the entrypoints.json file is missing or an entry is missing from the data // Default: true + * builds?: array, + * script_attributes?: array, + * link_attributes?: array, + * } + * @psalm-type ItkdevVaultConfig = array{ + * role_id?: scalar|Param|null, + * secret_id?: scalar|Param|null, + * url?: scalar|Param|null, + * } + * @psalm-type TwigExtraConfig = array{ + * cache?: bool|array{ + * enabled?: bool|Param, // Default: false + * }, + * html?: bool|array{ + * enabled?: bool|Param, // Default: true + * }, + * markdown?: bool|array{ + * enabled?: bool|Param, // Default: false + * }, + * intl?: bool|array{ + * enabled?: bool|Param, // Default: false + * }, + * cssinliner?: bool|array{ + * enabled?: bool|Param, // Default: false + * }, + * inky?: bool|array{ + * enabled?: bool|Param, // Default: false + * }, + * string?: bool|array{ + * enabled?: bool|Param, // Default: false + * }, + * commonmark?: array{ + * renderer?: array{ // Array of options for rendering HTML. + * block_separator?: scalar|Param|null, + * inner_separator?: scalar|Param|null, + * soft_break?: scalar|Param|null, + * }, + * html_input?: "strip"|"allow"|"escape"|Param, // How to handle HTML input. + * allow_unsafe_links?: bool|Param, // Remove risky link and image URLs by setting this to false. // Default: true + * max_nesting_level?: int|Param, // The maximum nesting level for blocks. // Default: 9223372036854775807 + * max_delimiters_per_line?: int|Param, // The maximum number of strong/emphasis delimiters per line. // Default: 9223372036854775807 + * slug_normalizer?: array{ // Array of options for configuring how URL-safe slugs are created. + * instance?: mixed, + * max_length?: int|Param, // Default: 255 + * unique?: mixed, + * }, + * commonmark?: array{ // Array of options for configuring the CommonMark core extension. + * enable_em?: bool|Param, // Default: true + * enable_strong?: bool|Param, // Default: true + * use_asterisk?: bool|Param, // Default: true + * use_underscore?: bool|Param, // Default: true + * unordered_list_markers?: list, + * }, + * ... + * }, + * } + * @psalm-type TwigComponentConfig = array{ + * defaults?: array, + * anonymous_template_directory?: scalar|Param|null, // Defaults to `components` + * profiler?: bool|array{ // Enables the profiler for Twig Component + * enabled?: bool|Param, // Default: "%kernel.debug%" + * collect_components?: bool|Param, // Collect components instances // Default: true + * }, + * } + * @psalm-type DoctrineMigrationsConfig = array{ + * enable_service_migrations?: bool|Param, // Whether to enable fetching migrations from the service container. // Default: false + * migrations_paths?: array, + * services?: array, + * factories?: array, + * storage?: array{ // Storage to use for migration status metadata. + * table_storage?: array{ // The default metadata storage, implemented as a table in the database. + * table_name?: scalar|Param|null, // Default: null + * version_column_name?: scalar|Param|null, // Default: null + * version_column_length?: scalar|Param|null, // Default: null + * executed_at_column_name?: scalar|Param|null, // Default: null + * execution_time_column_name?: scalar|Param|null, // Default: null + * }, + * }, + * migrations?: list, + * connection?: scalar|Param|null, // Connection name to use for the migrations database. // Default: null + * em?: scalar|Param|null, // Entity manager name to use for the migrations database (available when doctrine/orm is installed). // Default: null + * all_or_nothing?: scalar|Param|null, // Run all migrations in a transaction. // Default: false + * check_database_platform?: scalar|Param|null, // Adds an extra check in the generated migrations to allow execution only on the same platform as they were initially generated on. // Default: true + * custom_template?: scalar|Param|null, // Custom template path for generated migration classes. // Default: null + * organize_migrations?: scalar|Param|null, // Organize migrations mode. Possible values are: "BY_YEAR", "BY_YEAR_AND_MONTH", false // Default: false + * enable_profiler?: bool|Param, // Whether or not to enable the profiler collector to calculate and visualize migration status. This adds some queries overhead. // Default: false + * transactional?: bool|Param, // Whether or not to wrap migrations in a single transaction. // Default: true + * } + * @psalm-type ConfigType = array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * framework?: FrameworkConfig, + * twig?: TwigConfig, + * security?: SecurityConfig, + * doctrine?: DoctrineConfig, + * nelmio_cors?: NelmioCorsConfig, + * api_platform?: ApiPlatformConfig, + * monolog?: MonologConfig, + * itkdev_openid_connect?: ItkdevOpenidConnectConfig, + * webpack_encore?: WebpackEncoreConfig, + * itkdev_vault?: ItkdevVaultConfig, + * twig_extra?: TwigExtraConfig, + * twig_component?: TwigComponentConfig, + * doctrine_migrations?: DoctrineMigrationsConfig, + * "when@dev"?: array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * framework?: FrameworkConfig, + * twig?: TwigConfig, + * security?: SecurityConfig, + * doctrine?: DoctrineConfig, + * nelmio_cors?: NelmioCorsConfig, + * api_platform?: ApiPlatformConfig, + * maker?: MakerConfig, + * web_profiler?: WebProfilerConfig, + * monolog?: MonologConfig, + * debug?: DebugConfig, + * nelmio_alice?: NelmioAliceConfig, + * fidry_alice_data_fixtures?: FidryAliceDataFixturesConfig, + * hautelook_alice?: HautelookAliceConfig, + * itkdev_openid_connect?: ItkdevOpenidConnectConfig, + * webpack_encore?: WebpackEncoreConfig, + * itkdev_vault?: ItkdevVaultConfig, + * twig_extra?: TwigExtraConfig, + * twig_component?: TwigComponentConfig, + * doctrine_migrations?: DoctrineMigrationsConfig, + * }, + * "when@prod"?: array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * framework?: FrameworkConfig, + * twig?: TwigConfig, + * security?: SecurityConfig, + * doctrine?: DoctrineConfig, + * nelmio_cors?: NelmioCorsConfig, + * api_platform?: ApiPlatformConfig, + * monolog?: MonologConfig, + * itkdev_openid_connect?: ItkdevOpenidConnectConfig, + * webpack_encore?: WebpackEncoreConfig, + * itkdev_vault?: ItkdevVaultConfig, + * twig_extra?: TwigExtraConfig, + * twig_component?: TwigComponentConfig, + * doctrine_migrations?: DoctrineMigrationsConfig, + * }, + * "when@test"?: array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * framework?: FrameworkConfig, + * twig?: TwigConfig, + * security?: SecurityConfig, + * doctrine?: DoctrineConfig, + * nelmio_cors?: NelmioCorsConfig, + * api_platform?: ApiPlatformConfig, + * web_profiler?: WebProfilerConfig, + * monolog?: MonologConfig, + * nelmio_alice?: NelmioAliceConfig, + * fidry_alice_data_fixtures?: FidryAliceDataFixturesConfig, + * hautelook_alice?: HautelookAliceConfig, + * itkdev_openid_connect?: ItkdevOpenidConnectConfig, + * webpack_encore?: WebpackEncoreConfig, + * itkdev_vault?: ItkdevVaultConfig, + * twig_extra?: TwigExtraConfig, + * twig_component?: TwigComponentConfig, + * doctrine_migrations?: DoctrineMigrationsConfig, + * }, + * ..., + * }> + * } + */ +final class App +{ + /** + * @param ConfigType $config + * + * @psalm-return ConfigType + */ + public static function config(array $config): array + { + /** @var ConfigType $config */ + $config = AppReference::config($config); + + return $config; + } +} + +namespace Symfony\Component\Routing\Loader\Configurator; + +/** + * This class provides array-shapes for configuring the routes of an application. + * + * Example: + * + * ```php + * // config/routes.php + * namespace Symfony\Component\Routing\Loader\Configurator; + * + * return Routes::config([ + * 'controllers' => [ + * 'resource' => 'routing.controllers', + * ], + * ]); + * ``` + * + * @psalm-type RouteConfig = array{ + * path: string|array, + * controller?: string, + * methods?: string|list, + * requirements?: array, + * defaults?: array, + * options?: array, + * host?: string|array, + * schemes?: string|list, + * condition?: string, + * locale?: string, + * format?: string, + * utf8?: bool, + * stateless?: bool, + * } + * @psalm-type ImportConfig = array{ + * resource: string, + * type?: string, + * exclude?: string|list, + * prefix?: string|array, + * name_prefix?: string, + * trailing_slash_on_root?: bool, + * controller?: string, + * methods?: string|list, + * requirements?: array, + * defaults?: array, + * options?: array, + * host?: string|array, + * schemes?: string|list, + * condition?: string, + * locale?: string, + * format?: string, + * utf8?: bool, + * stateless?: bool, + * } + * @psalm-type AliasConfig = array{ + * alias: string, + * deprecated?: array{package:string, version:string, message?:string}, + * } + * @psalm-type RoutesConfig = array{ + * "when@dev"?: array, + * "when@prod"?: array, + * "when@test"?: array, + * ... + * } + */ +final class Routes +{ + /** + * @param RoutesConfig $config + * + * @psalm-return RoutesConfig + */ + public static function config(array $config): array + { + return $config; + } +} diff --git a/config/routes/framework.yaml b/config/routes/framework.yaml index 0fc74bb..bc1feac 100644 --- a/config/routes/framework.yaml +++ b/config/routes/framework.yaml @@ -1,4 +1,4 @@ when@dev: _errors: - resource: '@FrameworkBundle/Resources/config/routing/errors.xml' + resource: '@FrameworkBundle/Resources/config/routing/errors.php' prefix: /_error diff --git a/config/routes/web_profiler.yaml b/config/routes/web_profiler.yaml index 8d85319..b3b7b4b 100644 --- a/config/routes/web_profiler.yaml +++ b/config/routes/web_profiler.yaml @@ -1,8 +1,8 @@ when@dev: web_profiler_wdt: - resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' + resource: '@WebProfilerBundle/Resources/config/routing/wdt.php' prefix: /_wdt web_profiler_profiler: - resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' + resource: '@WebProfilerBundle/Resources/config/routing/profiler.php' prefix: /_profiler diff --git a/config/services.yaml b/config/services.yaml index c98c423..71d40c9 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -36,7 +36,3 @@ services: arguments: $resultHandlers: !tagged_iterator app.handler.detection_result_handler $keepResults: '%env(int:APP_KEEP_RESULTS)%' - - App\Service\Leantime\LeantimeProjectUrlFactory: - arguments: - $baseUrl: '%env(string:APP_LEANTIME_URI)%' diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index ca2f4d1..71a88b4 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -6,7 +6,7 @@ services: nginx: labels: - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.middlewares=ITKBasicAuth@file" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.middlewares=ITKBasicAuth@file" mail: image: axllent/mailpit @@ -17,11 +17,11 @@ services: labels: - "traefik.enable=true" - "traefik.docker.network=frontend" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}-http.rule=Host(`mail.${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}-http.entrypoints=web" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}-http.middlewares=redirect-to-https" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}-http.rule=Host(`mail.${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}-http.entrypoints=web" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}-http.middlewares=redirect-to-https" - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}.rule=Host(`mail.${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}.entrypoints=websecure" - - "traefik.http.services.mail_${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=8025" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}.middlewares=ITKMailhogAuth@file" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}.rule=Host(`mail.${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}.entrypoints=websecure" + - "traefik.http.services.mail_${COMPOSE_PROJECT_NAME:?}.loadbalancer.server.port=8025" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}.middlewares=ITKMailhogAuth@file" diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 4208ae2..73f8b43 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -9,7 +9,7 @@ services: environment: - RABBITMQ_DEFAULT_USER=user - RABBITMQ_DEFAULT_PASS=password - - RABBITMQ_ERLANG_COOKIE='d53f219cd9376f8f440aaf9889f315ab' + - RABBITMQ_ERLANG_COOKIE='local-dev-cookie-not-a-secret' healthcheck: test: rabbitmq-diagnostics check_port_connectivity interval: 1s @@ -17,7 +17,9 @@ services: retries: 30 node: - image: node:20 + profiles: + - dev + image: node:24 volumes: - .:/app working_dir: /app diff --git a/docker-compose.redirect.yml b/docker-compose.redirect.yml index e9ba157..2e7ac33 100644 --- a/docker-compose.redirect.yml +++ b/docker-compose.redirect.yml @@ -3,13 +3,13 @@ services: nginx: labels: # Add www before domain and set redirect to non-www - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}-http.rule=Host(`www.${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}-http.entrypoints=web" - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}-http.middlewares=redirect-to-https,non_www" - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}.rule=Host(`www.${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}.entrypoints=websecure" - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}.middlewares=non_www" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}-http.rule=Host(`www.${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}-http.entrypoints=web" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}-http.middlewares=redirect-to-https,non_www" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}.rule=Host(`www.${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}.entrypoints=websecure" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}.middlewares=non_www" - traefik.http.middlewares.non_www.redirectregex.regex=^(http|https)?://(?:www\.)?(.+) - - traefik.http.middlewares.non_www.redirectregex.replacement=https://$${2} + - traefik.http.middlewares.non_www.redirectregex.replacement=https://$${2:?} - traefik.http.middlewares.non_www.redirectregex.permanent=true diff --git a/docker-compose.server.yml b/docker-compose.server.yml index 44c75cd..fffc693 100644 --- a/docker-compose.server.yml +++ b/docker-compose.server.yml @@ -33,16 +33,20 @@ services: - ./.docker/nginx.conf:/etc/nginx/nginx.conf:ro - .:/app environment: - NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME}-phpfpm-1:9000 + NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME:?}-phpfpm-1:9000 + NGINX_CRON_METRICS: ${COMPOSE_PROJECT_NAME:?}-phpfpm-1:9746 NGINX_WEB_ROOT: /app/public NGINX_PORT: 8080 NGINX_MAX_BODY_SIZE: 5M labels: - "traefik.enable=true" - "traefik.docker.network=frontend" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}-http.rule=Host(`${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}-http.entrypoints=web" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}-http.middlewares=redirect-to-https" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-http.rule=Host(`${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-http.entrypoints=web" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-http.middlewares=redirect-to-https" - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.entrypoints=websecure" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.rule=Host(`${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.entrypoints=websecure" + # Cron-metrics protection. + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-metrics.rule=Host(`${COMPOSE_SERVER_DOMAIN:?}`) && PathPrefix(`/cron-metrics`) " + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-metrics.middlewares=ITKMetricsAuth@file" diff --git a/docker-compose.yml b/docker-compose.yml index dc2ef97..4dff245 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,7 +39,7 @@ services: - PHP_MEMORY_LIMIT=256M # Depending on the setup, you may have to remove --read-envelope-from from msmtp (cf. https://marlam.de/msmtp/msmtp.html) or use SMTP to send mail - PHP_SENDMAIL_PATH=/usr/bin/msmtp --host=mail --port=1025 --read-recipients --read-envelope-from - - DOCKER_HOST_DOMAIN=${COMPOSE_DOMAIN} + - DOCKER_HOST_DOMAIN=${COMPOSE_DOMAIN:?} - PHP_IDE_CONFIG=serverName=localhost depends_on: mariadb: @@ -60,17 +60,21 @@ services: - ./.docker/templates:/etc/nginx/templates:ro - .:/app environment: - NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME}-phpfpm-1:9000 + NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME:?}-phpfpm-1:9000 + NGINX_CRON_METRICS: ${COMPOSE_PROJECT_NAME:?}-phpfpm-1:9746 NGINX_WEB_ROOT: /app/public NGINX_PORT: 8080 NGINX_MAX_BODY_SIZE: 5M labels: - "traefik.enable=true" - "traefik.docker.network=frontend" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`${COMPOSE_DOMAIN}`)" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.rule=Host(`${COMPOSE_DOMAIN:?}`)" # HTTPS config - uncomment to enable redirect from :80 to :443 - # - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.middlewares=redirect-to-https" + # - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.middlewares=redirect-to-https" # - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" + # Cron-metrics protection. + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-metrics.rule=Host(`${COMPOSE_DOMAIN:?}`) && PathPrefix(`/cron-metrics`) " + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-metrics.middlewares=ITKMetricsAuth@file" mail: image: axllent/mailpit @@ -83,8 +87,8 @@ services: labels: - "traefik.enable=true" - "traefik.docker.network=frontend" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}mail.rule=Host(`mail-${COMPOSE_DOMAIN}`)" - - "traefik.http.services.${COMPOSE_PROJECT_NAME}mail.loadbalancer.server.port=8025" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}mail.rule=Host(`mail-${COMPOSE_DOMAIN:?}`)" + - "traefik.http.services.${COMPOSE_PROJECT_NAME:?}mail.loadbalancer.server.port=8025" # Code checks tools markdownlint: diff --git a/migrations/Version20260519061123.php b/migrations/Version20260519061123.php new file mode 100644 index 0000000..4c04db0 --- /dev/null +++ b/migrations/Version20260519061123.php @@ -0,0 +1,39 @@ +addSql('ALTER TABLE security_contract DROP FOREIGN KEY `FK_8AE4AF8B166D1F9C`'); + $this->addSql('DROP TABLE project'); + $this->addSql('DROP INDEX UNIQ_8AE4AF8B166D1F9C ON security_contract'); + $this->addSql('ALTER TABLE security_contract ADD economics_id INT NOT NULL, ADD project_name VARCHAR(255) NOT NULL, ADD hosting_provider VARCHAR(255) DEFAULT NULL, ADD document_url VARCHAR(255) DEFAULT NULL, ADD eol TINYINT NOT NULL, ADD leantime_url VARCHAR(255) DEFAULT NULL, ADD client_contact_name VARCHAR(255) DEFAULT NULL, ADD client_contact_email VARCHAR(255) DEFAULT NULL, ADD dedicated_server TINYINT NOT NULL, ADD server_size VARCHAR(255) DEFAULT NULL, ADD system_owner_notices JSON DEFAULT NULL, ADD project_tracker_key VARCHAR(255) DEFAULT NULL, ADD cybersecurity_price DOUBLE PRECISION DEFAULT NULL, ADD cybersecurity_note LONGTEXT DEFAULT NULL, DROP notes, DROP project_id, CHANGE valid_from valid_from DATE DEFAULT NULL, CHANGE valid_to valid_to DATE DEFAULT NULL, CHANGE economics_report_url client_name VARCHAR(255) DEFAULT NULL, CHANGE operational_contract_url git_repos LONGTEXT DEFAULT NULL'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_8AE4AF8B4416F7E8 ON security_contract (economics_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TABLE project (id BINARY(16) NOT NULL, created_at DATETIME NOT NULL, modified_at DATETIME NOT NULL, created_by VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT \'\' NOT NULL COLLATE `utf8mb4_unicode_ci`, modified_by VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT \'\' NOT NULL COLLATE `utf8mb4_unicode_ci`, leantime_id INT NOT NULL, name VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, leantime_url VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, economics_url VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, details LONGTEXT CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, leantime_modified_at DATETIME NOT NULL, UNIQUE INDEX UNIQ_2FB3D0EE4B785F0C (leantime_id), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); + $this->addSql('DROP INDEX UNIQ_8AE4AF8B4416F7E8 ON security_contract'); + $this->addSql('ALTER TABLE security_contract ADD economics_report_url VARCHAR(255) DEFAULT NULL, ADD operational_contract_url LONGTEXT DEFAULT NULL, ADD notes LONGTEXT NOT NULL, ADD project_id BINARY(16) NOT NULL, DROP economics_id, DROP project_name, DROP client_name, DROP hosting_provider, DROP document_url, DROP eol, DROP leantime_url, DROP client_contact_name, DROP client_contact_email, DROP dedicated_server, DROP server_size, DROP git_repos, DROP system_owner_notices, DROP project_tracker_key, DROP cybersecurity_price, DROP cybersecurity_note, CHANGE valid_from valid_from DATE NOT NULL, CHANGE valid_to valid_to DATE NOT NULL'); + $this->addSql('ALTER TABLE security_contract ADD CONSTRAINT `FK_8AE4AF8B166D1F9C` FOREIGN KEY (project_id) REFERENCES project (id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_8AE4AF8B166D1F9C ON security_contract (project_id)'); + } +} diff --git a/public/api-spec-v1.json b/public/api-spec-v1.json index 1639d4b..47ce588 100644 --- a/public/api-spec-v1.json +++ b/public/api-spec-v1.json @@ -1,13 +1,13 @@ { "openapi": "3.1.0", "info": { - "title": "", - "description": "", - "version": "0.0.0" + "title": "ITKsites Detection API", + "description": "REST API for ingesting server detection results from the ITK sites server harvester. Detection results are processed asynchronously to track servers, sites, domains, packages, modules, Docker images, and git repositories.", + "version": "1.0.0" }, "servers": [ { - "url": "/", + "url": "https://itksites.local.itkdev.dk", "description": "" } ], @@ -20,19 +20,11 @@ ], "responses": { "202": { - "description": "DetectionResult resource created", - "content": { - "application/json": { - "schema": {} - }, - "application/ld+json": { - "schema": {} - } - }, + "description": "Detection result accepted for processing", "links": {} }, "400": { - "description": "Invalid input", + "description": "Invalid input \u2014 malformed request body", "content": { "application/ld+json": { "schema": { @@ -52,30 +44,36 @@ }, "links": {} }, + "401": { + "description": "Unauthorized \u2014 missing or invalid API key. The Authorization header must use the format: Apikey {key}" + }, + "403": { + "description": "Forbidden \u2014 the authenticated server does not have the required ROLE_SERVER role" + }, "422": { - "description": "An error occurred", + "description": "Validation error \u2014 one or more fields failed constraint validation", "content": { "application/ld+json": { "schema": { - "$ref": "#/components/schemas/ConstraintViolation.jsonld-jsonld" + "$ref": "#/components/schemas/ConstraintViolation.jsonld" } }, "application/problem+json": { "schema": { - "$ref": "#/components/schemas/ConstraintViolation-json" + "$ref": "#/components/schemas/ConstraintViolation" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/ConstraintViolation-json" + "$ref": "#/components/schemas/ConstraintViolation" } } }, "links": {} } }, - "summary": "Creates a DetectionResult resource.", - "description": "Creates a DetectionResult resource.", + "summary": "Submit a detection result for async processing", + "description": "Accepts a detection result from the server harvester and queues it for asynchronous processing. The result is deduplicated by content hash \u2014 identical submissions update the last contact timestamp without triggering reprocessing. Returns 202 Accepted with an empty body.", "parameters": [], "requestBody": { "description": "The new DetectionResult resource", @@ -92,21 +90,18 @@ } }, "required": true - }, - "deprecated": false + } } } }, "components": { "schemas": { - "ConstraintViolation-json": { + "ConstraintViolation": { "type": "object", "description": "Unprocessable entity", - "deprecated": false, "properties": { "status": { "default": 422, - "example": 422, "type": "integer" }, "violations": { @@ -121,8 +116,25 @@ "message": { "type": "string", "description": "The message associated with the violation" + }, + "code": { + "type": "string", + "description": "The code of the violation" + }, + "hint": { + "type": "string", + "description": "An extra hint to understand the violation" + }, + "payload": { + "type": "object", + "additionalProperties": true, + "description": "The serialized payload of the violation" } - } + }, + "required": [ + "propertyPath", + "message" + ] } }, "detail": { @@ -149,14 +161,12 @@ } } }, - "ConstraintViolation.jsonld-jsonld": { + "ConstraintViolation.jsonld": { "type": "object", "description": "Unprocessable entity", - "deprecated": false, "properties": { "status": { "default": 422, - "example": 422, "type": "integer" }, "violations": { @@ -171,8 +181,25 @@ "message": { "type": "string", "description": "The message associated with the violation" + }, + "code": { + "type": "string", + "description": "The code of the violation" + }, + "hint": { + "type": "string", + "description": "An extra hint to understand the violation" + }, + "payload": { + "type": "object", + "additionalProperties": true, + "description": "The serialized payload of the violation" } - } + }, + "required": [ + "propertyPath", + "message" + ] } }, "detail": { @@ -205,22 +232,27 @@ }, "DetectionResult-write": { "type": "object", - "description": "", - "deprecated": false, "properties": { "type": { - "default": "", - "example": "", - "type": "string" + "enum": [ + "dir", + "docker", + "drupal", + "git", + "nginx", + "symfony" + ] }, "rootDir": { + "description": "Absolute path to the root directory of the detected installation on the server", "default": "", - "example": "", + "example": "/data/www/example-site/htdocs", "type": "string" }, "data": { + "description": "JSON-encoded payload from the server harvester containing the detection details. Structure varies by type.", "default": "", - "example": "", + "example": "{\"packages\":{\"symfony/framework-bundle\":{\"version\":\"7.2.1\"}}}", "type": "string" } } @@ -228,7 +260,6 @@ "Error": { "type": "object", "description": "A representation of common errors.", - "deprecated": false, "properties": { "title": { "readOnly": true, @@ -247,7 +278,10 @@ ] }, "status": { - "type": "number", + "type": [ + "integer", + "null" + ], "examples": [ 404 ], @@ -271,7 +305,6 @@ "Error.jsonld": { "type": "object", "description": "A representation of common errors.", - "deprecated": false, "properties": { "title": { "readOnly": true, @@ -290,7 +323,10 @@ ] }, "status": { - "type": "number", + "type": [ + "integer", + "null" + ], "examples": [ 404 ], @@ -327,7 +363,7 @@ "securitySchemes": { "apiKey": { "type": "apiKey", - "description": "Value for the Authorization header parameter.", + "description": "Server API key. Use the format: Apikey {your-api-key}", "name": "Authorization", "in": "header" } @@ -340,7 +376,8 @@ ], "tags": [ { - "name": "DetectionResult" + "name": "DetectionResult", + "description": "Resource 'DetectionResult' operations." } ], "webhooks": {} diff --git a/public/api-spec-v1.yaml b/public/api-spec-v1.yaml index c8a4009..72bb018 100755 --- a/public/api-spec-v1.yaml +++ b/public/api-spec-v1.yaml @@ -1,11 +1,11 @@ openapi: 3.1.0 info: - title: '' - description: '' - version: 0.0.0 + title: 'ITKsites Detection API' + description: 'REST API for ingesting server detection results from the ITK sites server harvester. Detection results are processed asynchronously to track servers, sites, domains, packages, modules, Docker images, and git repositories.' + version: 1.0.0 servers: - - url: / + url: 'https://itksites.local.itkdev.dk' description: '' paths: /api/detection_results: @@ -15,15 +15,10 @@ paths: - DetectionResult responses: '202': - description: 'DetectionResult resource created' - content: - application/json: - schema: { } - application/ld+json: - schema: { } + description: 'Detection result accepted for processing' links: { } '400': - description: 'Invalid input' + description: 'Invalid input — malformed request body' content: application/ld+json: schema: @@ -35,21 +30,25 @@ paths: schema: $ref: '#/components/schemas/Error' links: { } + '401': + description: 'Unauthorized — missing or invalid API key. The Authorization header must use the format: Apikey {key}' + '403': + description: 'Forbidden — the authenticated server does not have the required ROLE_SERVER role' '422': - description: 'An error occurred' + description: 'Validation error — one or more fields failed constraint validation' content: application/ld+json: schema: - $ref: '#/components/schemas/ConstraintViolation.jsonld-jsonld' + $ref: '#/components/schemas/ConstraintViolation.jsonld' application/problem+json: schema: - $ref: '#/components/schemas/ConstraintViolation-json' + $ref: '#/components/schemas/ConstraintViolation' application/json: schema: - $ref: '#/components/schemas/ConstraintViolation-json' + $ref: '#/components/schemas/ConstraintViolation' links: { } - summary: 'Creates a DetectionResult resource.' - description: 'Creates a DetectionResult resource.' + summary: 'Submit a detection result for async processing' + description: 'Accepts a detection result from the server harvester and queues it for asynchronous processing. The result is deduplicated by content hash — identical submissions update the last contact timestamp without triggering reprocessing. Returns 202 Accepted with an empty body.' parameters: [] requestBody: description: 'The new DetectionResult resource' @@ -61,17 +60,14 @@ paths: schema: $ref: '#/components/schemas/DetectionResult-write' required: true - deprecated: false components: schemas: - ConstraintViolation-json: + ConstraintViolation: type: object description: 'Unprocessable entity' - deprecated: false properties: status: default: 422 - example: 422 type: integer violations: type: array @@ -84,6 +80,19 @@ components: message: type: string description: 'The message associated with the violation' + code: + type: string + description: 'The code of the violation' + hint: + type: string + description: 'An extra hint to understand the violation' + payload: + type: object + additionalProperties: true + description: 'The serialized payload of the violation' + required: + - propertyPath + - message detail: readOnly: true type: string @@ -100,14 +109,12 @@ components: type: - string - 'null' - ConstraintViolation.jsonld-jsonld: + ConstraintViolation.jsonld: type: object description: 'Unprocessable entity' - deprecated: false properties: status: default: 422 - example: 422 type: integer violations: type: array @@ -120,6 +127,19 @@ components: message: type: string description: 'The message associated with the violation' + code: + type: string + description: 'The code of the violation' + hint: + type: string + description: 'An extra hint to understand the violation' + payload: + type: object + additionalProperties: true + description: 'The serialized payload of the violation' + required: + - propertyPath + - message detail: readOnly: true type: string @@ -141,25 +161,28 @@ components: - 'null' DetectionResult-write: type: object - description: '' - deprecated: false properties: type: - default: '' - example: '' - type: string + enum: + - dir + - docker + - drupal + - git + - nginx + - symfony rootDir: + description: 'Absolute path to the root directory of the detected installation on the server' default: '' - example: '' + example: /data/www/example-site/htdocs type: string data: + description: 'JSON-encoded payload from the server harvester containing the detection details. Structure varies by type.' default: '' - example: '' + example: '{"packages":{"symfony/framework-bundle":{"version":"7.2.1"}}}' type: string Error: type: object description: 'A representation of common errors.' - deprecated: false properties: title: readOnly: true @@ -174,7 +197,9 @@ components: - string - 'null' status: - type: number + type: + - integer + - 'null' examples: - 404 default: 400 @@ -191,7 +216,6 @@ components: Error.jsonld: type: object description: 'A representation of common errors.' - deprecated: false properties: title: readOnly: true @@ -206,7 +230,9 @@ components: - string - 'null' status: - type: number + type: + - integer + - 'null' examples: - 404 default: 400 @@ -233,7 +259,7 @@ components: securitySchemes: apiKey: type: apiKey - description: 'Value for the Authorization header parameter.' + description: 'Server API key. Use the format: Apikey {your-api-key}' name: Authorization in: header security: @@ -242,4 +268,5 @@ security: tags: - name: DetectionResult + description: "Resource 'DetectionResult' operations." webhooks: { } diff --git a/rector.php b/rector.php index 2b49758..ded9606 100644 --- a/rector.php +++ b/rector.php @@ -18,12 +18,13 @@ // define sets of rules $rectorConfig->sets([ - LevelSetList::UP_TO_PHP_83, - SymfonySetList::SYMFONY_64, + LevelSetList::UP_TO_PHP_85, + SymfonySetList::SYMFONY_74, SymfonySetList::CONFIGS, SymfonySetList::SYMFONY_CODE_QUALITY, SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION, Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_100, Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_110, + Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_120, ]); }; diff --git a/src/Admin/Field/AdvisoryCountField.php b/src/Admin/Field/AdvisoryCountField.php index 0606845..6271123 100644 --- a/src/Admin/Field/AdvisoryCountField.php +++ b/src/Admin/Field/AdvisoryCountField.php @@ -6,18 +6,19 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class AdvisoryCountField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) // this template is used in 'index' and 'detail' pages - ->setTemplatePath('EasyAdminBundle/Fields/advisory_count.html.twig'); + ->setTemplatePath('EasyAdminBundle/Fields/advisories.html.twig'); } } diff --git a/src/Admin/Field/AffectedSitesField.php b/src/Admin/Field/AffectedSitesField.php new file mode 100644 index 0000000..058b5b6 --- /dev/null +++ b/src/Admin/Field/AffectedSitesField.php @@ -0,0 +1,22 @@ +setProperty($propertyName) + ->setLabel($label) + ->setTemplatePath('EasyAdminBundle/Fields/affected_sites.html.twig'); + } +} diff --git a/src/Admin/Field/ChangesField.php b/src/Admin/Field/ChangesField.php index e8c2e5b..35c61c8 100644 --- a/src/Admin/Field/ChangesField.php +++ b/src/Admin/Field/ChangesField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class ChangesField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/ClonedByField.php b/src/Admin/Field/ClonedByField.php index 13f19f9..644a33f 100644 --- a/src/Admin/Field/ClonedByField.php +++ b/src/Admin/Field/ClonedByField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class ClonedByField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/ConfigFilePathField.php b/src/Admin/Field/ConfigFilePathField.php index fa6000f..9215f64 100644 --- a/src/Admin/Field/ConfigFilePathField.php +++ b/src/Admin/Field/ConfigFilePathField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class ConfigFilePathField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/DomainField.php b/src/Admin/Field/DomainField.php index b1418a6..83c33e4 100644 --- a/src/Admin/Field/DomainField.php +++ b/src/Admin/Field/DomainField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class DomainField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/EolTypeField.php b/src/Admin/Field/EolTypeField.php index 27f6de2..e102797 100644 --- a/src/Admin/Field/EolTypeField.php +++ b/src/Admin/Field/EolTypeField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class EolTypeField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/LatestStatusField.php b/src/Admin/Field/LatestStatusField.php index c1494f8..1e06566 100644 --- a/src/Admin/Field/LatestStatusField.php +++ b/src/Admin/Field/LatestStatusField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class LatestStatusField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/RootDirField.php b/src/Admin/Field/RootDirField.php index 59a6a76..185eabd 100644 --- a/src/Admin/Field/RootDirField.php +++ b/src/Admin/Field/RootDirField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class RootDirField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/ServerTypeField.php b/src/Admin/Field/ServerTypeField.php index 0e585bd..0a7fd70 100644 --- a/src/Admin/Field/ServerTypeField.php +++ b/src/Admin/Field/ServerTypeField.php @@ -7,14 +7,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Contracts\Translation\TranslatableInterface; class ServerTypeField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/SiteTypeField.php b/src/Admin/Field/SiteTypeField.php index e807a7e..a92b735 100644 --- a/src/Admin/Field/SiteTypeField.php +++ b/src/Admin/Field/SiteTypeField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class SiteTypeField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/SourcesField.php b/src/Admin/Field/SourcesField.php index 1d08b05..6e17569 100644 --- a/src/Admin/Field/SourcesField.php +++ b/src/Admin/Field/SourcesField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class SourcesField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/TextMonospaceField.php b/src/Admin/Field/TextMonospaceField.php index c3396d0..86ce73c 100644 --- a/src/Admin/Field/TextMonospaceField.php +++ b/src/Admin/Field/TextMonospaceField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class TextMonospaceField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/VersionField.php b/src/Admin/Field/VersionField.php index 3cf559c..10701af 100644 --- a/src/Admin/Field/VersionField.php +++ b/src/Admin/Field/VersionField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class VersionField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Admin/Field/WarningField.php b/src/Admin/Field/WarningField.php index 7a28320..7e4c7ba 100644 --- a/src/Admin/Field/WarningField.php +++ b/src/Admin/Field/WarningField.php @@ -6,14 +6,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; +use Symfony\Contracts\Translation\TranslatableInterface; class WarningField implements FieldInterface { use FieldTrait; - public static function new(string $propertyName, ?string $label = null): self + public static function new(string $propertyName, TranslatableInterface|string|bool|null $label = null): self { - return (new self()) + return new self() ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Command/SyncLeantimeProjectsCommand.php b/src/Command/SyncLeantimeProjectsCommand.php deleted file mode 100644 index dc66a49..0000000 --- a/src/Command/SyncLeantimeProjectsCommand.php +++ /dev/null @@ -1,54 +0,0 @@ -addOption('project-id', null, InputOption::VALUE_REQUIRED, 'Leantime project ID'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $io = new SymfonyStyle($input, $output); - - try { - if ($input->getOption('project-id')) { - $projectId = (int) $input->getOption('project-id'); - - $project = $this->projectSyncService->syncProject($projectId); - - $io->success(sprintf('Project: %s (%d) synced successfully.', $project->getName(), $project->getLeantimeId())); - } else { - $count = $this->projectSyncService->syncAllProjects(); - - $io->success(sprintf('Synced %d projects successfully.', $count)); - } - } catch (\Throwable $e) { - $io->error($e->getMessage()); - - return Command::FAILURE; - } - - return Command::SUCCESS; - } -} diff --git a/src/Command/SyncServiceAgreementsCommand.php b/src/Command/SyncServiceAgreementsCommand.php new file mode 100644 index 0000000..55fcb64 --- /dev/null +++ b/src/Command/SyncServiceAgreementsCommand.php @@ -0,0 +1,40 @@ +syncService->syncAll(); + + $io->success(sprintf('Synced %d service agreements successfully.', $count)); + } catch (\Throwable $e) { + $io->error($e->getMessage()); + + return Command::FAILURE; + } + + return Command::SUCCESS; + } +} diff --git a/src/Controller/Admin/AdvisoryCrudController.php b/src/Controller/Admin/AdvisoryCrudController.php index 8e89d9b..50c18ec 100644 --- a/src/Controller/Admin/AdvisoryCrudController.php +++ b/src/Controller/Admin/AdvisoryCrudController.php @@ -4,6 +4,7 @@ namespace App\Controller\Admin; +use App\Admin\Field\AffectedSitesField; use App\Admin\Field\SourcesField; use App\Admin\Field\TextMonospaceField; use App\Entity\Advisory; @@ -58,6 +59,7 @@ public function configureFields(string $pageName): iterable yield DateField::new('reportedAt')->setColumns(6)->onlyOnIndex(); yield DateTimeField::new('reportedAt')->setColumns(6)->onlyOnDetail(); yield SourcesField::new('sourceLinks')->setColumns(6)->onlyOnDetail(); + yield AffectedSitesField::new('sites')->setLabel('Affected Sites'); } #[\Override] diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php index 9e1e0e6..883b425 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/Admin/DashboardController.php @@ -4,29 +4,8 @@ namespace App\Controller\Admin; -use App\EasyAdmin\Config\AutoBadgeMenuItem; -use App\Entity\Advisory; -use App\Entity\DetectionResult; -use App\Entity\DockerImage; -use App\Entity\DockerImageTag; -use App\Entity\Domain; -use App\Entity\GitRepo; -use App\Entity\GitTag; -use App\Entity\Installation; -use App\Entity\Module; -use App\Entity\ModuleVersion; -use App\Entity\OIDC; -use App\Entity\Package; -use App\Entity\PackageVersion; -use App\Entity\Project; -use App\Entity\SecurityContract; -use App\Entity\Server; -use App\Entity\ServiceCertificate; -use App\Entity\Site; use App\Repository\AdvisoryRepository; -use App\Repository\OIDCRepository; -use App\Repository\SecurityContractRepository; -use App\Repository\ServiceCertificateRepository; +use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard; use EasyCorp\Bundle\EasyAdminBundle\Config\Assets; use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard; @@ -35,18 +14,15 @@ use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator; use Symfony\Component\HttpFoundation\Response; +#[AdminDashboard(routePath: '/admin', routeName: 'admin')] class DashboardController extends AbstractDashboardController { public function __construct( private readonly AdminUrlGenerator $adminUrlGenerator, private readonly AdvisoryRepository $advisoryRepository, - private readonly ServiceCertificateRepository $serviceCertificateRepository, - private readonly OIDCRepository $oidcRepository, - private readonly SecurityContractRepository $securityContractRepository, ) { } - #[\Symfony\Component\Routing\Attribute\Route('/admin', name: 'admin')] #[\Override] public function index(): Response { @@ -70,35 +46,25 @@ public function configureDashboard(): Dashboard public function configureMenuItems(): iterable { yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home'); - - yield MenuItem::section('Projects'); - yield MenuItem::linkToCrud('Projects', 'fas fa-folder', Project::class); - yield AutoBadgeMenuItem::linkToCrud('Cyber Security', 'fas fa-file-contract', SecurityContract::class) - ->setBadge($this->securityContractRepository->countExpiredContracts(), 'danger'); - yield AutoBadgeMenuItem::linkToCrud('OIDC', 'fas fa-shield-halved', OIDC::class) - ->setBadge($this->oidcRepository->countExpiredCertificates(), 'danger'); - yield AutoBadgeMenuItem::linkToCrud('Service certificates', 'fas fa-passport', ServiceCertificate::class) - ->setBadge($this->serviceCertificateRepository->countExpiredCertificates(), 'danger'); - - yield MenuItem::section('Hosting'); - yield MenuItem::linkToCrud('Servers', 'fas fa-server', Server::class); - yield MenuItem::linkToCrud('Installations', 'fas fa-folder', Installation::class); - yield MenuItem::linkToCrud('Sites', 'fas fa-bookmark', Site::class); - yield MenuItem::linkToCrud('Domains', 'fas fa-link', Domain::class); - + yield MenuItem::linkTo(ServerCrudController::class, 'Servers', 'fas fa-server'); + yield MenuItem::linkTo(InstallationCrudController::class, 'Installations', 'fas fa-folder'); + yield MenuItem::linkTo(SiteCrudController::class, 'Sites', 'fas fa-bookmark'); + yield MenuItem::linkTo(DomainCrudController::class, 'Domains', 'fas fa-link'); + yield MenuItem::linkTo(OIDCCrudController::class, 'OIDC', 'fas fa-key'); + yield MenuItem::linkTo(ServiceCertificateCrudController::class, 'Service certificates', 'fas fa-lock'); + yield MenuItem::linkTo(SecurityContractCrudController::class, 'Service Agreements', 'fas fa-file-contract'); yield MenuItem::section('Dependencies'); - yield MenuItem::linkToCrud('Packages', 'fas fa-cube', Package::class); - yield MenuItem::linkToCrud('Package Versions', 'fas fa-cubes', PackageVersion::class); - yield AutoBadgeMenuItem::linkToCrud('Advisories', 'fas fa-skull-crossbones', Advisory::class) - ->setBadge($this->advisoryRepository->count([]), 'danger'); - yield MenuItem::linkToCrud('Modules', 'fas fa-cube', Module::class); - yield MenuItem::linkToCrud('Modules Versions', 'fas fa-cubes', ModuleVersion::class); - yield MenuItem::linkToCrud('Docker Images', 'fas fa-cube', DockerImage::class); - yield MenuItem::linkToCrud('Docker Image Tags', 'fas fa-cubes', DockerImageTag::class); - yield MenuItem::linkToCrud('Git Repositories', 'fa-brands fa-github', GitRepo::class); - yield MenuItem::linkToCrud('Git Tags', 'fa-brands fa-git-alt', GitTag::class); + yield MenuItem::linkTo(PackageCrudController::class, 'Packages', 'fas fa-cube'); + yield MenuItem::linkTo(PackageVersionCrudController::class, 'Package Versions', 'fas fa-cubes'); + yield MenuItem::linkTo(AdvisoryCrudController::class, 'Advisories', 'fas fa-skull-crossbones')->setBadge($this->advisoryRepository->count([]), 'dark'); + yield MenuItem::linkTo(ModuleCrudController::class, 'Modules', 'fas fa-cube'); + yield MenuItem::linkTo(ModuleVersionCrudController::class, 'Modules Versions', 'fas fa-cubes'); + yield MenuItem::linkTo(DockerImageCrudController::class, 'Docker Images', 'fas fa-cube'); + yield MenuItem::linkTo(DockerImageTagCrudController::class, 'Docker Image Tags', 'fas fa-cubes'); + yield MenuItem::linkTo(GitRepoCrudController::class, 'Git Repositories', 'fa-brands fa-github'); + yield MenuItem::linkTo(GitTagCrudController::class, 'Git Tags', 'fa-brands fa-git-alt'); yield MenuItem::section('Results'); - yield MenuItem::linkToCrud('Detection Results', 'fas fa-upload', DetectionResult::class); + yield MenuItem::linkTo(DetectionResultCrudController::class, 'Detection Results', 'fas fa-upload'); } #[\Override] diff --git a/src/Controller/Admin/ProjectCrudController.php b/src/Controller/Admin/ProjectCrudController.php deleted file mode 100644 index a435adf..0000000 --- a/src/Controller/Admin/ProjectCrudController.php +++ /dev/null @@ -1,93 +0,0 @@ -setDefaultSort(['name' => 'ASC']) - ->setSearchFields(['name', 'details']) - ->showEntityActionsInlined() - ->setPageTitle(Crud::PAGE_INDEX, 'Projects') - ->setHelp(Crud::PAGE_INDEX, 'Projects are synced from Leantime. Click on the "Sync all" button to update all projects.'); - } - - #[\Override] - public function configureActions(Actions $actions): Actions - { - return $actions - ->disable(Action::DELETE, Action::NEW, Action::EDIT) - ->add(Crud::PAGE_INDEX, $this->createLeantimeAction()) - ->add(Crud::PAGE_DETAIL, $this->createLeantimeAction()) - ->add(Crud::PAGE_INDEX, Action::DETAIL) - ->add(Crud::PAGE_INDEX, $this->createExportAction()) - ->add(Crud::PAGE_INDEX, $this->createUpdateAllProjectsAction()); - } - - #[\Override] - public function configureFields(string $pageName): iterable - { - yield VersionField::new('LeantimeId')->setLabel('id'); - yield TextField::new('name'); - yield TextEditorField::new('details')->formatValue(fn ($value) => strip_tags($value)); - yield DateTimeField::new('createdAt')->hideOnIndex(); - } - - public function createLeantimeAction(?string $label = null): Action - { - return Action::new('leantime', $label ?? new TranslatableMessage('Leantime'), 'fa fa-link') - ->linkToUrl(function (Project $project) { - return $project->getLeantimeUrl(); - }); - } - - public function createUpdateAllProjectsAction(?string $label = null): Action - { - return Action::new('update', $label ?? new TranslatableMessage('Sync all'), 'fa fa-rotate') - ->createAsGlobalAction() - ->linkToCrudAction('updateAllProjects'); - } - - public function updateAllProjects(SiteRepository $siteRepository): RedirectResponse - { - try { - $this->projectSyncService->syncAllProjects(); - - $this->addFlash('info', 'All projects have been synced.'); - } catch (\Throwable $e) { - $this->addFlash('error', 'An error occurred while syncing projects. Check the log for details.'); - } - - return $this->redirectToRoute('admin_project_index'); - } -} diff --git a/src/Controller/Admin/SecurityContractCrudController.php b/src/Controller/Admin/SecurityContractCrudController.php index c95ccad..758851d 100644 --- a/src/Controller/Admin/SecurityContractCrudController.php +++ b/src/Controller/Admin/SecurityContractCrudController.php @@ -3,17 +3,31 @@ namespace App\Controller\Admin; use App\Entity\SecurityContract; +use App\Service\ServiceAgreementSyncService; +use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute; +use EasyCorp\Bundle\EasyAdminBundle\Config\Action; +use EasyCorp\Bundle\EasyAdminBundle\Config\Actions; use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; -use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; +use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController; use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField; use EasyCorp\Bundle\EasyAdminBundle\Field\DateField; use EasyCorp\Bundle\EasyAdminBundle\Field\FormField; use EasyCorp\Bundle\EasyAdminBundle\Field\NumberField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField; +use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; use EasyCorp\Bundle\EasyAdminBundle\Field\UrlField; +use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\Translation\TranslatableMessage; -class SecurityContractCrudController extends AbstractFullCrudController +class SecurityContractCrudController extends AbstractCrudController { + public function __construct( + private readonly ServiceAgreementSyncService $syncService, + private readonly AdminUrlGenerator $adminUrlGenerator, + ) { + } + public static function getEntityFqcn(): string { return SecurityContract::class; @@ -23,10 +37,20 @@ public static function getEntityFqcn(): string public function configureCrud(Crud $crud): Crud { return $crud - ->setDefaultSort(['project.name' => 'ASC']) - // ->setSearchFields(['name', 'details']) + ->setDefaultSort(['projectName' => 'ASC']) + ->setSearchFields(['projectName', 'clientName', 'hostingProvider']) ->showEntityActionsInlined() - ->setPageTitle(Crud::PAGE_INDEX, 'Cyber Security Contracts'); + ->setPageTitle(Crud::PAGE_INDEX, 'Service Agreements') + ->setHelp(Crud::PAGE_INDEX, 'Service agreements are synced from Economics. Click "Sync all" to update.'); + } + + #[\Override] + public function configureActions(Actions $actions): Actions + { + return $actions + ->disable(Action::DELETE, Action::NEW, Action::EDIT) + ->add(Crud::PAGE_INDEX, Action::DETAIL) + ->add(Crud::PAGE_INDEX, $this->createSyncAllAction()); } #[\Override] @@ -34,22 +58,62 @@ public function configureFields(string $pageName): iterable { yield FormField::addFieldset('Project'); yield BooleanField::new('active')->renderAsSwitch(false)->setColumns(2); - yield AssociationField::new('project')->setColumns(10); + yield BooleanField::new('eol')->setLabel('EOL')->renderAsSwitch(false)->setColumns(2); + yield TextField::new('projectName')->setColumns(8); + yield TextField::new('clientName')->hideOnIndex(); + yield TextField::new('hostingProvider'); yield FormField::addFieldset('Links'); - yield UrlField::new('project.leantimeUrl')->setLabel('Leantime URL')->setDisabled()->hideOnIndex()->hideWhenCreating(); - yield UrlField::new('economicsReportUrl')->setLabel('Economics URL')->hideOnIndex()->setColumns(12); - yield UrlField::new('operationalContractUrl')->setLabel('Contract URL')->hideOnIndex()->setColumns(12); + yield UrlField::new('leantimeUrl')->setLabel('Leantime URL')->hideOnIndex(); + yield UrlField::new('documentUrl')->setLabel('Document URL')->hideOnIndex(); + + yield FormField::addFieldset('Contact'); + yield TextField::new('clientContactName')->hideOnIndex(); + yield TextField::new('clientContactEmail')->hideOnIndex(); yield FormField::addFieldset('Budget'); yield NumberField::new('monthlyPrice')->setTextAlign('right')->setColumns(6); yield NumberField::new('quarterlyHours')->setTextAlign('right')->setColumns(6); + yield NumberField::new('cybersecurityPrice')->setTextAlign('right')->hideOnIndex()->setColumns(6); + yield TextareaField::new('cybersecurityNote')->hideOnIndex()->setColumns(12); + + yield FormField::addFieldset('Infrastructure'); + yield BooleanField::new('dedicatedServer')->renderAsSwitch(false)->hideOnIndex(); + yield TextField::new('serverSize')->hideOnIndex(); + yield TextareaField::new('gitRepos')->hideOnIndex(); + yield TextField::new('projectTrackerKey')->hideOnIndex(); yield FormField::addFieldset('Validity'); yield DateField::new('validFrom')->setColumns(6); yield DateField::new('validTo')->setColumns(6); + } + + #[AdminRoute] + public function syncAll(): RedirectResponse + { + try { + $count = $this->syncService->syncAll(); + + $this->addFlash('info', sprintf('Synced %d service agreements.', $count)); + } catch (\Throwable $e) { + $this->addFlash('error', sprintf('An error occurred while syncing: %s', $e->getMessage())); + } - yield FormField::addFieldset('Notes'); - yield TextareaField::new('notes')->hideOnIndex()->setColumns(12); + return $this->redirect( + $this->adminUrlGenerator + ->setController(static::class) + ->setAction(Crud::PAGE_INDEX) + ->generateUrl() + ); + } + + private function createSyncAllAction(): Action + { + return Action::new('syncAll', new TranslatableMessage('Sync all'), 'fa fa-rotate') + ->createAsGlobalAction() + ->linkToCrudAction('syncAll') + ->setHtmlAttributes([ + 'onclick' => "const i=this.querySelector('i');if(i){i.classList.add('fa-spin')}this.style.pointerEvents='none';this.style.opacity='0.6'", + ]); } } diff --git a/src/Entity/AbstractHandlerResult.php b/src/Entity/AbstractHandlerResult.php index 66a5dc7..33ab8da 100644 --- a/src/Entity/AbstractHandlerResult.php +++ b/src/Entity/AbstractHandlerResult.php @@ -5,8 +5,8 @@ namespace App\Entity; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation\Groups; -use Symfony\Component\Serializer\Annotation\SerializedName; +use Symfony\Component\Serializer\Attribute\Groups; +use Symfony\Component\Serializer\Attribute\SerializedName; #[ORM\MappedSuperclass] class AbstractHandlerResult extends AbstractBaseEntity diff --git a/src/Entity/Advisory.php b/src/Entity/Advisory.php index 160a914..586f15e 100644 --- a/src/Entity/Advisory.php +++ b/src/Entity/Advisory.php @@ -190,6 +190,23 @@ public function removePackageVersion(PackageVersion $packageVersion): self return $this; } + /** + * @return Collection + */ + public function getSites(): Collection + { + $sites = []; + foreach ($this->packageVersions as $packageVersion) { + foreach ($packageVersion->getInstallations() as $installation) { + foreach ($installation->getSites() as $site) { + $sites[$site->getId()->toRfc4122()] = $site; + } + } + } + + return new ArrayCollection(array_values($sites)); + } + public function getPackage(): ?Package { return $this->package; diff --git a/src/Entity/DetectionResult.php b/src/Entity/DetectionResult.php index 569f069..d47821f 100644 --- a/src/Entity/DetectionResult.php +++ b/src/Entity/DetectionResult.php @@ -4,16 +4,44 @@ namespace App\Entity; +use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Model; use App\Repository\DetectionResultRepository; +use App\Types\DetectionType; use App\Utils\RootDirNormalizer; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Attribute\Groups; #[ApiResource( operations: [ - new Post(status: 202, output: false, messenger: true), + new Post( + status: 202, + output: false, + messenger: true, + openapi: new Model\Operation( + summary: 'Submit a detection result for async processing', + description: 'Accepts a detection result from the server harvester and queues it for asynchronous processing. The result is deduplicated by content hash — identical submissions update the last contact timestamp without triggering reprocessing. Returns 202 Accepted with an empty body.', + responses: [ + '202' => new Model\Response( + description: 'Detection result accepted for processing', + ), + '400' => new Model\Response( + description: 'Invalid input — malformed request body', + ), + '401' => new Model\Response( + description: 'Unauthorized — missing or invalid API key. The Authorization header must use the format: Apikey {key}', + ), + '403' => new Model\Response( + description: 'Forbidden — the authenticated server does not have the required ROLE_SERVER role', + ), + '422' => new Model\Response( + description: 'Validation error — one or more fields failed constraint validation', + ), + ], + ), + ), ], denormalizationContext: ['groups' => ['write']], )] @@ -24,10 +52,19 @@ class DetectionResult extends AbstractBaseEntity implements \Stringable { #[ORM\Column(type: 'string', length: 255)] #[Groups(['write'])] + #[ApiProperty( + description: 'The type of detection result, determines which handler processes the data', + example: DetectionType::NGINX, + schema: ['enum' => [DetectionType::DIRECTORY, DetectionType::DOCKER, DetectionType::DRUPAL, DetectionType::GIT, DetectionType::NGINX, DetectionType::SYMFONY]], + )] private string $type = ''; #[ORM\Column(type: 'string', length: 255)] #[Groups(['write'])] + #[ApiProperty( + description: 'Absolute path to the root directory of the detected installation on the server', + example: '/data/www/example-site/htdocs', + )] private string $rootDir = ''; #[ORM\ManyToOne(targetEntity: Server::class, inversedBy: 'detectionResults')] @@ -36,6 +73,10 @@ class DetectionResult extends AbstractBaseEntity implements \Stringable #[ORM\Column(type: 'text')] #[Groups(['write'])] + #[ApiProperty( + description: 'JSON-encoded payload from the server harvester containing the detection details. Structure varies by type.', + example: '{"packages":{"symfony/framework-bundle":{"version":"7.2.1"}}}', + )] private string $data = ''; #[ORM\Column(type: 'string', length: 255, unique: true)] diff --git a/src/Entity/Domain.php b/src/Entity/Domain.php index e37c58b..02d9f6e 100644 --- a/src/Entity/Domain.php +++ b/src/Entity/Domain.php @@ -6,7 +6,7 @@ use App\Repository\DomainRepository; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Attribute\Groups; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: DomainRepository::class)] diff --git a/src/Entity/Installation.php b/src/Entity/Installation.php index 9d5f1f4..d0d5452 100644 --- a/src/Entity/Installation.php +++ b/src/Entity/Installation.php @@ -9,7 +9,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Attribute\Groups; #[ORM\Entity(repositoryClass: InstallationRepository::class)] #[ORM\AssociationOverrides([ diff --git a/src/Entity/OIDC.php b/src/Entity/OIDC.php index 80dc4d6..56977df 100644 --- a/src/Entity/OIDC.php +++ b/src/Entity/OIDC.php @@ -7,8 +7,8 @@ use App\Repository\OIDCRepository; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation\Groups; -use Symfony\Component\Serializer\Annotation\SerializedName; +use Symfony\Component\Serializer\Attribute\Groups; +use Symfony\Component\Serializer\Attribute\SerializedName; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: OIDCRepository::class)] diff --git a/src/Entity/Project.php b/src/Entity/Project.php deleted file mode 100644 index a1841ed..0000000 --- a/src/Entity/Project.php +++ /dev/null @@ -1,133 +0,0 @@ -leantimeId = $leantimeId; - $this->name = $name; - $this->details = $details; - } - - public function __toString(): string - { - return $this->name; - } - - public function getName(): string - { - return $this->name; - } - - public function setName(string $name): static - { - $this->name = $name; - - return $this; - } - - public function getLeantimeUrl(): ?string - { - return $this->LeantimeUrl; - } - - public function setLeantimeUrl(string $LeantimeUrl): static - { - $this->LeantimeUrl = $LeantimeUrl; - - return $this; - } - - public function getEconomicsUrl(): ?string - { - return $this->EconomicsUrl; - } - - public function setEconomicsUrl(string $EconomicsUrl): static - { - $this->EconomicsUrl = $EconomicsUrl; - - return $this; - } - - public function getDetails(): ?string - { - return $this->details; - } - - public function setDetails(?string $details): static - { - $this->details = $details; - - return $this; - } - - public function getLeantimeId(): ?int - { - return $this->leantimeId; - } - - public function setLeantimeId(int $leantimeId): static - { - $this->leantimeId = $leantimeId; - - return $this; - } - - public function getLeantimeModifiedAt(): ?\DateTimeImmutable - { - return $this->leantimeModifiedAt; - } - - public function setLeantimeModifiedAt(\DateTimeImmutable $leantimeModifiedAt): static - { - $this->leantimeModifiedAt = $leantimeModifiedAt; - - return $this; - } - - public function getSecurityContract(): ?SecurityContract - { - return $this->securityContract; - } - - public function setSecurityContract(SecurityContract $securityContract): static - { - // set the owning side of the relation if necessary - if ($securityContract->getProject() !== $this) { - $securityContract->setProject($this); - } - - $this->securityContract = $securityContract; - - return $this; - } -} diff --git a/src/Entity/SecurityContract.php b/src/Entity/SecurityContract.php index 97741e6..390cdd7 100644 --- a/src/Entity/SecurityContract.php +++ b/src/Entity/SecurityContract.php @@ -9,95 +9,142 @@ #[ORM\Entity(repositoryClass: SecurityContractRepository::class)] class SecurityContract extends AbstractBaseEntity implements \Stringable { - #[ORM\OneToOne(inversedBy: 'securityContract')] - #[ORM\JoinColumn(nullable: false)] - private ?Project $project = null; + #[ORM\Column(unique: true)] + private ?int $economicsId = null; + + #[ORM\Column(length: 255)] + private string $projectName = ''; #[ORM\Column(length: 255, nullable: true)] - private ?string $economicsReportUrl = null; + private ?string $clientName = null; - #[ORM\Column(type: Types::TEXT, nullable: true)] - private ?string $operationalContractUrl = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $hostingProvider = null; - #[ORM\Column(nullable: true)] - private ?float $monthlyPrice = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $documentUrl = null; #[ORM\Column(nullable: true)] - private ?float $quarterlyHours = null; + private ?float $monthlyPrice = null; - #[ORM\Column(type: Types::DATE_IMMUTABLE)] + #[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)] private ?\DateTimeImmutable $validFrom = null; - #[ORM\Column(type: Types::DATE_IMMUTABLE)] + #[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)] private ?\DateTimeImmutable $validTo = null; #[ORM\Column] - private ?bool $active = null; + private bool $active = false; + + #[ORM\Column] + private bool $eol = false; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $leantimeUrl = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $clientContactName = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $clientContactEmail = null; + + #[ORM\Column] + private bool $dedicatedServer = false; - #[ORM\Column(type: Types::TEXT)] - private string $notes = ''; + #[ORM\Column(length: 255, nullable: true)] + private ?string $serverSize = null; + + #[ORM\Column(type: Types::TEXT, nullable: true)] + private ?string $gitRepos = null; + + #[ORM\Column(type: Types::JSON, nullable: true)] + private ?array $systemOwnerNotices = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $projectTrackerKey = null; + + #[ORM\Column(nullable: true)] + private ?float $quarterlyHours = null; + + #[ORM\Column(nullable: true)] + private ?float $cybersecurityPrice = null; + + #[ORM\Column(type: Types::TEXT, nullable: true)] + private ?string $cybersecurityNote = null; public function __toString(): string { - return $this->project->getName(); + return $this->projectName; } - public function getProject(): ?Project + public function getEconomicsId(): ?int { - return $this->project; + return $this->economicsId; } - public function setProject(Project $project): static + public function setEconomicsId(int $economicsId): static { - $this->project = $project; + $this->economicsId = $economicsId; return $this; } - public function getEconomicsReportUrl(): ?string + public function getProjectName(): string { - return $this->economicsReportUrl; + return $this->projectName; } - public function setEconomicsReportUrl(?string $economicsReportUrl): static + public function setProjectName(string $projectName): static { - $this->economicsReportUrl = $economicsReportUrl; + $this->projectName = $projectName; return $this; } - public function getOperationalContractUrl(): ?string + public function getClientName(): ?string { - return $this->operationalContractUrl; + return $this->clientName; } - public function setOperationalContractUrl(?string $operationalContractUrl): static + public function setClientName(?string $clientName): static { - $this->operationalContractUrl = $operationalContractUrl; + $this->clientName = $clientName; return $this; } - public function getMonthlyPrice(): ?float + public function getHostingProvider(): ?string { - return $this->monthlyPrice; + return $this->hostingProvider; } - public function setMonthlyPrice(?float $monthlyPrice): static + public function setHostingProvider(?string $hostingProvider): static { - $this->monthlyPrice = $monthlyPrice; + $this->hostingProvider = $hostingProvider; return $this; } - public function getQuarterlyHours(): ?float + public function getDocumentUrl(): ?string { - return $this->quarterlyHours; + return $this->documentUrl; } - public function setQuarterlyHours(float $quarterlyHours): static + public function setDocumentUrl(?string $documentUrl): static { - $this->quarterlyHours = $quarterlyHours; + $this->documentUrl = $documentUrl; + + return $this; + } + + public function getMonthlyPrice(): ?float + { + return $this->monthlyPrice; + } + + public function setMonthlyPrice(?float $monthlyPrice): static + { + $this->monthlyPrice = $monthlyPrice; return $this; } @@ -107,7 +154,7 @@ public function getValidFrom(): ?\DateTimeImmutable return $this->validFrom; } - public function setValidFrom(\DateTimeImmutable $validFrom): static + public function setValidFrom(?\DateTimeImmutable $validFrom): static { $this->validFrom = $validFrom; @@ -119,14 +166,14 @@ public function getValidTo(): ?\DateTimeImmutable return $this->validTo; } - public function setValidTo(\DateTimeImmutable $validTo): static + public function setValidTo(?\DateTimeImmutable $validTo): static { $this->validTo = $validTo; return $this; } - public function isActive(): ?bool + public function isActive(): bool { return $this->active; } @@ -138,14 +185,146 @@ public function setActive(bool $active): static return $this; } - public function getNotes(): ?string + public function isEol(): bool + { + return $this->eol; + } + + public function setEol(bool $eol): static + { + $this->eol = $eol; + + return $this; + } + + public function getLeantimeUrl(): ?string + { + return $this->leantimeUrl; + } + + public function setLeantimeUrl(?string $leantimeUrl): static + { + $this->leantimeUrl = $leantimeUrl; + + return $this; + } + + public function getClientContactName(): ?string + { + return $this->clientContactName; + } + + public function setClientContactName(?string $clientContactName): static + { + $this->clientContactName = $clientContactName; + + return $this; + } + + public function getClientContactEmail(): ?string + { + return $this->clientContactEmail; + } + + public function setClientContactEmail(?string $clientContactEmail): static + { + $this->clientContactEmail = $clientContactEmail; + + return $this; + } + + public function isDedicatedServer(): bool + { + return $this->dedicatedServer; + } + + public function setDedicatedServer(bool $dedicatedServer): static + { + $this->dedicatedServer = $dedicatedServer; + + return $this; + } + + public function getServerSize(): ?string + { + return $this->serverSize; + } + + public function setServerSize(?string $serverSize): static + { + $this->serverSize = $serverSize; + + return $this; + } + + public function getGitRepos(): ?string + { + return $this->gitRepos; + } + + public function setGitRepos(?string $gitRepos): static + { + $this->gitRepos = $gitRepos; + + return $this; + } + + public function getSystemOwnerNotices(): ?array + { + return $this->systemOwnerNotices; + } + + public function setSystemOwnerNotices(?array $systemOwnerNotices): static + { + $this->systemOwnerNotices = $systemOwnerNotices; + + return $this; + } + + public function getProjectTrackerKey(): ?string + { + return $this->projectTrackerKey; + } + + public function setProjectTrackerKey(?string $projectTrackerKey): static + { + $this->projectTrackerKey = $projectTrackerKey; + + return $this; + } + + public function getQuarterlyHours(): ?float + { + return $this->quarterlyHours; + } + + public function setQuarterlyHours(?float $quarterlyHours): static + { + $this->quarterlyHours = $quarterlyHours; + + return $this; + } + + public function getCybersecurityPrice(): ?float + { + return $this->cybersecurityPrice; + } + + public function setCybersecurityPrice(?float $cybersecurityPrice): static + { + $this->cybersecurityPrice = $cybersecurityPrice; + + return $this; + } + + public function getCybersecurityNote(): ?string { - return $this->notes; + return $this->cybersecurityNote; } - public function setNotes(string $notes): static + public function setCybersecurityNote(?string $cybersecurityNote): static { - $this->notes = $notes; + $this->cybersecurityNote = $cybersecurityNote; return $this; } diff --git a/src/Entity/Server.php b/src/Entity/Server.php index 03c7060..cdf15b1 100644 --- a/src/Entity/Server.php +++ b/src/Entity/Server.php @@ -9,8 +9,8 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Serializer\Annotation\Groups; -use Symfony\Component\Serializer\Annotation\SerializedName; +use Symfony\Component\Serializer\Attribute\Groups; +use Symfony\Component\Serializer\Attribute\SerializedName; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: ServerRepository::class)] @@ -109,12 +109,6 @@ public function getRoles(): array return self::ROLES; } - #[\Deprecated] - public function eraseCredentials(): void - { - // Deprecated: since Symfony 7.3, erase credentials using the "__serialize()" method instead - } - public function getUserIdentifier(): string { return $this->getApiKey(); diff --git a/src/Entity/ServiceCertificate.php b/src/Entity/ServiceCertificate.php index d417ed1..2f11207 100644 --- a/src/Entity/ServiceCertificate.php +++ b/src/Entity/ServiceCertificate.php @@ -10,7 +10,7 @@ use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Attribute\Groups; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: ServiceCertificateRepository::class)] diff --git a/src/Entity/ServiceCertificate/Service.php b/src/Entity/ServiceCertificate/Service.php index 86d980c..b42f744 100644 --- a/src/Entity/ServiceCertificate/Service.php +++ b/src/Entity/ServiceCertificate/Service.php @@ -8,7 +8,7 @@ use App\Entity\ServiceCertificate; use App\Repository\ServiceCertificate\ServiceRepository; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Attribute\Groups; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: ServiceRepository::class)] diff --git a/src/Entity/Site.php b/src/Entity/Site.php index 08d606a..dbfd6c7 100644 --- a/src/Entity/Site.php +++ b/src/Entity/Site.php @@ -9,8 +9,8 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation\Groups; -use Symfony\Component\Serializer\Annotation\SerializedName; +use Symfony\Component\Serializer\Attribute\Groups; +use Symfony\Component\Serializer\Attribute\SerializedName; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: SiteRepository::class)] @@ -200,7 +200,10 @@ public function setType(string $type): self return $this; } - public function getAdvisoryCount(): int + /** + * @return Collection + */ + public function getAdvisories(): Collection { $advisories = new ArrayCollection(); foreach ($this->installation->getPackageVersions() as $packageVersion) { @@ -209,6 +212,11 @@ public function getAdvisoryCount(): int } } - return $advisories->count(); + return $advisories; + } + + public function getAdvisoryCount(): int + { + return $this->getAdvisories()->count(); } } diff --git a/src/Entity/User.php b/src/Entity/User.php index f58cbfa..70ad536 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -68,15 +68,6 @@ public function setRoles(array $roles): self return $this; } - /** - * @see UserInterface - */ - public function eraseCredentials(): void - { - // If you store any temporary, sensitive data on the user, clear it here - // $this->plainPassword = null; - } - public function getName(): ?string { return $this->name; diff --git a/src/Exception/LeantimeApiException.php b/src/Exception/LeantimeApiException.php deleted file mode 100644 index ba79156..0000000 --- a/src/Exception/LeantimeApiException.php +++ /dev/null @@ -1,7 +0,0 @@ -setFilterFqcn(self::class) ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Form/Type/Admin/FrameworkFilter.php b/src/Form/Type/Admin/FrameworkFilter.php index 9cc469c..9bb8a1c 100644 --- a/src/Form/Type/Admin/FrameworkFilter.php +++ b/src/Form/Type/Admin/FrameworkFilter.php @@ -18,7 +18,7 @@ class FrameworkFilter implements FilterInterface public static function new(string $propertyName, false|string|TranslatableInterface|null $label = null): self { - return (new self()) + return new self() ->setFilterFqcn(self::class) ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Form/Type/Admin/HostingProviderFilter.php b/src/Form/Type/Admin/HostingProviderFilter.php index 8a518df..0be1203 100644 --- a/src/Form/Type/Admin/HostingProviderFilter.php +++ b/src/Form/Type/Admin/HostingProviderFilter.php @@ -18,7 +18,7 @@ class HostingProviderFilter implements FilterInterface public static function new(string $propertyName, false|string|TranslatableInterface|null $label = null): self { - return (new self()) + return new self() ->setFilterFqcn(self::class) ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Form/Type/Admin/MariaDbVersionFilter.php b/src/Form/Type/Admin/MariaDbVersionFilter.php index 6633a5e..69b040f 100644 --- a/src/Form/Type/Admin/MariaDbVersionFilter.php +++ b/src/Form/Type/Admin/MariaDbVersionFilter.php @@ -18,7 +18,7 @@ class MariaDbVersionFilter implements FilterInterface public static function new(string $propertyName, false|string|TranslatableInterface|null $label = null): self { - return (new self()) + return new self() ->setFilterFqcn(self::class) ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Form/Type/Admin/ServerTypeFilter.php b/src/Form/Type/Admin/ServerTypeFilter.php index 495e399..502dd91 100644 --- a/src/Form/Type/Admin/ServerTypeFilter.php +++ b/src/Form/Type/Admin/ServerTypeFilter.php @@ -18,7 +18,7 @@ class ServerTypeFilter implements FilterInterface public static function new(string $propertyName, false|string|TranslatableInterface|null $label = null): self { - return (new self()) + return new self() ->setFilterFqcn(self::class) ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Form/Type/Admin/SystemFilter.php b/src/Form/Type/Admin/SystemFilter.php index 154d97d..ec6b577 100644 --- a/src/Form/Type/Admin/SystemFilter.php +++ b/src/Form/Type/Admin/SystemFilter.php @@ -18,7 +18,7 @@ class SystemFilter implements FilterInterface public static function new(string $propertyName, false|string|TranslatableInterface|null $label = null): self { - return (new self()) + return new self() ->setFilterFqcn(self::class) ->setProperty($propertyName) ->setLabel($label) diff --git a/src/Form/Type/ServiceCertificate/ServiceType.php b/src/Form/Type/ServiceCertificate/ServiceType.php index 8507ac1..40a5565 100644 --- a/src/Form/Type/ServiceCertificate/ServiceType.php +++ b/src/Form/Type/ServiceCertificate/ServiceType.php @@ -34,6 +34,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ]) ->add('onePasswordUrl', UrlType::class, [ 'label' => new TranslatableMessage('1Password url'), + 'default_protocol' => 'https', ]) ; } diff --git a/src/Handler/DockerImageHandler.php b/src/Handler/DockerImageHandler.php index 0f4cc27..a460b4a 100644 --- a/src/Handler/DockerImageHandler.php +++ b/src/Handler/DockerImageHandler.php @@ -86,7 +86,7 @@ private function getPhpVersionFromContainers(array $containers): string $matches = []; foreach ($containers as $container) { if (self::PHP_CONTAINER === $container->name) { - \preg_match('/\d.+\d/', $container->image, $matches); + \preg_match('/\d.+\d/', (string) $container->image, $matches); } } diff --git a/src/Handler/NginxHandler.php b/src/Handler/NginxHandler.php index 214c19a..75f9276 100644 --- a/src/Handler/NginxHandler.php +++ b/src/Handler/NginxHandler.php @@ -34,7 +34,7 @@ public function handleResult(DetectionResult $detectionResult): void $data = \json_decode($detectionResult->getData(), false, 512, JSON_THROW_ON_ERROR); // Nginx 'default' sites should not be indexed. - if (str_ends_with($data->config, self::NGINX_DEFAULT)) { + if (str_ends_with((string) $data->config, self::NGINX_DEFAULT)) { return; } diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php new file mode 100644 index 0000000..9e6d642 --- /dev/null +++ b/src/OpenApi/OpenApiFactory.php @@ -0,0 +1,43 @@ +decorated->__invoke($context); + + $domain = ($this->serverDomain ?? '') !== '' ? $this->serverDomain : $this->fallbackDomain; + + if (null !== $domain && '' !== $domain) { + $openApi = $openApi->withServers([new Server('https://'.$domain)]); + } + + $securitySchemes = $openApi->getComponents()->getSecuritySchemes(); + if ($securitySchemes instanceof \ArrayObject && isset($securitySchemes['apiKey']) && $securitySchemes['apiKey'] instanceof SecurityScheme) { + $securitySchemes['apiKey'] = $securitySchemes['apiKey']->withDescription('Server API key. Use the format: Apikey {your-api-key}'); + } + + return $openApi; + } +} diff --git a/src/Repository/GitRepoRepository.php b/src/Repository/GitRepoRepository.php index 63a9419..d960d78 100644 --- a/src/Repository/GitRepoRepository.php +++ b/src/Repository/GitRepoRepository.php @@ -9,7 +9,7 @@ use Doctrine\Persistence\ManagerRegistry; /** - * @extends ServiceEntityRepository + * @extends ServiceEntityRepository * * @method GitRepo|null find($id, $lockMode = null, $lockVersion = null) * @method GitRepo|null findOneBy(array $criteria, array $orderBy = null) diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php deleted file mode 100644 index 0865b50..0000000 --- a/src/Repository/ProjectRepository.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -class ProjectRepository extends ServiceEntityRepository -{ - public function __construct(ManagerRegistry $registry) - { - parent::__construct($registry, Project::class); - } - - // /** - // * @return Project[] Returns an array of Project objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('p') - // ->andWhere('p.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('p.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?Project - // { - // return $this->createQueryBuilder('p') - // ->andWhere('p.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } -} diff --git a/src/Serializer/Normalizer/CustomNormalizer.php b/src/Serializer/Normalizer/CustomNormalizer.php index cc794f4..ba4b863 100644 --- a/src/Serializer/Normalizer/CustomNormalizer.php +++ b/src/Serializer/Normalizer/CustomNormalizer.php @@ -12,7 +12,7 @@ class CustomNormalizer implements NormalizerInterface { public function __construct( #[Autowire(service: 'serializer.normalizer.object')] - private NormalizerInterface $normalizer, + private readonly NormalizerInterface $normalizer, ) { } diff --git a/src/Service/DockerImageTagFactory.php b/src/Service/DockerImageTagFactory.php index 0a31b49..9aa7222 100644 --- a/src/Service/DockerImageTagFactory.php +++ b/src/Service/DockerImageTagFactory.php @@ -32,7 +32,7 @@ public function setDockerImageTags(Installation $installation, array $containers $dockerImageTags = new ArrayCollection(); $images = []; foreach ($containersKeyed as $container) { - $parts = explode('/', $container->image); + $parts = explode('/', (string) $container->image); $organization = $parts[0]; $repository = $parts[1] ?? ''; diff --git a/src/Service/Exporter.php b/src/Service/Exporter.php index c3203bc..3026fd6 100644 --- a/src/Service/Exporter.php +++ b/src/Service/Exporter.php @@ -33,7 +33,7 @@ public function export(AbstractQuery $query, string $className, string $format = 'itksites-export-%s-%s.%s', preg_replace('@^.+\\\\([^\\\\]+)$@', '$1', $className), // Windows cannot handle colons in filenames so we use . to separate time parts. - (new \DateTimeImmutable())->format('Y-m-d\TH.i.s'), + new \DateTimeImmutable()->format('Y-m-d\TH.i.s'), $format ); diff --git a/src/Service/Leantime/ApiService.php b/src/Service/Leantime/ApiService.php deleted file mode 100644 index 83a3ea6..0000000 --- a/src/Service/Leantime/ApiService.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * @throws LeantimeApiException - */ - public function getProjects(): array - { - $projects = []; - - $data = $this->request(self::API_PATH_JSONRPC, 'POST', 'leantime.rpc.projects.getAll', []); - - foreach ($data as $project) { - $projects[$project->id] = ProjectDto::fromStdClass($project); - } - - return $projects; - } - - /** - * Get project. - * - * @param $key - * A project key or id - * - * @throws LeantimeApiException - */ - public function getProject(int $key): ProjectDto - { - $data = $this->request(self::API_PATH_JSONRPC, 'POST', 'leantime.rpc.projects.getProject', ['id' => $key]); - - if (is_array($data) && false === $data[0]) { - throw new LeantimeApiException(sprintf('Project (id: %d) not found', $key), 404); - } - - return ProjectDto::fromStdClass($data); - } - - /** - * Get from Leantime. - * - * @throws LeantimeApiException - */ - private function request(string $path, string $type, string $method, array $params = []): mixed - { - try { - $response = $this->leantimeClient->request($type, $path, - ['json' => [ - 'jsonrpc' => '2.0', - 'method' => $method, - 'id' => new Ulid()->jsonSerialize(), - 'params' => $params, - ]] - ); - - $body = $response->getContent(); - - if ($body) { - $data = json_decode($body, null, 512, JSON_THROW_ON_ERROR); - - if (isset($data->error)) { - $message = $data->error->message; - if (isset($data->error->data)) { - $message .= ': '.(is_scalar($data->error->data) ? $data->error->data : json_encode($data->error->data)); - } - throw new LeantimeApiException($message, $data->error->code); - } - - return $data->result; - } - } catch (\Throwable $e) { - throw new LeantimeApiException('Error from Leantime API: '.$e->getMessage(), (int) $e->getCode(), $e); - } - - return null; - } -} diff --git a/src/Service/Leantime/LeantimeProjectUrlFactory.php b/src/Service/Leantime/LeantimeProjectUrlFactory.php deleted file mode 100644 index 5ed6f5d..0000000 --- a/src/Service/Leantime/LeantimeProjectUrlFactory.php +++ /dev/null @@ -1,17 +0,0 @@ -baseUrl, $projectId); - } -} diff --git a/src/Service/Leantime/ProjectDto.php b/src/Service/Leantime/ProjectDto.php deleted file mode 100644 index 986f051..0000000 --- a/src/Service/Leantime/ProjectDto.php +++ /dev/null @@ -1,75 +0,0 @@ -Udfyld beskrivelse

'; - - public function __construct( - #[Map(target: 'leantimeId')] - #[Map(target: 'leantimeUrl', transform: LeantimeProjectUrlFactory::class)] - public int $id, - #[Map(target: 'name')] - public string $name, - #[Map(target: 'details')] - public ?string $details, - public int $clientId, - public ?int $state, - public float $hourBudget, - public float $dollarBudget, - public string $menuType, - public string $type, - public ?int $parent, - #[Map(target: 'leantimeModifiedAt')] - public \DateTimeImmutable $modified, - public ?string $start, - public ?string $end, - public string $clientName, - public ?int $parentId, - public ?string $parentName, - public int $isFavorite, - ) { - } - - /** - * Create a LeantimeProjectDto from JSON data. - */ - public static function fromStdClass(\stdClass $data): self - { - $data->details = self::DETAILS_DEFAULT === $data->details ? null : $data->details; - $data->hourBudget = floatval($data->hourBudget); - $data->dollarBudget = floatval($data->dollarBudget); - $data->modified = \DateTimeImmutable::createFromFormat( - self::DATE_FORMAT, - $data->modified, - new \DateTimeZone(self::TIMEZONE) - ); - - return new self( - id: $data->id, - name: $data->name, - details: $data->details ?? null, - clientId: $data->clientId, - state: $data->state, - hourBudget: $data->hourBudget, - dollarBudget: $data->dollarBudget, - menuType: $data->menuType, - type: $data->type, - parent: $data->parent, - modified: $data->modified, - start: $data->start ?? null, - end: $data->end ?? null, - clientName: $data->clientName, - parentId: $data->parentId ?? null, - parentName: $data->parentName ?? null, - isFavorite: $data->isFavorite, - ); - } -} diff --git a/src/Service/Leantime/ProjectSyncService.php b/src/Service/Leantime/ProjectSyncService.php deleted file mode 100644 index 60f3ae8..0000000 --- a/src/Service/Leantime/ProjectSyncService.php +++ /dev/null @@ -1,76 +0,0 @@ -leantimeApiService->getProjects(); - $count = count($projectDtoArray); - - $projects = $this->entityManager->getRepository(Project::class)->findBy([], ['id' => 'ASC']); - - foreach ($projects as $project) { - if (isset($projectDtoArray[$project->getLeantimeId()])) { - $dto = $projectDtoArray[$project->getLeantimeId()]; - $this->setProperties($project, $dto); - - unset($projectDtoArray[$project->getLeantimeId()]); - } else { - // Delete projects that are not in Leantime - $this->entityManager->remove($project); - } - } - - foreach ($projectDtoArray as $id => $projectDto) { - $project = $this->objectMapper->map($projectDto, Project::class); - $this->setProperties($project, $projectDto); - $this->entityManager->persist($project); - } - - $this->entityManager->flush(); - - return $count; - } - - public function syncProject(int $leantimeId): Project - { - $projectDto = $this->leantimeApiService->getProject($leantimeId); - $project = $this->entityManager->getRepository(Project::class)->findOneBy(['leantimeId' => $leantimeId]); - - if (null === $project) { - $project = $this->objectMapper->map($projectDto, Project::class); - $this->entityManager->persist($project); - } - - $this->setProperties($project, $projectDto); - - $this->entityManager->flush(); - - return $project; - } - - private function setProperties(Project $project, ProjectDto $projectDto): void - { - $project->setLeantimeId($projectDto->id); - $project->setName($projectDto->name); - $project->setDetails($projectDto->details); - $project->setLeantimeModifiedAt($projectDto->modified); - - $url = $this->leantimeProjectUrlFactory->getProjectUrl($project->getLeantimeId()); - $project->setLeantimeUrl($url); - } -} diff --git a/src/Service/PackageVersionFactory.php b/src/Service/PackageVersionFactory.php index ac997c5..dff3bee 100644 --- a/src/Service/PackageVersionFactory.php +++ b/src/Service/PackageVersionFactory.php @@ -28,7 +28,7 @@ public function setPackageVersions(Installation $installation, array $installedP { $packageVersions = new ArrayCollection(); foreach ($installedPackages as $installed) { - [$vendor, $name] = explode('/', $installed->name); + [$vendor, $name] = explode('/', (string) $installed->name); $package = $this->getPackage($vendor, $name); diff --git a/src/Service/ServiceAgreementSyncService.php b/src/Service/ServiceAgreementSyncService.php new file mode 100644 index 0000000..6b19dc2 --- /dev/null +++ b/src/Service/ServiceAgreementSyncService.php @@ -0,0 +1,133 @@ +economicsClient->request('GET', '/api/serviceagreements'); + $agreements = $response->toArray(); + } catch (ExceptionInterface $e) { + throw new \RuntimeException('Failed to fetch service agreements from Economics API: '.$e->getMessage(), 0, $e); + } + + $existingContracts = $this->entityManager->getRepository(SecurityContract::class)->findAll(); + $existingByEconomicsId = []; + foreach ($existingContracts as $contract) { + $existingByEconomicsId[$contract->getEconomicsId()] = $contract; + } + + $seenIds = []; + + foreach ($agreements as $data) { + $economicsId = $data['id']; + $seenIds[] = $economicsId; + + $contract = $existingByEconomicsId[$economicsId] ?? new SecurityContract(); + + $this->mapDataToContract($contract, $data); + + if (null === $contract->getEconomicsId()) { + $contract->setEconomicsId($economicsId); + } + + $this->entityManager->persist($contract); + } + + foreach ($existingContracts as $contract) { + if (!in_array($contract->getEconomicsId(), $seenIds, true)) { + $this->entityManager->remove($contract); + } + } + + $this->entityManager->flush(); + + return count($agreements); + } + + /** + * Map API response data onto a SecurityContract entity. + * + * @param array $data a single service agreement from the API + * @throws \Exception + */ + private function mapDataToContract(SecurityContract $contract, array $data): void + { + $contract->setEconomicsId($data['id']); + $contract->setProjectName($data['projectName'] ?? ''); + $contract->setClientName($data['clientName'] ?? null); + $contract->setHostingProvider($data['hostingProvider'] ?? null); + $contract->setDocumentUrl($data['documentUrl'] ?? null); + $contract->setMonthlyPrice(isset($data['price']) ? (float) $data['price'] : null); + $contract->setValidFrom($this->parseDate($data['validFrom'] ?? null)); + $contract->setValidTo($this->parseDate($data['validTo'] ?? null)); + $contract->setActive($data['isActive'] ?? false); + $contract->setEol($data['isEol'] ?? false); + $contract->setLeantimeUrl($data['leantimeUrl'] ?? null); + $contract->setClientContactName($data['clientContactName'] ?? null); + $contract->setClientContactEmail($data['clientContactEmail'] ?? null); + $contract->setDedicatedServer($data['dedicatedServer'] ?? false); + $contract->setServerSize($data['serverSize'] ?? null); + $contract->setGitRepos($data['gitRepos'] ?? null); + $contract->setSystemOwnerNotices($data['systemOwnerNotices'] ?? null); + $contract->setProjectTrackerKey($data['projectTrackerKey'] ?? null); + + $cyber = $data['cybersecurityAgreement'] ?? null; + if (is_array($cyber)) { + $contract->setQuarterlyHours(isset($cyber['quarterlyHours']) ? (float) $cyber['quarterlyHours'] : null); + $contract->setCybersecurityPrice(isset($cyber['price']) ? (float) $cyber['price'] : null); + $contract->setCybersecurityNote($cyber['note'] ?? null); + } + } + + /** + * Parse the Economics API date format ({date, timezone_type, timezone}) into a DateTimeImmutable. + * + * @param array{date?: string, timezone_type?: int, timezone?: string}|null $dateData raw date object from the API + * + * @return \DateTimeImmutable|null the parsed date, or null if no valid date data was provided + * + * @throws \Exception if the date string cannot be parsed + */ + private function parseDate(?array $dateData): ?\DateTimeImmutable + { + if (null === $dateData || !isset($dateData['date'])) { + return null; + } + + $timezone = new \DateTimeZone($dateData['timezone'] ?? 'UTC'); + + return new \DateTimeImmutable($dateData['date'], $timezone); + } +} diff --git a/src/Trait/ExportCrudControllerTrait.php b/src/Trait/ExportCrudControllerTrait.php index 4e9656c..71f5c2f 100644 --- a/src/Trait/ExportCrudControllerTrait.php +++ b/src/Trait/ExportCrudControllerTrait.php @@ -5,6 +5,7 @@ namespace App\Trait; use App\Service\Exporter; +use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute; use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection; use EasyCorp\Bundle\EasyAdminBundle\Config\Action; use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; @@ -39,6 +40,7 @@ protected function createExportAction(string|TranslatableMessage|null $label = n ->linkToCrudAction('export'); } + #[AdminRoute] public function export(AdminContext $context): Response { if (!isset($this->exporter)) { @@ -47,7 +49,7 @@ public function export(AdminContext $context): Response assert($this instanceof AbstractCrudController); // Lifted from self::index(). - $fields = FieldCollection::new($this->configureFields(Crud::PAGE_INDEX)); + $fields = new FieldCollection($this->configureFields(Crud::PAGE_INDEX)); $context->getCrud()->setFieldAssets($this->getFieldAssets($fields)); $filters = $this->filterFactory->create($context->getCrud()->getFiltersConfig(), $fields, $context->getEntity()); diff --git a/src/Twig/AppExtension.php b/src/Twig/AppExtension.php index 498c786..90be01a 100644 --- a/src/Twig/AppExtension.php +++ b/src/Twig/AppExtension.php @@ -5,14 +5,14 @@ namespace App\Twig; use App\Entity\AbstractBaseEntity; -use EasyCorp\Bundle\EasyAdminBundle\Registry\CrudControllerRegistry; +use EasyCorp\Bundle\EasyAdminBundle\Contracts\Registry\AdminControllerRegistryInterface; use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; class AppExtension extends AbstractExtension { - public function __construct(private readonly AdminUrlGenerator $adminUrlGenerator, private readonly CrudControllerRegistry $crudControllerRegistry) + public function __construct(private readonly AdminUrlGenerator $adminUrlGenerator, private readonly AdminControllerRegistryInterface $crudControllerRegistry) { } @@ -36,9 +36,9 @@ public function entityDisplay(AbstractBaseEntity $entity, string $action): strin if (method_exists($entity, 'display')) { if ('detail' == $action) { return $entity->display(1); - } else { - return $entity->display(0); } + + return $entity->display(0); } return $entity->__toString(); @@ -46,7 +46,7 @@ public function entityDisplay(AbstractBaseEntity $entity, string $action): strin public function url(AbstractBaseEntity $entity): string { - $crudController = $this->crudControllerRegistry->findCrudFqcnByEntityFqcn($entity::class); + $crudController = $this->crudControllerRegistry->findCrudControllerByEntity($entity::class); $id = $entity->getId(); if (null === $id || null === $crudController) { return ''; diff --git a/symfony.lock b/symfony.lock index 68d5966..4e95b38 100644 --- a/symfony.lock +++ b/symfony.lock @@ -38,12 +38,12 @@ "version": "v0.5.3" }, "doctrine/doctrine-bundle": { - "version": "2.13", + "version": "3.2", "recipe": { "repo": "github.com/symfony/recipes", "branch": "main", - "version": "2.10", - "ref": "c170ded8fc587d6bd670550c43dafcf093762245" + "version": "3.0", + "ref": "18ee08e513ba0303fd09a01fc1c934870af06ffa" }, "files": [ "config/packages/doctrine.yaml", @@ -203,15 +203,6 @@ "php-cs-fixer/diff": { "version": "v2.0.2" }, - "phpdocumentor/reflection-common": { - "version": "2.2.0" - }, - "phpdocumentor/reflection-docblock": { - "version": "5.3.0" - }, - "phpdocumentor/type-resolver": { - "version": "1.6.0" - }, "phpspec/prophecy": { "version": "v1.15.0" }, @@ -694,7 +685,7 @@ "version": "v3.3.8" }, "vincentlanglet/twig-cs-fixer": { - "version": "3.10", + "version": "3.14", "recipe": { "repo": "github.com/symfony/recipes-contrib", "branch": "main", diff --git a/templates/EasyAdminBundle/Fields/advisories.html.twig b/templates/EasyAdminBundle/Fields/advisories.html.twig new file mode 100644 index 0000000..cfe1bba --- /dev/null +++ b/templates/EasyAdminBundle/Fields/advisories.html.twig @@ -0,0 +1,33 @@ +{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #} +{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #} +{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #} +{% if ea().crud.currentAction == 'detail' %} + {% set advisories = entity.instance.advisories %} + {% if advisories|length > 0 %} + + + + + + + + + + {% for advisory in advisories %} + {% set url = ea_url() + .unsetAll() + .setController('App\\Controller\\Admin\\AdvisoryCrudController') + .setAction('detail') + .setEntityId(advisory.id) %} + + + + + + {% endfor %} + +
PackageCVETitle
{{ advisory.package }}{{ advisory.cve ?? advisory.advisoryId }}{{ advisory.title }}
+ {% endif %} +{% elseif field.formattedValue != 0 %} + {{ field.formattedValue }} +{% endif %} diff --git a/templates/EasyAdminBundle/Fields/affected_sites.html.twig b/templates/EasyAdminBundle/Fields/affected_sites.html.twig new file mode 100644 index 0000000..a2eb5e3 --- /dev/null +++ b/templates/EasyAdminBundle/Fields/affected_sites.html.twig @@ -0,0 +1,33 @@ +{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #} +{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #} +{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #} +{% set sites = entity.instance.sites %} +{% if ea().crud.currentAction == 'detail' %} + {% if sites|length > 0 %} + + + + + + + + + {% for site in sites %} + {% set url = ea_url() + .unsetAll() + .setController('App\\Controller\\Admin\\SiteCrudController') + .setAction('detail') + .setEntityId(site.id) %} + + + + + {% endfor %} + +
DomainServer
{{ site.primaryDomain }}{{ site.server }}
+ {% else %} + None + {% endif %} +{% else %} + {{ sites|length }} +{% endif %} diff --git a/templates/EasyAdminBundle/Fields/config_file_path.html.twig b/templates/EasyAdminBundle/Fields/config_file_path.html.twig index da9307a..1866d11 100644 --- a/templates/EasyAdminBundle/Fields/config_file_path.html.twig +++ b/templates/EasyAdminBundle/Fields/config_file_path.html.twig @@ -1,7 +1,7 @@ {# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #} {# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #} {# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #} -{% if ea.crud.currentAction == 'detail' %} +{% if ea().crud.currentAction == 'detail' %} {{ field.formattedValue }} {% else %} {{ field.formattedValue|replace({'/etc/nginx/sites-enabled/': './'}) }} diff --git a/templates/EasyAdminBundle/Fields/domain.html.twig b/templates/EasyAdminBundle/Fields/domain.html.twig index 3dc99f1..accac03 100644 --- a/templates/EasyAdminBundle/Fields/domain.html.twig +++ b/templates/EasyAdminBundle/Fields/domain.html.twig @@ -5,7 +5,7 @@ (see https://web.dev/external-anchors-use-rel-noopener/) #} {% if field.formattedValue == 'unknown' %} ? -{% elseif ea.crud.currentAction == 'detail' %} +{% elseif ea().crud.currentAction == 'detail' %} {{ field.value }} {% else %} {{ field.formattedValue }} diff --git a/templates/EasyAdminBundle/Fields/eol.html.twig b/templates/EasyAdminBundle/Fields/eol.html.twig index 9acd5bd..91efa11 100644 --- a/templates/EasyAdminBundle/Fields/eol.html.twig +++ b/templates/EasyAdminBundle/Fields/eol.html.twig @@ -3,7 +3,8 @@ {# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #} {% set slicedDate = field.formattedValue|slice(0, 7) %} -{% if ea.crud.currentAction == 'detail' %} + +{% if ea().crud.currentAction == 'detail' %} {% set outputValue = field.formattedValue %} {% else %} {% set outputValue = slicedDate %} diff --git a/templates/EasyAdminBundle/Fields/root_dir.html.twig b/templates/EasyAdminBundle/Fields/root_dir.html.twig index c31765c..8626ad8 100644 --- a/templates/EasyAdminBundle/Fields/root_dir.html.twig +++ b/templates/EasyAdminBundle/Fields/root_dir.html.twig @@ -1,7 +1,7 @@ {# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #} {# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #} {# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #} -{% if ea.crud.currentAction == 'detail' %} +{% if ea().crud.currentAction == 'detail' %} {{ field.formattedValue }} {% else %} diff --git a/templates/EasyAdminBundle/Fields/sources.html.twig b/templates/EasyAdminBundle/Fields/sources.html.twig index a4ba5ad..34301d1 100644 --- a/templates/EasyAdminBundle/Fields/sources.html.twig +++ b/templates/EasyAdminBundle/Fields/sources.html.twig @@ -1,7 +1,7 @@ {# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #} {# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #} {# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #} -{% if ea.crud.currentAction == 'detail' %} +{% if ea().crud.currentAction == 'detail' %}
    {% for item in field.value %} {#
  • {{ item }}
  • #} diff --git a/templates/EasyAdminBundle/Fields/warning.html.twig b/templates/EasyAdminBundle/Fields/warning.html.twig index daf0484..27adefc 100644 --- a/templates/EasyAdminBundle/Fields/warning.html.twig +++ b/templates/EasyAdminBundle/Fields/warning.html.twig @@ -1,7 +1,7 @@ {# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #} {# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #} {# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #} -{% if ea.crud.currentAction == 'detail' %} +{% if ea().crud.currentAction == 'detail' %} {{ field.formattedValue|raw|nl2br }} {% else %} {% if field.formattedValue is null %} diff --git a/templates/bundles/EasyAdminBundle/crud/field/association.html.twig b/templates/bundles/EasyAdminBundle/crud/field/association.html.twig index 8bd21aa..7d5393a 100644 --- a/templates/bundles/EasyAdminBundle/crud/field/association.html.twig +++ b/templates/bundles/EasyAdminBundle/crud/field/association.html.twig @@ -4,14 +4,14 @@ {% if 'toMany' == field.customOptions.get('associationType') %} {% if field.value.count == 0 %} None - {% elseif not has_display(field.value[0]) and ea.crud.currentAction != 'detail' %} + {% elseif not has_display(field.value[0]) and ea().crud.currentAction != 'detail' %} {{ field.value|length }} {% else %} {% for value in field.value %} - {% if ea.crud.currentAction == 'detail' %} - + {% if ea().crud.currentAction == 'detail' %} + {% elseif has_display(value) %} - {{ entity_display(value, ea.crud.currentAction) }} + {{ entity_display(value, ea().crud.currentAction) }} {% else %} None {% endif %} diff --git a/templates/service_certificate/services.html.twig b/templates/service_certificate/services.html.twig index 714ed20..eebbee1 100644 --- a/templates/service_certificate/services.html.twig +++ b/templates/service_certificate/services.html.twig @@ -1,5 +1,5 @@ {# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #} -{% if ea.crud.currentPage == constant('EasyCorp\\Bundle\\EasyAdminBundle\\Config\\Crud::PAGE_INDEX') %} +{% if ea().crud.currentPage == constant('EasyCorp\\Bundle\\EasyAdminBundle\\Config\\Crud::PAGE_INDEX') %} {% for service in field.value -%} {{ loop.index0 > 0 ? ', ' }}{{ service }} {%- endfor %} diff --git a/tests/Controller/Admin/AdminSmokeTest.php b/tests/Controller/Admin/AdminSmokeTest.php new file mode 100644 index 0000000..c41e253 --- /dev/null +++ b/tests/Controller/Admin/AdminSmokeTest.php @@ -0,0 +1,75 @@ +get('doctrine')->getManager() + ->getRepository(User::class)->findOneBy([]); + $client->loginUser($user); + + $url = static::getContainer()->get(AdminUrlGenerator::class) + ->setController($controllerClass) + ->setAction(Crud::PAGE_INDEX) + ->generateUrl(); + + $client->request('GET', $url); + + $this->assertResponseIsSuccessful(); + } + + /** + * @return iterable + */ + public static function crudControllerProvider(): iterable + { + yield 'Advisory' => [AdvisoryCrudController::class]; + yield 'DetectionResult' => [DetectionResultCrudController::class]; + yield 'DockerImage' => [DockerImageCrudController::class]; + yield 'DockerImageTag' => [DockerImageTagCrudController::class]; + yield 'Domain' => [DomainCrudController::class]; + yield 'GitRepo' => [GitRepoCrudController::class]; + yield 'GitTag' => [GitTagCrudController::class]; + yield 'Installation' => [InstallationCrudController::class]; + yield 'Module' => [ModuleCrudController::class]; + yield 'ModuleVersion' => [ModuleVersionCrudController::class]; + yield 'OIDC' => [OIDCCrudController::class]; + yield 'Package' => [PackageCrudController::class]; + yield 'PackageVersion' => [PackageVersionCrudController::class]; + yield 'Server' => [ServerCrudController::class]; + yield 'ServiceCertificate' => [ServiceCertificateCrudController::class]; + yield 'Site' => [SiteCrudController::class]; + } +} diff --git a/tests/MessageHandler/ProcessDetectionResultHandlerTest.php b/tests/MessageHandler/ProcessDetectionResultHandlerTest.php index 5de44b0..066fdda 100644 --- a/tests/MessageHandler/ProcessDetectionResultHandlerTest.php +++ b/tests/MessageHandler/ProcessDetectionResultHandlerTest.php @@ -28,9 +28,9 @@ protected function setUp(): void parent::setUp(); $this->detectionResultRepositoryMock = $this->createMock(DetectionResultRepository::class); - $this->entityManagerMock = $this->createMock(EntityManagerInterface::class); - $connectionMock = $this->createMock(Connection::class); - $this->entityManagerMock->expects($this->any())->method('getConnection')->willReturn($connectionMock); + $this->entityManagerMock = $this->createStub(EntityManagerInterface::class); + $connectionMock = $this->createStub(Connection::class); + $this->entityManagerMock->method('getConnection')->willReturn($connectionMock); $this->dirResultHandler = $this->createMock(DetectionResultHandlerInterface::class); $this->nginxResultHandler = $this->createMock(DetectionResultHandlerInterface::class); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8e0530d..bc44ee1 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -7,7 +7,7 @@ require dirname(__DIR__).'/vendor/autoload.php'; if (method_exists(Dotenv::class, 'bootEnv')) { - (new Dotenv())->bootEnv(dirname(__DIR__).'/.env'); + new Dotenv()->bootEnv(dirname(__DIR__).'/.env'); } if ($_SERVER['APP_DEBUG']) {