From 150c46636805713a1c8b66d166f6164a19ee85e8 Mon Sep 17 00:00:00 2001 From: Ismo Vuorinen Date: Sat, 7 Mar 2026 13:28:39 +0200 Subject: [PATCH] feat!: migrate to Valinor DTOs, add v3 YTJ API client, modernize tooling (#13) * feat!: moved v1 api under src/v1 BREAKING CHANGE: please update your namespaces if you use any of the classes directly or composer doesn't like the namespace change. * feat!: migrate from spatie/dto to valinor, add pest and rector BREAKING CHANGE: replaced spatie/data-transfer-object with cuyz/valinor, upgraded phpcs to v4, added pestphp/pest and rector/rector. Removed ivuorinen/markdowndocs due to symfony/console conflict. * feat: add shared HTTP AbstractClient with Valinor mapper Base class providing Guzzle HTTP client and Valinor TreeMapper with allowSuperfluousKeys() for PRH API response hydration. * refactor!: migrate v1 DTOs to final readonly with Valinor hydration BREAKING CHANGE: all v1 DTOs are now final readonly classes with constructor promotion. Traits are method-only (properties removed). HasVersion trait deleted. BusinessDataFetcher extends AbstractClient. * feat: add v3 YTJ API client with DTOs New client for PRH opendata-ytj-api v3 with full DTO coverage matching the v3 OpenAPI schema. * test: add Pest test suite for v1 and v3 Unit tests for all v1 DTOs, traits, and BusinessDataFetcher client. Unit tests for v3 Client and Company DTO hydration. Includes JSON fixtures for realistic response testing. * chore: add Rector config with deadCode, codeQuality, typeDeclarations * ci: update workflows to use ivuorinen/actions, pin SHAs, add PHP 8.4 Migrated from ivuorinen/.github reusable workflows to ivuorinen/actions composite actions with pinned commit SHAs. Added PHP 8.4 to test matrix. Updated renovate config preset path. Added labels.yml for sync-labels workflow. * chore: clean up .gitignore for PHP-only project Removed irrelevant entries for Node.js, Next.js, Nuxt, Laravel, Vagrant, Android/Crashlytics, CMake, and other unused ecosystems. * docs: update README, add CHANGELOG, CONTRIBUTING, and CLAUDE.md Rewrote README with v1/v3 usage examples and badges. Added CHANGELOG, CONTRIBUTING guide, and CLAUDE.md project instructions. Added .claude/ settings for Claude Code integration. * chore(deps): update composer.lock * chore: add CaptainHook with conventional commits and secrets detection Added captainhook/captainhook, captainhook/hook-installer, captainhook/secrets, and ramsey/conventional-commits. Hooks configured: - pre-commit: secrets check, lint-fix, test - commit-msg: conventional commit validation - post-change: composer install on lock/json changes * fix(deps): regenerate composer.lock with PHP 8.2 compatibility Downgraded symfony packages from v8 to v7.4 to support the full PHP 8.2/8.3/8.4 test matrix. * fix: address PR #13 code review feedback - Pin captainhook versions (^5.0, ^1.0) instead of wildcard - Add ext-json to composer.json require block - Fix v3 Client base URI to avoid duplicate path prefix - Handle null language in HasLanguage trait - Remove empty-string defaults from structural DTO fields - Remove branch-specific section from CLAUDE.md - Fix vendor path deny pattern in .claude/settings.json - Use phpcbf directly in post-edit lint hook - Add null register test case - Cast json_encode in ClientTest for type safety * chore(deps): regenerate composer.lock with PHP 8.2 * fix: address PR #13 code review feedback (round 2) - Downgrade stale.yml permissions from contents:write to contents:read - Remove nullable arrays in BisCompanyDetails (PRH API always returns arrays) - Mark 3 additional breaking changes in CHANGELOG.md - Extract API_PREFIX constant in v3 Client to reduce path duplication * fix(ci): resolve merge conflict markers in composer workflow * fix(ci): clean up pr-lint workflow * chore: add MegaLinter configuration * docs: add PHPDoc to HTTP layer classes * docs: add PHPDoc to v1 DTOs, traits, and exceptions * docs: add PHPDoc to v3 DTOs and exceptions * fix(ci): use Composer download cache instead of vendor cache Caching vendor/ can inject stale binaries that pass unexpected arguments (e.g. --cache-directory) to Pest. Switch to caching ~/.composer/cache which only stores download archives. * fix(ci): add permissions to PR lint job MegaLinter needs write access to issues, pull-requests, and statuses to post results. Add explicit permissions block to the lint job. * fix: add source directory to phpcs.xml MegaLinter runs phpcs in project mode which relies on phpcs.xml to know which directories to scan. Add src so it finds code. * fix(deps): add SARIF and phpstan extension-installer packages Add phpstan/extension-installer, jbelien/phpstan-sarif-formatter, and bartlett/sarif-php-converters so MegaLinter can produce SARIF output from phpstan. * fix(ci): disable JSON_PRETTIER and configure markdownlint - Disable JSON_PRETTIER linter in MegaLinter (not needed) - Add .markdownlint.json disabling MD041 (first-line-heading false positive on YAML frontmatter) - Wrap bare URLs in README.md with angle brackets (MD034) * fix: add phpunit.xml.dist with cacheDirectory Prevents Pest's Cache plugin from injecting --cache-directory argument in CI, which caused PHPUnit to misparse it as a config file. * fix(ci): fix MegaLinter config Remove JSON_PRETTIER and PHP_PHPSTAN from ENABLE_LINTERS to resolve conflicts with DISABLE_LINTERS and missing SARIF extension. Add .claude/ to FILTER_REGEX_EXCLUDE. * fix: resolve markdownlint errors Disable MD013 (line length), add blank lines around headings/lists in CHANGELOG.md, fix table separator spacing in README.md. * fix: harden v3 Client error handling and URL encoding - Make BusinessLine::$code required (no empty-string default) - URL-encode businessId in v1 client to prevent path injection - Catch \JsonException alongside RequestException in v3 Client searchCompanies() and getPostCodes() since getJson() can throw it * fix: pin dependency versions and enable workflow YAML linting - Pin phpstan/extension-installer, jbelien/phpstan-sarif-formatter, and bartlett/sarif-php-converters to ^1.0 instead of wildcard - Remove .github/ from MegaLinter FILTER_REGEX_EXCLUDE so YAML linters can check workflow files * fix: resolve yamllint errors in GitHub YAML files - Add missing document start marker to labels.yml - Fix step indentation in composer.yml workflow * fix: resolve markdownlint errors and disable YAML_PRETTIER * chore: add yamllint configuration --- .claude/settings.json | 31 + .claude/skills/check-quality/SKILL.md | 13 + .claude/skills/gen-test/SKILL.md | 16 + .claude/skills/new-dto/SKILL.md | 15 + .github/labels.yml | 28 + .github/renovate.json | 2 +- .github/workflows/composer.yml | 47 +- .github/workflows/pr-lint.yml | 17 +- .github/workflows/release-drafter.yml | 14 +- .github/workflows/stale.yml | 13 +- .github/workflows/sync-labels.yml | 8 +- .gitignore | 124 +- .markdownlint.json | 4 + .markdownlintignore | 1 + .mega-linter.yml | 26 + .yamllint.yml | 20 + CHANGELOG.md | 40 + CLAUDE.md | 45 + CONTRIBUTING.md | 47 + README.md | 111 +- captainhook.json | 69 + composer.json | 42 +- composer.lock | 4580 ++++++++++++++++- example.php | 3 +- gen/parser.php | 10 +- phpcs.xml | 1 + phpunit.xml.dist | 18 + rector.php | 16 + src/BusinessDataFetcher.php | 53 +- src/Dto/BisAddress.php | 56 - src/Dto/BisCompanyBusinessIdChange.php | 41 - src/Dto/BisCompanyBusinessLine.php | 36 - src/Dto/BisCompanyContactDetail.php | 36 - src/Dto/BisCompanyDetails.php | 115 - src/Dto/BisCompanyForm.php | 36 - src/Dto/BisCompanyLanguage.php | 31 - src/Dto/BisCompanyLiquidation.php | 36 - src/Dto/BisCompanyName.php | 36 - src/Dto/BisCompanyRegisteredEntry.php | 36 - src/Dto/BisCompanyRegisteredOffice.php | 36 - src/Exceptions/ApiResponseErrorException.php | 9 - src/Exceptions/UnexpectedValueException.php | 9 - src/Http/AbstractClient.php | 73 + src/Http/HttpClientFactory.php | 18 + src/Traits/HasLanguage.php | 26 - src/Traits/HasSource.php | 34 - src/Traits/HasVersion.php | 11 - src/v1/Dto/BisAddress.php | 27 + src/v1/Dto/BisCompanyBusinessIdChange.php | 25 + src/v1/Dto/BisCompanyBusinessLine.php | 23 + src/v1/Dto/BisCompanyContactDetail.php | 23 + src/v1/Dto/BisCompanyDetails.php | 40 + src/v1/Dto/BisCompanyForm.php | 23 + src/v1/Dto/BisCompanyLanguage.php | 22 + src/v1/Dto/BisCompanyLiquidation.php | 23 + src/v1/Dto/BisCompanyName.php | 23 + src/v1/Dto/BisCompanyRegisteredEntry.php | 24 + src/v1/Dto/BisCompanyRegisteredOffice.php | 23 + .../Exceptions/ApiResponseErrorException.php | 10 + .../Exceptions/UnexpectedValueException.php | 10 + src/{ => v1}/Traits/HasAuthority.php | 13 +- src/{ => v1}/Traits/HasChange.php | 14 +- src/v1/Traits/HasLanguage.php | 22 + src/{ => v1}/Traits/HasRegister.php | 13 +- src/v1/Traits/HasSource.php | 19 + src/v3/Client.php | 124 + src/v3/Dto/Address.php | 28 + src/v3/Dto/BusinessId.php | 14 + src/v3/Dto/BusinessLine.php | 19 + src/v3/Dto/Company.php | 32 + src/v3/Dto/CompanyForm.php | 20 + src/v3/Dto/CompanySearchResult.php | 16 + src/v3/Dto/CompanySituation.php | 15 + src/v3/Dto/DescriptionEntry.php | 13 + src/v3/Dto/EuId.php | 13 + src/v3/Dto/PostCodeEntry.php | 16 + src/v3/Dto/PostOffice.php | 14 + src/v3/Dto/RegisterName.php | 17 + src/v3/Dto/RegisteredEntry.php | 20 + src/v3/Dto/Website.php | 14 + src/v3/Exceptions/V3ApiException.php | 10 + tests/Fixtures/sample-response.json | 112 + tests/Fixtures/v3-search-response.json | 101 + tests/Pest.php | 9 + tests/Unit/BusinessDataFetcherTest.php | 52 + tests/Unit/v1/Dto/BisCompanyDetailsTest.php | 43 + tests/Unit/v1/Dto/DtoHydrationTest.php | 87 + tests/Unit/v1/Traits/HasAuthorityTest.php | 30 + tests/Unit/v1/Traits/HasChangeTest.php | 41 + tests/Unit/v1/Traits/HasLanguageTest.php | 30 + tests/Unit/v1/Traits/HasRegisterTest.php | 35 + tests/Unit/v1/Traits/HasSourceTest.php | 34 + tests/Unit/v3/ClientTest.php | 73 + tests/Unit/v3/Dto/CompanyTest.php | 53 + 94 files changed, 6602 insertions(+), 949 deletions(-) create mode 100644 .claude/settings.json create mode 100644 .claude/skills/check-quality/SKILL.md create mode 100644 .claude/skills/gen-test/SKILL.md create mode 100644 .claude/skills/new-dto/SKILL.md create mode 100644 .github/labels.yml create mode 100644 .markdownlint.json create mode 100644 .markdownlintignore create mode 100644 .mega-linter.yml create mode 100644 .yamllint.yml create mode 100644 CHANGELOG.md create mode 100644 CLAUDE.md create mode 100644 CONTRIBUTING.md create mode 100644 captainhook.json create mode 100644 phpunit.xml.dist create mode 100644 rector.php delete mode 100644 src/Dto/BisAddress.php delete mode 100644 src/Dto/BisCompanyBusinessIdChange.php delete mode 100644 src/Dto/BisCompanyBusinessLine.php delete mode 100644 src/Dto/BisCompanyContactDetail.php delete mode 100644 src/Dto/BisCompanyDetails.php delete mode 100644 src/Dto/BisCompanyForm.php delete mode 100644 src/Dto/BisCompanyLanguage.php delete mode 100644 src/Dto/BisCompanyLiquidation.php delete mode 100644 src/Dto/BisCompanyName.php delete mode 100644 src/Dto/BisCompanyRegisteredEntry.php delete mode 100644 src/Dto/BisCompanyRegisteredOffice.php delete mode 100644 src/Exceptions/ApiResponseErrorException.php delete mode 100644 src/Exceptions/UnexpectedValueException.php create mode 100644 src/Http/AbstractClient.php create mode 100644 src/Http/HttpClientFactory.php delete mode 100644 src/Traits/HasLanguage.php delete mode 100644 src/Traits/HasSource.php delete mode 100644 src/Traits/HasVersion.php create mode 100644 src/v1/Dto/BisAddress.php create mode 100644 src/v1/Dto/BisCompanyBusinessIdChange.php create mode 100644 src/v1/Dto/BisCompanyBusinessLine.php create mode 100644 src/v1/Dto/BisCompanyContactDetail.php create mode 100644 src/v1/Dto/BisCompanyDetails.php create mode 100644 src/v1/Dto/BisCompanyForm.php create mode 100644 src/v1/Dto/BisCompanyLanguage.php create mode 100644 src/v1/Dto/BisCompanyLiquidation.php create mode 100644 src/v1/Dto/BisCompanyName.php create mode 100644 src/v1/Dto/BisCompanyRegisteredEntry.php create mode 100644 src/v1/Dto/BisCompanyRegisteredOffice.php create mode 100644 src/v1/Exceptions/ApiResponseErrorException.php create mode 100644 src/v1/Exceptions/UnexpectedValueException.php rename src/{ => v1}/Traits/HasAuthority.php (58%) rename src/{ => v1}/Traits/HasChange.php (63%) create mode 100644 src/v1/Traits/HasLanguage.php rename src/{ => v1}/Traits/HasRegister.php (68%) create mode 100644 src/v1/Traits/HasSource.php create mode 100644 src/v3/Client.php create mode 100644 src/v3/Dto/Address.php create mode 100644 src/v3/Dto/BusinessId.php create mode 100644 src/v3/Dto/BusinessLine.php create mode 100644 src/v3/Dto/Company.php create mode 100644 src/v3/Dto/CompanyForm.php create mode 100644 src/v3/Dto/CompanySearchResult.php create mode 100644 src/v3/Dto/CompanySituation.php create mode 100644 src/v3/Dto/DescriptionEntry.php create mode 100644 src/v3/Dto/EuId.php create mode 100644 src/v3/Dto/PostCodeEntry.php create mode 100644 src/v3/Dto/PostOffice.php create mode 100644 src/v3/Dto/RegisterName.php create mode 100644 src/v3/Dto/RegisteredEntry.php create mode 100644 src/v3/Dto/Website.php create mode 100644 src/v3/Exceptions/V3ApiException.php create mode 100644 tests/Fixtures/sample-response.json create mode 100644 tests/Fixtures/v3-search-response.json create mode 100644 tests/Pest.php create mode 100644 tests/Unit/BusinessDataFetcherTest.php create mode 100644 tests/Unit/v1/Dto/BisCompanyDetailsTest.php create mode 100644 tests/Unit/v1/Dto/DtoHydrationTest.php create mode 100644 tests/Unit/v1/Traits/HasAuthorityTest.php create mode 100644 tests/Unit/v1/Traits/HasChangeTest.php create mode 100644 tests/Unit/v1/Traits/HasLanguageTest.php create mode 100644 tests/Unit/v1/Traits/HasRegisterTest.php create mode 100644 tests/Unit/v1/Traits/HasSourceTest.php create mode 100644 tests/Unit/v3/ClientTest.php create mode 100644 tests/Unit/v3/Dto/CompanyTest.php diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..e8b80f1 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,31 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "bash -c 'input=$(cat); fp=$(echo \"$input\" | jq -r \".tool_input.file_path\"); if [[ \"$fp\" == *composer.lock ]] || [[ \"$fp\" == vendor/* ]] || [[ \"$fp\" == */vendor/* ]]; then echo \"{\\\"decision\\\":\\\"deny\\\",\\\"reason\\\":\\\"Do not edit $fp directly\\\"}\" >&2; exit 2; fi'" + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "bash -c 'input=$(cat); fp=$(echo \"$input\" | jq -r \".tool_input.file_path\"); if [[ \"$fp\" == *.php ]]; then cd \"$CLAUDE_PROJECT_DIR\" && php vendor/bin/phpcbf --standard=PSR12 \"$fp\" 2>/dev/null; fi; true'" + }, + { + "type": "command", + "command": "bash -c 'input=$(cat); fp=$(echo \"$input\" | jq -r \".tool_input.file_path\"); if [[ \"$fp\" == *.php ]]; then cd \"$CLAUDE_PROJECT_DIR\" && composer phpstan 2>&1 | tail -5; fi; true'", + "timeout": 30 + } + ] + } + ] + } +} diff --git a/.claude/skills/check-quality/SKILL.md b/.claude/skills/check-quality/SKILL.md new file mode 100644 index 0000000..4fa1b9d --- /dev/null +++ b/.claude/skills/check-quality/SKILL.md @@ -0,0 +1,13 @@ +--- +name: check-quality +description: Run lint, phpstan, and tests in sequence, fix any issues found +disable-model-invocation: true +--- + +Run the full quality check pipeline: +1. `composer lint` — fix any PSR-12 violations with `composer lint-fix` +2. `composer phpstan` — fix any static analysis issues +3. `composer test` — fix any failing tests + +After each step, if issues are found, fix them before proceeding to the next step. +Do not commit. Report results when done. diff --git a/.claude/skills/gen-test/SKILL.md b/.claude/skills/gen-test/SKILL.md new file mode 100644 index 0000000..8724eba --- /dev/null +++ b/.claude/skills/gen-test/SKILL.md @@ -0,0 +1,16 @@ +--- +name: gen-test +description: Generate a Pest test for a PHP class following project conventions +disable-model-invocation: true +--- + +Generate a Pest test file for the specified class. + +Rules: +- Place tests in `tests/Unit/` mirroring the src/ structure +- Use Pest syntax (test(), expect(), describe()) +- For DTOs: test construction with valid data, verify all readonly properties +- For traits: test each method the trait provides +- For clients: mock Guzzle responses, test Valinor hydration +- Follow existing test patterns in tests/Unit/ +- Read existing tests first to match style diff --git a/.claude/skills/new-dto/SKILL.md b/.claude/skills/new-dto/SKILL.md new file mode 100644 index 0000000..3e4e1ae --- /dev/null +++ b/.claude/skills/new-dto/SKILL.md @@ -0,0 +1,15 @@ +--- +name: new-dto +description: Create a new final readonly DTO class with Valinor-compatible constructor +disable-model-invocation: true +--- + +Create a new DTO class following project conventions: +- `final readonly class` with constructor promotion +- Place in `src/v1/Dto/` or `src/v3/Dto/` based on API version +- All properties as constructor-promoted readonly parameters +- Use appropriate PHP types (string, int, ?string for nullable) +- Apply relevant traits (HasSource, HasLanguage, etc.) if applicable +- Namespace: `Ivuorinen\BusinessDataFetcher\{v1|v3}\Dto` +- Generate a corresponding Pest test in tests/Unit/ +- Read existing DTOs first to match style diff --git a/.github/labels.yml b/.github/labels.yml new file mode 100644 index 0000000..5a2d3ba --- /dev/null +++ b/.github/labels.yml @@ -0,0 +1,28 @@ +--- +- name: bug + color: d73a4a + description: Something isn't working + +- name: enhancement + color: a2eeef + description: New feature or request + +- name: documentation + color: 0075ca + description: Improvements or additions to documentation + +- name: dependencies + color: 0366d6 + description: Pull requests that update a dependency file + +- name: breaking-change + color: e11d48 + description: Breaking change to public API + +- name: maintenance + color: fbca04 + description: Code maintenance and refactoring + +- name: ci + color: 7057ff + description: Continuous integration and tooling diff --git a/.github/renovate.json b/.github/renovate.json index 5b9cda3..d1ad7f8 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,4 +1,4 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["github>ivuorinen/.github:renovate-config"] + "extends": ["github>ivuorinen/renovate-config"] } diff --git a/.github/workflows/composer.yml b/.github/workflows/composer.yml index 33c30a8..f0482da 100644 --- a/.github/workflows/composer.yml +++ b/.github/workflows/composer.yml @@ -18,33 +18,36 @@ jobs: strategy: matrix: - php-versions: ['8.2', '8.3'] + php-versions: ['8.2', '8.3', '8.4'] steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2 - with: - php-version: ${{ matrix.php-versions }} + - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # 2.36.0 + with: + php-version: ${{ matrix.php-versions }} - - name: Validate composer.json and composer.lock - run: composer validate --strict + - name: Validate composer.json and composer.lock + run: composer validate --strict - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5 - with: - path: vendor - key: ${{ runner.os }}-php-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php-${{ matrix.php-versions }}- - ${{ runner.os }}-php- + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + with: + path: ~/.composer/cache + key: ${{ runner.os }}-composer-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-${{ matrix.php-versions }}- + ${{ runner.os }}-composer- - - name: Install dependencies - run: composer install --prefer-dist --no-progress + - name: Install dependencies + run: composer install --prefer-dist --no-progress - # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" - # Docs: https://getcomposer.org/doc/articles/scripts.md + - name: Run linting + run: composer lint - # - name: Run test suite - # run: composer run-script test + - name: Run static analysis + run: composer phpstan + + - name: Run test suite + run: composer test diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml index 36b989c..7220ab5 100644 --- a/.github/workflows/pr-lint.yml +++ b/.github/workflows/pr-lint.yml @@ -2,12 +2,19 @@ name: PR Lint on: - push: - branches-ignore: [master, main] - # Remove the line above to run when pushing to master pull_request: branches: [master, main] jobs: - SuperLinter: - uses: ivuorinen/.github/.github/workflows/pr-lint.yml@main + lint: + permissions: + contents: read + issues: write + pull-requests: write + statuses: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: ivuorinen/actions/pr-lint@d1af04260d903f572ee953cc790ff7c1410709a6 # v2026.03.05 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 3b3945d..520770e 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -1,10 +1,18 @@ --- -name: Release Drafter +name: Release Monthly # yamllint disable-line rule:truthy on: workflow_call: + workflow_dispatch: + +permissions: + contents: write jobs: - Draft: - uses: ivuorinen/.github/.github/workflows/sync-labels.yml@main + release: + runs-on: ubuntu-latest + steps: + - uses: ivuorinen/actions/release-monthly@d1af04260d903f572ee953cc790ff7c1410709a6 # v2026.03.05 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 2055b92..7057920 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -8,12 +8,15 @@ on: workflow_call: workflow_dispatch: +permissions: + contents: read + issues: write + pull-requests: write + jobs: stale: runs-on: ubuntu-latest - permissions: - contents: write # only for delete-branch option - issues: write - pull-requests: write steps: - - uses: ivuorinen/actions/stale@main + - uses: ivuorinen/actions/stale@d1af04260d903f572ee953cc790ff7c1410709a6 # v2026.03.05 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml index f8dbfba..507f870 100644 --- a/.github/workflows/sync-labels.yml +++ b/.github/workflows/sync-labels.yml @@ -18,4 +18,10 @@ permissions: jobs: SyncLabels: - uses: ivuorinen/.github/.github/workflows/sync-labels.yml@main + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: ivuorinen/actions/sync-labels@d1af04260d903f572ee953cc790ff7c1410709a6 # v2026.03.05 + with: + labels: .github/labels.yml + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index ca05913..35a752d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,90 +1,49 @@ -.php-cs-fixer.cache -.php-cs-fixer.php +# PHP / Composer composer.phar /vendor/ + +# PHP CS Fixer +.php-cs-fixer.cache +.php-cs-fixer.php + +# PHPUnit / Pest .phpunit.result.cache .phpunit.cache -/app/phpunit.xml /phpunit.xml + +# Build artifacts /build/ -logs + +# Logs *.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json -pids -*.pid -*.seed -*.pid.lock -lib-cov -coverage -*.lcov -.nyc_output -.grunt -bower_components -.lock-wscript -build/Release -node_modules/ -jspm_packages/ -web_modules/ -*.tsbuildinfo -.npm -.eslintcache -.stylelintcache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ -.node_repl_history -*.tgz -.yarn-integrity + +# Environment .env -.env.development.local -.env.test.local -.env.production.local -.env.local -.cache -.parcel-cache -.next -out -.nuxt -dist -.cache/ -.vuepress/dist -.temp -.docusaurus -.serverless/ -.fusebox/ -.dynamodb/ -.tern-port -.vscode-test -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* +.env*.local + +# OS / Editor +.DS_Store [._]*.s[a-v][a-z] -!*.svg # comment out if you don't need vector files +!*.svg [._]*.sw[a-p] [._]s[a-rt-v][a-z] [._]ss[a-gi-z] [._]sw[a-p] +*~ +[._]*.un~ + +# Vim Session.vim Sessionx.vim .netrwhist -*~ tags -[._]*.un~ + +# JetBrains (PHPStorm) .idea/**/workspace.xml .idea/**/tasks.xml .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf -.idea/**/aws.xml -.idea/**/contentModel.xml .idea/**/dataSources/ .idea/**/dataSources.ids .idea/**/dataSources.local.xml @@ -92,44 +51,13 @@ tags .idea/**/dynamic.xml .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml -.idea/**/gradle.xml .idea/**/libraries -cmake-build-*/ -.idea/**/mongoSettings.xml *.iws -out/ .idea_modules/ -atlassian-ide-plugin.xml .idea/replstate.xml .idea/sonarlint/ -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties .idea/httpRequests .idea/caches/build_file_checksums.ser -npm-debug.log -yarn-error.log -bootstrap/compiled.php -app/storage/ -public/storage -public/hot -public_html/storage -public_html/hot -storage/*.key -Homestead.yaml -Homestead.json -/.vagrant -/node_modules -/.pnp -.pnp.js -/coverage -/.next/ -/out/ -/build -.DS_Store -*.pem -.env*.local -.vercel -next-env.d.ts +# Claude Code +.claude/settings.local.json diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..cd07f00 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,4 @@ +{ + "MD013": false, + "MD041": false +} diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 0000000..9038976 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1 @@ +docs.md diff --git a/.mega-linter.yml b/.mega-linter.yml new file mode 100644 index 0000000..41eea22 --- /dev/null +++ b/.mega-linter.yml @@ -0,0 +1,26 @@ +--- +APPLY_FIXES: all +PARALLEL: true +VALIDATE_ALL_CODEBASE: true +GITHUB_STATUS_REPORTER: true +SARIF_REPORTER: true +IGNORE_GENERATED_FILES: true +PRINT_ALPACA: false +SHOW_SKIPPED_LINTERS: false +SHOW_ELAPSED_TIME: false +FILEIO_REPORTER: false + +ENABLE_LINTERS: + - YAML_YAMLLINT + - MARKDOWN_MARKDOWNLINT + - PHP_PHPCS + +DISABLE_LINTERS: + - REPOSITORY_DEVSKIM + - JSON_PRETTIER + - YAML_PRETTIER + +PHP_PHPCS_CLI_LINT_MODE: project +PHP_PHPCS_ARGUMENTS: "--warning-severity=0" + +FILTER_REGEX_EXCLUDE: "(vendor/|node_modules/|\\.git/|\\.claude/|composer\\.lock|package-lock\\.json)" diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 0000000..0fb3c03 --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,20 @@ +--- +extends: default + +rules: + comments: + require-starting-space: true + ignore-shebangs: true + min-spaces-from-content: 1 + line-length: + max: 120 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: false + truthy: + check-keys: false + brackets: + min-spaces-inside: 0 + max-spaces-inside: 1 + +ignore: | + vendor/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a458fe4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,40 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- v3 API client (`Ivuorinen\BusinessDataFetcher\v3\Client`) with support for: + - Company search (`searchCompanies()`) + - Code list descriptions (`getDescription()`) + - Postal code lookup (`getPostCodes()`) + - Full company ZIP download (`getAllCompanies()`) +- v3 DTOs based on the official OpenAPI schema +- Pest test suite with 57 tests covering traits, DTOs, and both API clients +- Shared HTTP infrastructure (`AbstractClient`, `HttpClientFactory`) +- PHP 8.4 added to CI matrix +- CI pipeline now runs linting, static analysis, and tests +- `phpunit.xml` configuration +- `.github/labels.yml` for label sync workflow + +### Changed + +- **BREAKING:** Replaced `spatie/data-transfer-object` with `cuyz/valinor` for DTO hydration +- **BREAKING:** All v1 DTOs are now `final readonly` classes with constructor promotion +- **BREAKING:** Removed `$sourceText` and `$authorityText` computed properties from traits; use `getSourceText()` and `getAuthorityString()` methods instead +- **BREAKING:** `BusinessDataFetcher` constructor now accepts an optional `?Client` parameter for testability +- Upgraded `squizlabs/php_codesniffer` from v3 to v4 +- **BREAKING:** `BusinessDataFetcher` now extends `AbstractClient` +- **BREAKING:** Traits are now method-only (no properties or constructors) +- **BREAKING:** Removed empty `HasVersion` trait (version is a regular constructor property) + +### Removed + +- `spatie/data-transfer-object` dependency +- `ivuorinen/markdowndocs` dependency (incompatible with Pest's symfony/console requirement) +- `composer docs` script (pending markdowndocs update) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d67178e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,45 @@ +# CLAUDE.md + +## Project Overview + +PHP library for fetching Finnish business data from the PRH (Finnish Patent and Registration Office) open data API. Supports both v1 (BIS) and v3 (YTJ) APIs. + +## Commands + +```bash +composer lint # PSR-12 linting (phpcs v4) + Rector dry-run +composer lint-fix # Auto-fix PSR-12 violations (phpcbf) + Rector apply +composer rector # Run Rector standalone +composer phpstan # Static analysis (level 9) +composer test # Pest test suite +``` + +## Architecture + +- **v1 Entry point**: `src/BusinessDataFetcher.php` — extends `AbstractClient`, fetches from `/bis/v1` +- **v3 Entry point**: `src/v3/Client.php` — extends `AbstractClient`, fetches from `/opendata-ytj-api/v3` +- **Shared HTTP**: `src/Http/AbstractClient.php` — base class with Guzzle + Valinor mapper +- **v1 DTOs**: `src/v1/Dto/` — `final readonly` classes with Valinor-compatible constructors +- **v3 DTOs**: `src/v3/Dto/` — `final readonly` classes matching the v3 OpenAPI schema +- **Traits**: `src/v1/Traits/` — method-only helpers (`HasSource`, `HasLanguage`, `HasRegister`, `HasAuthority`, `HasChange`) +- **Exceptions**: `src/v1/Exceptions/`, `src/v3/Exceptions/` +- **Tests**: `tests/Unit/` — Pest tests for traits, DTOs, and both clients + +## Conventions + +- PHP 8.2+ +- PSR-12 coding standard (squizlabs/php_codesniffer v4) +- Rector (`rector.php`): `deadCode`, `codeQuality`, `typeDeclarations` sets — no `codingStyle` (phpcs handles that) +- PHPStan level 9 +- Valinor v1 for DTO hydration with `allowSuperfluousKeys()` +- All DTOs are `final readonly` with constructor promotion +- Traits are method-only (no properties) +- Namespace: `Ivuorinen\BusinessDataFetcher` + +## CI/CD + +- GitHub Actions workflows in `.github/workflows/` +- Reusable composite actions from `ivuorinen/actions` (not `ivuorinen/.github`) +- Renovate config: `.github/renovate.json` extends `github>ivuorinen/renovate-config` +- `pinact run -u -v --fix` — pin all action refs to commit SHAs after editing workflows +- PHP matrix: 8.2, 8.3, 8.4 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4082e06 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,47 @@ +# Contributing + +## Requirements + +- PHP 8.2+ +- Composer + +## Setup + +```bash +git clone https://github.com/ivuorinen/business-data-fetcher.git +cd business-data-fetcher +composer install +``` + +## Development Workflow + +### Running checks + +```bash +composer lint # PSR-12 linting +composer lint-fix # Auto-fix PSR-12 violations +composer phpstan # Static analysis (level 9) +composer test # Pest test suite +``` + +### Branch Strategy + +- `main` — stable release branch +- `feat/*` — feature branches +- `fix/*` — bugfix branches + +### Pull Request Process + +1. Create a feature or fix branch from `main` +2. Make your changes +3. Ensure all checks pass: `composer lint && composer phpstan && composer test` +4. Open a PR against `main` +5. PRs require passing CI before merge + +## Code Conventions + +- PSR-12 coding standard +- PHPStan level 9 strict analysis +- All DTOs are `final readonly` classes with constructor promotion +- Use Valinor for hydration (not manual array mapping) +- Write Pest tests for new functionality diff --git a/README.md b/README.md index b16824a..8de3fb4 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,112 @@ # Business Data Fetcher -This is an API client to Finnish Patent and Registration -Office's (PRH) Business Information System (BIS) v1. +[![PHP Composer](https://github.com/ivuorinen/business-data-fetcher/actions/workflows/composer.yml/badge.svg)](https://github.com/ivuorinen/business-data-fetcher/actions/workflows/composer.yml) -Use it to get company data from the Business Information System by Business ID. +PHP library for fetching Finnish business data from the PRH (Finnish Patent and Registration Office) open data API. + +Supports both **v1** (BIS) and **v3** (YTJ) APIs. ## Installation ```bash -composer install ivuorinen/business-data-fetcher +composer require ivuorinen/business-data-fetcher ``` -## Usage example +## Usage + +### v1 API (BIS) + +Fetch company details by Business ID: ```php -getBusinessInformation('1639413-9'); - print_r($results); -} catch (\GuzzleHttp\Exception\GuzzleException $e) { - var_dump($e); +$client = new BusinessDataFetcher(); +$results = $client->getBusinessInformation('1639413-9'); + +foreach ($results as $company) { + echo $company->name . "\n"; + echo $company->businessId . "\n"; + + foreach ($company->names as $name) { + echo $name->name . ' (' . $name->getLanguageString() . ")\n"; + } } ``` -## Data source +### v3 API (YTJ) -All models are transcribed from PRH Open Data portal. You can find the examples -and models descriptions, among other details and live API query tool following -this link: https://avoindata.prh.fi/ytj_en.html +Search companies with multiple filters: + +```php +use Ivuorinen\BusinessDataFetcher\v3\Client; + +$client = new Client(); + +// Search by name +$result = $client->searchCompanies(name: 'Example'); +echo "Found {$result->totalResults} companies\n"; + +foreach ($result->companies as $company) { + echo $company->businessId->value . ' - '; + echo $company->names[0]->name . "\n"; +} + +// Search by Business ID +$result = $client->searchCompanies(businessId: '1639413-9'); + +// Search with multiple filters +$result = $client->searchCompanies( + location: 'Helsinki', + companyForm: 'OY', + mainBusinessLine: '62010', +); + +// Get code list descriptions +$description = $client->getDescription('REK', 'en'); + +// Get postal codes +$postCodes = $client->getPostCodes('fi'); + +// Download all companies as ZIP +$stream = $client->getAllCompanies(); +file_put_contents('companies.zip', $stream->getContents()); +``` + +## v1 vs v3 API Comparison + +| Feature | v1 (BIS) | v3 (YTJ) | +| --------- | ---------- | ---------- | +| Base URL | `/bis/v1` | `/opendata-ytj-api/v3` | +| Lookup | By Business ID only | Search by name, location, form, etc. | +| Company form | String code | Structured with descriptions | +| Addresses | Flat structure | Nested with PostOffice objects | +| Code lists | N/A | `/description` endpoint | +| Postal codes | N/A | `/post_codes` endpoint | +| Bulk download | N/A | `/all_companies` ZIP | + +## Migration from v1 DTOs + +If upgrading from the Spatie DTO version: + +- DTOs are now `final readonly` classes (no `->toArray()`) +- `$sourceText` / `$authorityText` properties removed; use `getSourceText()` / `getAuthorityString()` methods +- `HasVersion` trait removed; `$version` is a regular constructor property +- `BusinessDataFetcher` constructor accepts an optional Guzzle `Client` for testing + +## Development + +```bash +composer lint # PSR-12 linting +composer lint-fix # Auto-fix violations +composer phpstan # Static analysis (level 9) +composer test # Pest test suite +``` + +## Data Source + +- v1: +- v3: ## Notice of Liability @@ -40,4 +116,3 @@ of this library are providing this without compensation and cannot be held respo ## License [MIT licensed](LICENSE.md) - diff --git a/captainhook.json b/captainhook.json new file mode 100644 index 0000000..2e890a5 --- /dev/null +++ b/captainhook.json @@ -0,0 +1,69 @@ +{ + "pre-commit": { + "enabled": true, + "actions": [ + { + "action": "\\CaptainHook\\App\\Hook\\Diff\\Action\\BlockSecrets", + "options": {}, + "conditions": [] + }, + { + "action": "composer lint-fix", + "options": {}, + "conditions": [] + }, + { + "action": "composer test", + "options": {}, + "conditions": [] + } + ] + }, + "commit-msg": { + "enabled": true, + "actions": [ + { + "action": "\\Ramsey\\CaptainHook\\ValidateConventionalCommit", + "options": {}, + "conditions": [] + } + ] + }, + "pre-push": { + "enabled": false, + "actions": [] + }, + "post-commit": { + "enabled": false, + "actions": [] + }, + "post-merge": { + "enabled": false, + "actions": [] + }, + "post-checkout": { + "enabled": false, + "actions": [] + }, + "post-rewrite": { + "enabled": false, + "actions": [] + }, + "post-change": { + "enabled": true, + "actions": [ + { + "action": "composer install", + "options": {}, + "conditions": [ + { + "exec": "CaptainHook.FileChanged.Any", + "args": [ + ["composer.json", "composer.lock"] + ] + } + ] + } + ] + } +} diff --git a/composer.json b/composer.json index 30aad10..7e359ac 100644 --- a/composer.json +++ b/composer.json @@ -5,23 +5,51 @@ "license": "MIT", "require": { "php": "^8.2", + "ext-json": "*", "guzzlehttp/guzzle": "^7.4", - "spatie/data-transfer-object": "^3.9" + "cuyz/valinor": "^1.0" }, "autoload": { "psr-4": { "Ivuorinen\\BusinessDataFetcher\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "Ivuorinen\\BusinessDataFetcher\\Tests\\": "tests/" + } + }, "require-dev": { "squizlabs/php_codesniffer": "^4.0", - "ivuorinen/markdowndocs": "^4.0", - "phpstan/phpstan": "^2.0" + "phpstan/phpstan": "^2.0", + "pestphp/pest": "^3.0", + "rector/rector": "^2.3", + "captainhook/captainhook": "^5.0", + "captainhook/hook-installer": "^1.0", + "ramsey/conventional-commits": "^1.7", + "captainhook/secrets": "^0.9.7", + "phpstan/extension-installer": "^1.0", + "jbelien/phpstan-sarif-formatter": "^1.0", + "bartlett/sarif-php-converters": "^1.0" }, "scripts": { - "docs": "php vendor/bin/phpdoc-md generate src > docs.md", - "lint": "php vendor/bin/phpcs --standard=PSR12 src", - "lint-fix": "php vendor/bin/phpcbf --standard=PSR12 src", - "phpstan": "php vendor/bin/phpstan analyse" + "lint": [ + "php vendor/bin/phpcs --standard=PSR12 src", + "php vendor/bin/rector --dry-run" + ], + "lint-fix": [ + "php vendor/bin/phpcbf --standard=PSR12 src", + "php vendor/bin/rector" + ], + "rector": "php vendor/bin/rector", + "phpstan": "php vendor/bin/phpstan analyse", + "test": "php vendor/bin/pest" + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true, + "captainhook/hook-installer": true, + "phpstan/extension-installer": true + } } } diff --git a/composer.lock b/composer.lock index ae1e582..8a1b975 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,86 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "83075a3e4a40fbf6e58d914985fd4ed9", + "content-hash": "02814da9b1173972503f6c7cb41ab4c0", "packages": [ + { + "name": "cuyz/valinor", + "version": "1.17.0", + "source": { + "type": "git", + "url": "https://github.com/CuyZ/Valinor.git", + "reference": "9eb2802797e36f3af1fd6e13ff23e4e3d0058022" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/9eb2802797e36f3af1fd6e13ff23e4e3d0058022", + "reference": "9eb2802797e36f3af1fd6e13ff23e4e3d0058022", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "conflict": { + "phpstan/phpstan": "<1.0 || >= 3.0", + "vimeo/psalm": "<5.0 || >=7.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.4", + "infection/infection": "^0.29", + "marcocesarato/php-conventional-changelog": "^1.12", + "mikey179/vfsstream": "^1.6.10", + "phpbench/phpbench": "^1.3", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^10.5", + "rector/rector": "^2.0", + "vimeo/psalm": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "CuyZ\\Valinor\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Romain Canon", + "email": "romain.hydrocanon@gmail.com", + "homepage": "https://github.com/romm" + } + ], + "description": "Library that helps to map any input into a strongly-typed value object structure.", + "homepage": "https://github.com/CuyZ/Valinor", + "keywords": [ + "array", + "conversion", + "hydrator", + "json", + "mapper", + "mapping", + "object", + "tree", + "yaml" + ], + "support": { + "issues": "https://github.com/CuyZ/Valinor/issues", + "source": "https://github.com/CuyZ/Valinor/tree/1.17.0" + }, + "funding": [ + { + "url": "https://github.com/romm", + "type": "github" + } + ], + "time": "2025-06-20T06:40:38+00:00" + }, { "name": "guzzlehttp/guzzle", "version": "7.10.0", @@ -491,6 +569,57 @@ }, "time": "2023-04-04T09:54:51+00:00" }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, { "name": "ralouphie/getallheaders", "version": "3.0.3", @@ -535,70 +664,6 @@ }, "time": "2019-03-08T08:55:37+00:00" }, - { - "name": "spatie/data-transfer-object", - "version": "3.9.1", - "source": { - "type": "git", - "url": "https://github.com/spatie/data-transfer-object.git", - "reference": "1df0906c4e9e3aebd6c0506fd82c8b7d5548c1c8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/data-transfer-object/zipball/1df0906c4e9e3aebd6c0506fd82c8b7d5548c1c8", - "reference": "1df0906c4e9e3aebd6c0506fd82c8b7d5548c1c8", - "shasum": "" - }, - "require": { - "php": "^8.0" - }, - "require-dev": { - "illuminate/collections": "^8.36", - "jetbrains/phpstorm-attributes": "^1.0", - "larapack/dd": "^1.1", - "phpunit/phpunit": "^9.5.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Spatie\\DataTransferObject\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Brent Roose", - "email": "brent@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" - } - ], - "description": "Data transfer objects with batteries included", - "homepage": "https://github.com/spatie/data-transfer-object", - "keywords": [ - "data-transfer-object", - "spatie" - ], - "support": { - "issues": "https://github.com/spatie/data-transfer-object/issues", - "source": "https://github.com/spatie/data-transfer-object/tree/3.9.1" - }, - "funding": [ - { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" - }, - { - "url": "https://github.com/spatie", - "type": "github" - } - ], - "abandoned": "spatie/laravel-data", - "time": "2022-09-16T13:34:38+00:00" - }, { "name": "symfony/deprecation-contracts", "version": "v3.6.0", @@ -669,39 +734,48 @@ ], "packages-dev": [ { - "name": "ivuorinen/markdowndocs", - "version": "4.0.0", + "name": "bartlett/sarif-php-converters", + "version": "1.5.0", "source": { "type": "git", - "url": "https://github.com/ivuorinen/markdowndocs.git", - "reference": "d5a11dcb177eeb998077dc69c6e65750e6ebe7ef" + "url": "https://github.com/llaville/sarif-php-converters.git", + "reference": "6230197e4054d004afec4ece83c50d0e3d451bee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ivuorinen/markdowndocs/zipball/d5a11dcb177eeb998077dc69c6e65750e6ebe7ef", - "reference": "d5a11dcb177eeb998077dc69c6e65750e6ebe7ef", + "url": "https://api.github.com/repos/llaville/sarif-php-converters/zipball/6230197e4054d004afec4ece83c50d0e3d451bee", + "reference": "6230197e4054d004afec4ece83c50d0e3d451bee", "shasum": "" }, "require": { + "bartlett/sarif-php-sdk": "^2.2", + "composer-runtime-api": "^2.0", "ext-json": "*", - "php": "^8.2", - "symfony/console": "^5 || ^6" + "ext-pcre": "*", + "ext-spl": "*", + "php": "^8.2" }, "require-dev": { - "ergebnis/composer-normalize": "^2.23.1", - "friendsofphp/php-cs-fixer": "^3.6", - "phpunit/phpunit": "^10", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "^5" + "bamarni/composer-bin-plugin": "^1.8", + "jawira/case-converter": "^3.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "symfony/console": "^7.0 || ^8.0" + }, + "suggest": { + "ext-libxml": "If you use the Console Tool and checkstyle input format", + "ext-simplexml": "If you use the Console Tool and checkstyle input format" }, - "bin": [ - "bin/phpdoc-md" - ], "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false, + "target-directory": "vendor-bin" + } + }, "autoload": { - "psr-0": { - "PHPDocsMD": "src/" + "psr-4": { + "Bartlett\\Sarif\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -710,34 +784,2010 @@ ], "authors": [ { - "name": "Victor Jonsson", - "email": "kontakt@victorjonsson.se" - }, - { - "name": "Grav CMS", - "email": "hello@getgrav.org", - "homepage": "https://getgrav.org" - }, - { - "name": "Ismo Vuorinen", - "homepage": "https://github.com/ivuorinen" + "name": "Laurent Laville", + "homepage": "https://github.com/llaville", + "role": "Lead" } ], - "description": "Command line tool for generating markdown-formatted class documentation", - "homepage": "https://github.com/ivuorinen/PHP-Markdown-Documentation-Generator", + "description": "Allows PHP projects to print a static code analysis report in SARIF format", + "keywords": [ + "converter", + "sarif" + ], "support": { - "issues": "https://github.com/ivuorinen/markdowndocs/issues", - "source": "https://github.com/ivuorinen/markdowndocs/tree/4.0.0" + "issues": "https://github.com/llaville/sarif-php-converters/issues", + "source": "https://github.com/llaville/sarif-php-converters" }, - "time": "2024-08-15T01:08:58+00:00" + "time": "2026-01-31T08:28:22+00:00" + }, + { + "name": "bartlett/sarif-php-sdk", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/llaville/sarif-php-sdk.git", + "reference": "d7f0174df65378b7f382372cef1bd7a76166abdd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/llaville/sarif-php-sdk/zipball/d7f0174df65378b7f382372cef1bd7a76166abdd", + "reference": "d7f0174df65378b7f382372cef1bd7a76166abdd", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-spl": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8", + "php-parallel-lint/php-console-highlighter": "^1.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false, + "target-directory": "vendor-bin" + } + }, + "autoload": { + "psr-4": { + "Bartlett\\Sarif\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Laville", + "homepage": "https://github.com/llaville", + "role": "Lead" + } + ], + "description": "PHP library to create and manipulate SARIF logs", + "keywords": [ + "sarif" + ], + "support": { + "issues": "https://github.com/llaville/sarif-php-sdk/issues", + "source": "https://github.com/llaville/sarif-php-sdk" + }, + "time": "2026-02-03T06:31:31+00:00" + }, + { + "name": "brianium/paratest", + "version": "v7.8.5", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "9b324c8fc319cf9728b581c7a90e1c8f6361c5e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/9b324c8fc319cf9728b581c7a90e1c8f6361c5e5", + "reference": "9b324c8fc319cf9728b581c7a90e1c8f6361c5e5", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.3.0", + "jean85/pretty-package-versions": "^2.1.1", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^11.0.12", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-timer": "^7.0.1", + "phpunit/phpunit": "^11.5.46", + "sebastian/environment": "^7.2.1", + "symfony/console": "^6.4.22 || ^7.3.4 || ^8.0.3", + "symfony/process": "^6.4.20 || ^7.3.4 || ^8.0.3" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^2.1.33", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.11", + "phpstan/phpstan-strict-rules": "^2.0.7", + "squizlabs/php_codesniffer": "^3.13.5", + "symfony/filesystem": "^6.4.13 || ^7.3.2 || ^8.0.1" + }, + "bin": [ + "bin/paratest", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.8.5" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2026-01-08T08:02:38+00:00" + }, + { + "name": "captainhook/captainhook", + "version": "5.28.5", + "source": { + "type": "git", + "url": "https://github.com/captainhook-git/captainhook.git", + "reference": "2a7316bf4ba4c3b11b3544c063788622d3520ee1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/captainhook-git/captainhook/zipball/2a7316bf4ba4c3b11b3544c063788622d3520ee1", + "reference": "2a7316bf4ba4c3b11b3544c063788622d3520ee1", + "shasum": "" + }, + "require": { + "captainhook/secrets": "^0.9.4", + "ext-json": "*", + "ext-spl": "*", + "ext-xml": "*", + "php": ">=8.0", + "sebastianfeldmann/camino": "^0.9.2", + "sebastianfeldmann/cli": "^3.3", + "sebastianfeldmann/git": "^3.16.0", + "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0" + }, + "replace": { + "sebastianfeldmann/captainhook": "*" + }, + "require-dev": { + "composer/composer": "~1 || ^2.0", + "mikey179/vfsstream": "~1" + }, + "bin": [ + "bin/captainhook" + ], + "type": "library", + "extra": { + "captainhook": { + "config": "captainhook.json" + }, + "branch-alias": { + "dev-main": "6.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "CaptainHook\\App\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "PHP git hook manager", + "homepage": "https://php.captainhook.info/", + "keywords": [ + "commit-msg", + "git", + "hooks", + "post-merge", + "pre-commit", + "pre-push", + "prepare-commit-msg" + ], + "support": { + "issues": "https://github.com/captainhook-git/captainhook/issues", + "source": "https://github.com/captainhook-git/captainhook/tree/5.28.5" + }, + "funding": [ + { + "url": "https://github.com/sponsors/sebastianfeldmann", + "type": "github" + } + ], + "time": "2026-02-28T08:59:22+00:00" + }, + { + "name": "captainhook/hook-installer", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/captainhook-git/hook-installer.git", + "reference": "fb3c45f6204b08baba999f4ffc4ae707bf684e8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/captainhook-git/hook-installer/zipball/fb3c45f6204b08baba999f4ffc4ae707bf684e8b", + "reference": "fb3c45f6204b08baba999f4ffc4ae707bf684e8b", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1|^2.0", + "php": ">=8.0" + }, + "require-dev": { + "composer/composer": "*" + }, + "type": "composer-plugin", + "extra": { + "class": "CaptainHook\\HookInstaller\\ComposerPlugin" + }, + "autoload": { + "psr-4": { + "CaptainHook\\HookInstaller\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "Composer Plugin that makes everyone activate the CaptainHook git hooks locally", + "support": { + "issues": "https://github.com/captainhook-git/hook-installer/issues", + "source": "https://github.com/captainhook-git/hook-installer/tree/1.0.4" + }, + "time": "2025-04-08T07:12:26+00:00" + }, + { + "name": "captainhook/secrets", + "version": "0.9.7", + "source": { + "type": "git", + "url": "https://github.com/captainhook-git/secrets.git", + "reference": "d62c97f75f81ac98e22f1c282482bd35fa82f631" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/captainhook-git/secrets/zipball/d62c97f75f81ac98e22f1c282482bd35fa82f631", + "reference": "d62c97f75f81ac98e22f1c282482bd35fa82f631", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "CaptainHook\\Secrets\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "Utility classes to detect secrets", + "keywords": [ + "commit-msg", + "keys", + "passwords", + "post-merge", + "prepare-commit-msg", + "secrets", + "tokens" + ], + "support": { + "issues": "https://github.com/captainhook-git/secrets/issues", + "source": "https://github.com/captainhook-git/secrets/tree/0.9.7" + }, + "funding": [ + { + "url": "https://github.com/sponsors/sebastianfeldmann", + "type": "github" + } + ], + "time": "2025-04-08T07:10:48+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.6", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "jawira/case-converter", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/jawira/case-converter.git", + "reference": "de9956122568743a83e0fc7e2eaa92c1b0de3f18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jawira/case-converter/zipball/de9956122568743a83e0fc7e2eaa92c1b0de3f18", + "reference": "de9956122568743a83e0fc7e2eaa92c1b0de3f18", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=7.4" + }, + "require-dev": { + "behat/behat": "^3.0", + "phpstan/phpstan": "^v2", + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "pds/skeleton": "PHP Package Development Standards", + "phing/phing": "PHP Build Tool" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jawira\\CaseConverter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jawira Portugal", + "email": "dev@tugal.be" + } + ], + "description": "Convert strings between 13 naming conventions: Snake case, Camel case, Pascal case, Kebab case, Ada case, Train case, Cobol case, Macro case, Upper case, Lower case, Sentence case, Title case and Dot notation.", + "homepage": "https://jawira.github.io/case-converter/", + "keywords": [ + "Ada case", + "Cobol case", + "Macro case", + "Train case", + "camel case", + "dot notation", + "kebab case", + "lower case", + "pascal case", + "sentence case", + "snake case", + "title case", + "upper case" + ], + "support": { + "issues": "https://github.com/jawira/case-converter/issues", + "source": "https://github.com/jawira/case-converter/tree/v3.6.0" + }, + "time": "2025-06-13T21:12:55+00:00" + }, + { + "name": "jbelien/phpstan-sarif-formatter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/jbelien/phpstan-sarif-formatter.git", + "reference": "6a21162f610238f86647065b7c99c9c7af380a96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jbelien/phpstan-sarif-formatter/zipball/6a21162f610238f86647065b7c99c9c7af380a96", + "reference": "6a21162f610238f86647065b7c99c9c7af380a96", + "shasum": "" + }, + "require": { + "nette/utils": "^3.0 || ^4.0", + "php": "^8.1", + "phpstan/phpstan": "^1.9 || ^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStanSarifErrorFormatter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "SARIF formatter for PHPStan", + "support": { + "issues": "https://github.com/jbelien/phpstan-sarif-formatter/issues", + "source": "https://github.com/jbelien/phpstan-sarif-formatter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/jbelien", + "type": "github" + } + ], + "time": "2025-02-07T08:32:44+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + }, + "time": "2025-03-19T14:43:43+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nette/utils", + "version": "v4.1.3", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe", + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe", + "shasum": "" + }, + "require": { + "php": "8.2 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.5", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.1.3" + }, + "time": "2026-02-13T03:05:33+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + }, + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.9.1", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "a1ed3fa530fd60bc515f9303e8520fcb7d4bd935" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/a1ed3fa530fd60bc515f9303e8520fcb7d4bd935", + "reference": "a1ed3fa530fd60bc515f9303e8520fcb7d4bd935", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.4", + "nunomaduro/termwind": "^2.4.0", + "php": "^8.2.0", + "symfony/console": "^7.4.4 || ^8.0.4" + }, + "conflict": { + "laravel/framework": "<11.48.0 || >=14.0.0", + "phpunit/phpunit": "<11.5.50 || >=14.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.5", + "larastan/larastan": "^3.9.2", + "laravel/framework": "^11.48.0 || ^12.52.0", + "laravel/pint": "^1.27.1", + "orchestra/testbench-core": "^9.12.0 || ^10.9.0", + "pestphp/pest": "^3.8.5 || ^4.4.1 || ^5.0.0", + "sebastian/environment": "^7.2.1 || ^8.0.3 || ^9.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "dev", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2026-02-17T17:33:08+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.4.4 || ^8.0.4" + }, + "require-dev": { + "illuminate/console": "^11.47.0", + "laravel/pint": "^1.27.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.5 || ^8.0.4", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "It's like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2026-02-16T23:10:27+00:00" + }, + { + "name": "opis/json-schema", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/opis/json-schema.git", + "reference": "8458763e0dd0b6baa310e04f1829fc73da4e8c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opis/json-schema/zipball/8458763e0dd0b6baa310e04f1829fc73da4e8c8a", + "reference": "8458763e0dd0b6baa310e04f1829fc73da4e8c8a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "opis/string": "^2.1", + "opis/uri": "^1.0", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ext-bcmath": "*", + "ext-intl": "*", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Opis\\JsonSchema\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Sorin Sarca", + "email": "sarca_sorin@hotmail.com" + }, + { + "name": "Marius Sarca", + "email": "marius.sarca@gmail.com" + } + ], + "description": "Json Schema Validator for PHP", + "homepage": "https://opis.io/json-schema", + "keywords": [ + "json", + "json-schema", + "schema", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/opis/json-schema/issues", + "source": "https://github.com/opis/json-schema/tree/2.6.0" + }, + "time": "2025-10-17T12:46:48+00:00" + }, + { + "name": "opis/string", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/opis/string.git", + "reference": "3e4d2aaff518ac518530b89bb26ed40f4503635e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opis/string/zipball/3e4d2aaff518ac518530b89bb26ed40f4503635e", + "reference": "3e4d2aaff518ac518530b89bb26ed40f4503635e", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "ext-json": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Opis\\String\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Marius Sarca", + "email": "marius.sarca@gmail.com" + }, + { + "name": "Sorin Sarca", + "email": "sarca_sorin@hotmail.com" + } + ], + "description": "Multibyte strings as objects", + "homepage": "https://opis.io/string", + "keywords": [ + "multi-byte", + "opis", + "string", + "string manipulation", + "utf-8" + ], + "support": { + "issues": "https://github.com/opis/string/issues", + "source": "https://github.com/opis/string/tree/2.1.0" + }, + "time": "2025-10-17T12:38:41+00:00" + }, + { + "name": "opis/uri", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/opis/uri.git", + "reference": "0f3ca49ab1a5e4a6681c286e0b2cc081b93a7d5a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opis/uri/zipball/0f3ca49ab1a5e4a6681c286e0b2cc081b93a7d5a", + "reference": "0f3ca49ab1a5e4a6681c286e0b2cc081b93a7d5a", + "shasum": "" + }, + "require": { + "opis/string": "^2.0", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Opis\\Uri\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Marius Sarca", + "email": "marius.sarca@gmail.com" + }, + { + "name": "Sorin Sarca", + "email": "sarca_sorin@hotmail.com" + } + ], + "description": "Build, parse and validate URIs and URI-templates", + "homepage": "https://opis.io", + "keywords": [ + "URI Template", + "parse url", + "punycode", + "uri", + "uri components", + "url", + "validate uri" + ], + "support": { + "issues": "https://github.com/opis/uri/issues", + "source": "https://github.com/opis/uri/tree/1.1.0" + }, + "time": "2021-05-22T15:57:08+00:00" + }, + { + "name": "pestphp/pest", + "version": "v3.8.5", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "7796630eafcfd1c02660cecdde3bc6984fbf01f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/7796630eafcfd1c02660cecdde3bc6984fbf01f4", + "reference": "7796630eafcfd1c02660cecdde3bc6984fbf01f4", + "shasum": "" + }, + "require": { + "brianium/paratest": "^7.8.5", + "nunomaduro/collision": "^8.8.3", + "nunomaduro/termwind": "^2.3.3", + "pestphp/pest-plugin": "^3.0.0", + "pestphp/pest-plugin-arch": "^3.1.1", + "pestphp/pest-plugin-mutate": "^3.0.5", + "php": "^8.2.0", + "phpunit/phpunit": "^11.5.50" + }, + "conflict": { + "filp/whoops": "<2.16.0", + "phpunit/phpunit": ">11.5.50", + "sebastian/exporter": "<6.0.0", + "webmozart/assert": "<1.11.0" + }, + "require-dev": { + "pestphp/pest-dev-tools": "^3.4.0", + "pestphp/pest-plugin-type-coverage": "^3.6.1", + "symfony/process": "^7.4.4" + }, + "bin": [ + "bin/pest" + ], + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Mutate\\Plugins\\Mutate", + "Pest\\Plugins\\Configuration", + "Pest\\Plugins\\Bail", + "Pest\\Plugins\\Cache", + "Pest\\Plugins\\Coverage", + "Pest\\Plugins\\Init", + "Pest\\Plugins\\Environment", + "Pest\\Plugins\\Help", + "Pest\\Plugins\\Memory", + "Pest\\Plugins\\Only", + "Pest\\Plugins\\Printer", + "Pest\\Plugins\\ProcessIsolation", + "Pest\\Plugins\\Profile", + "Pest\\Plugins\\Retry", + "Pest\\Plugins\\Snapshot", + "Pest\\Plugins\\Verbose", + "Pest\\Plugins\\Version", + "Pest\\Plugins\\Parallel" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php", + "src/Pest.php" + ], + "psr-4": { + "Pest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "The elegant PHP Testing Framework.", + "keywords": [ + "framework", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/pestphp/pest/issues", + "source": "https://github.com/pestphp/pest/tree/v3.8.5" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2026-01-28T01:33:45+00:00" + }, + { + "name": "pestphp/pest-plugin", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "e79b26c65bc11c41093b10150c1341cc5cdbea83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e79b26c65bc11c41093b10150c1341cc5cdbea83", + "reference": "e79b26c65bc11c41093b10150c1341cc5cdbea83", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", + "php": "^8.2" + }, + "conflict": { + "pestphp/pest": "<3.0.0" + }, + "require-dev": { + "composer/composer": "^2.7.9", + "pestphp/pest": "^3.0.0", + "pestphp/pest-dev-tools": "^3.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Pest\\Plugin\\Manager" + }, + "autoload": { + "psr-4": { + "Pest\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest plugin manager", + "keywords": [ + "framework", + "manager", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin/tree/v3.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2024-09-08T23:21:41+00:00" + }, + { + "name": "pestphp/pest-plugin-arch", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-arch.git", + "reference": "db7bd9cb1612b223e16618d85475c6f63b9c8daa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/db7bd9cb1612b223e16618d85475c6f63b9c8daa", + "reference": "db7bd9cb1612b223e16618d85475c6f63b9c8daa", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^3.0.0", + "php": "^8.2", + "ta-tikoma/phpunit-architecture-test": "^0.8.4" + }, + "require-dev": { + "pestphp/pest": "^3.8.1", + "pestphp/pest-dev-tools": "^3.4.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Arch\\Plugin" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Arch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Arch plugin for Pest PHP.", + "keywords": [ + "arch", + "architecture", + "framework", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-04-16T22:59:48+00:00" + }, + { + "name": "pestphp/pest-plugin-mutate", + "version": "v3.0.5", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-mutate.git", + "reference": "e10dbdc98c9e2f3890095b4fe2144f63a5717e08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/e10dbdc98c9e2f3890095b4fe2144f63a5717e08", + "reference": "e10dbdc98c9e2f3890095b4fe2144f63a5717e08", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.2.0", + "pestphp/pest-plugin": "^3.0.0", + "php": "^8.2", + "psr/simple-cache": "^3.0.0" + }, + "require-dev": { + "pestphp/pest": "^3.0.8", + "pestphp/pest-dev-tools": "^3.0.0", + "pestphp/pest-plugin-type-coverage": "^3.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Pest\\Mutate\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" + } + ], + "description": "Mutates your code to find untested cases", + "keywords": [ + "framework", + "mutate", + "mutation", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v3.0.5" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-09-22T07:54:40+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "897b5986ece6b4f9d8413fea345c7d49c757d6bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/897b5986ece6b4f9d8413fea345c7d49c757d6bf", + "reference": "897b5986ece6b4f9d8413fea345c7d49c757d6bf", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^2.0", + "phpstan/phpdoc-parser": "^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26", + "shipmonk/dead-code-detector": "^0.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.2" + }, + "time": "2026-03-01T18:43:49+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + }, + "time": "2026-01-06T21:53:42+00:00" + }, + { + "name": "phpstan/extension-installer", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" + }, + "time": "2024-09-04T20:21:43+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" + }, + "time": "2026-01-25T14:56:51+00:00" }, { "name": "phpstan/phpstan", - "version": "2.1.39", + "version": "2.1.40", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", - "reference": "c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", + "reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", "shasum": "" }, "require": { @@ -782,7 +2832,463 @@ "type": "github" } ], - "time": "2026-02-11T14:48:56+00:00" + "time": "2026-02-23T15:04:35+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.12", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.7.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.1", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.3.1" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.46" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-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 that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "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/11.0.12" + }, + "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-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-12-24T07:01:01+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "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/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-02-02T13:52:54+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "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/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "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/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.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": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "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/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.50", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "fdfc727f0fcacfeb8fcb30c7e5da173125b58be3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fdfc727f0fcacfeb8fcb30c7e5da173125b58be3", + "reference": "fdfc727f0fcacfeb8fcb30c7e5da173125b58be3", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.12", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.3", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.50" + }, + "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" + } + ], + "time": "2026-01-27T05:59:18+00:00" }, { "name": "psr/container", @@ -837,6 +3343,1381 @@ }, "time": "2021-11-05T16:47:00+00:00" }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "ramsey/conventional-commits", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/conventional-commits.git", + "reference": "2d24a17b3505f03948c315ff55c14b03d6f8bc61" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/conventional-commits/zipball/2d24a17b3505f03948c315ff55c14b03d6f8bc61", + "reference": "2d24a17b3505f03948c315ff55c14b03d6f8bc61", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0", + "ext-json": "*", + "jawira/case-converter": "^3.5", + "opis/json-schema": "^2.3", + "php": "^8.1", + "symfony/console": "^6.0 || ^7.0 || ^8.0", + "symfony/filesystem": "^6.0 || ^7.0 || ^8.0" + }, + "require-dev": { + "captainhook/captainhook": "^5.15", + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.30", + "hamcrest/hamcrest-php": "^2.0", + "mockery/mockery": "^1.6.12", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5.63", + "ramsey/coding-standard": "^2.3", + "ramsey/composer-repl": "^1.5", + "ramsey/composer-repl-lib": "^1.4.0", + "roave/security-advisories": "dev-latest", + "sebastianfeldmann/cli": "^3.4", + "sebastianfeldmann/git": "^3.8", + "spatie/phpunit-snapshot-assertions": "^5.1", + "symfony/process": "^6.0 || ^7.0 || ^8.0" + }, + "suggest": { + "captainhook/captainhook": "Manage your project's Git hooks with CaptainHook, and use ramsey/conventional-commits in your commit-msg and prepare-commit-msg hooks." + }, + "bin": [ + "bin/conventional-commits" + ], + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "branch-alias": { + "dev-main": "1.x-dev" + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\CaptainHook\\": "src/CaptainHook/", + "Ramsey\\ConventionalCommits\\": "src/ConventionalCommits/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for creating and validating commit messages according to the Conventional Commits specification. Includes a CaptainHook action!", + "keywords": [ + "HOOK", + "captainhook", + "commit", + "commit-msg", + "conventional", + "conventional-commits", + "git", + "plugin" + ], + "support": { + "issues": "https://github.com/ramsey/conventional-commits/issues", + "source": "https://github.com/ramsey/conventional-commits/tree/1.7.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + } + ], + "time": "2026-03-04T06:55:21+00:00" + }, + { + "name": "rector/rector", + "version": "2.3.8", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "bbd37aedd8df749916cffa2a947cfc4714d1ba2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/bbd37aedd8df749916cffa2a947cfc4714d1ba2c", + "reference": "bbd37aedd8df749916cffa2a947cfc4714d1ba2c", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.38" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com/", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/2.3.8" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2026-02-22T09:45:50+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "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/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-19T07:56:08+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.3.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-01-24T09:26:40+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2025-05-21T11:55:47+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:12:51+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "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/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "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/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:42:22+00:00" + }, + { + "name": "sebastian/type", + "version": "5.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" + }, + "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/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:55:48+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "sebastianfeldmann/camino", + "version": "0.9.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianfeldmann/camino.git", + "reference": "bf2e4c8b2a029e9eade43666132b61331e3e8184" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianfeldmann/camino/zipball/bf2e4c8b2a029e9eade43666132b61331e3e8184", + "reference": "bf2e4c8b2a029e9eade43666132b61331e3e8184", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "SebastianFeldmann\\Camino\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "Path management the OO way", + "homepage": "https://github.com/sebastianfeldmann/camino", + "keywords": [ + "file system", + "path" + ], + "support": { + "issues": "https://github.com/sebastianfeldmann/camino/issues", + "source": "https://github.com/sebastianfeldmann/camino/tree/0.9.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianfeldmann", + "type": "github" + } + ], + "time": "2022-01-03T13:15:10+00:00" + }, + { + "name": "sebastianfeldmann/cli", + "version": "3.4.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianfeldmann/cli.git", + "reference": "6fa122afd528dae7d7ec988a604aa6c600f5d9b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianfeldmann/cli/zipball/6fa122afd528dae7d7ec988a604aa6c600f5d9b5", + "reference": "6fa122afd528dae7d7ec988a604aa6c600f5d9b5", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "symfony/process": "^4.3 | ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "SebastianFeldmann\\Cli\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "PHP cli helper classes", + "homepage": "https://github.com/sebastianfeldmann/cli", + "keywords": [ + "cli" + ], + "support": { + "issues": "https://github.com/sebastianfeldmann/cli/issues", + "source": "https://github.com/sebastianfeldmann/cli/tree/3.4.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianfeldmann", + "type": "github" + } + ], + "time": "2024-11-26T10:19:01+00:00" + }, + { + "name": "sebastianfeldmann/git", + "version": "3.16.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianfeldmann/git.git", + "reference": "40a5cc043f0957228767f639e370ec92590e940f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianfeldmann/git/zipball/40a5cc043f0957228767f639e370ec92590e940f", + "reference": "40a5cc043f0957228767f639e370ec92590e940f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "php": ">=8.0", + "sebastianfeldmann/cli": "^3.0" + }, + "require-dev": { + "mikey179/vfsstream": "^1.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "SebastianFeldmann\\Git\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "PHP git wrapper", + "homepage": "https://github.com/sebastianfeldmann/git", + "keywords": [ + "git" + ], + "support": { + "issues": "https://github.com/sebastianfeldmann/git/issues", + "source": "https://github.com/sebastianfeldmann/git/tree/3.16.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianfeldmann", + "type": "github" + } + ], + "time": "2026-01-26T20:59:18+00:00" + }, { "name": "squizlabs/php_codesniffer", "version": "4.0.1", @@ -917,48 +4798,100 @@ "time": "2025-11-10T16:43:36+00:00" }, { - "name": "symfony/console", - "version": "v6.4.32", + "name": "staabm/side-effects-detector", + "version": "1.0.5", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3" + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3", - "reference": "0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", "shasum": "" }, "require": { - "php": ">=8.1", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "symfony/console", + "version": "v7.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d", + "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d", + "shasum": "" + }, + "require": { + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" + "symfony/string": "^7.2|^8.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -992,7 +4925,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.32" + "source": "https://github.com/symfony/console/tree/v7.4.7" }, "funding": [ { @@ -1012,7 +4945,145 @@ "type": "tidelift" } ], - "time": "2026-01-13T08:45:59+00:00" + "time": "2026-03-06T14:06:20+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "3ebc794fa5315e59fd122561623c2e2e4280538e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/3ebc794fa5315e59fd122561623c2e2e4280538e", + "reference": "3ebc794fa5315e59fd122561623c2e2e4280538e", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-02-25T16:50:00+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "8655bf1076b7a3a346cb11413ffdabff50c7ffcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/8655bf1076b7a3a346cb11413ffdabff50c7ffcf", + "reference": "8655bf1076b7a3a346cb11413ffdabff50c7ffcf", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-29T09:40:50+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1349,6 +5420,71 @@ ], "time": "2024-12-23T08:48:59+00:00" }, + { + "name": "symfony/process", + "version": "v7.4.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "608476f4604102976d687c483ac63a79ba18cc97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/608476f4604102976d687c483ac63a79ba18cc97", + "reference": "608476f4604102976d687c483ac63a79ba18cc97", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.4.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-26T15:07:59+00:00" + }, { "name": "symfony/service-contracts", "version": "v3.6.1", @@ -1438,16 +5574,16 @@ }, { "name": "symfony/string", - "version": "v7.4.4", + "version": "v7.4.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f" + "reference": "9f209231affa85aa930a5e46e6eb03381424b30b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/1c4b10461bf2ec27537b5f36105337262f5f5d6f", - "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f", + "url": "https://api.github.com/repos/symfony/string/zipball/9f209231affa85aa930a5e46e6eb03381424b30b", + "reference": "9f209231affa85aa930a5e46e6eb03381424b30b", "shasum": "" }, "require": { @@ -1505,7 +5641,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.4" + "source": "https://github.com/symfony/string/tree/v7.4.6" }, "funding": [ { @@ -1525,7 +5661,178 @@ "type": "tidelift" } ], - "time": "2026-01-12T10:54:30+00:00" + "time": "2026-02-09T09:33:46+00:00" + }, + { + "name": "ta-tikoma/phpunit-architecture-test", + "version": "0.8.7", + "source": { + "type": "git", + "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", + "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/1248f3f506ca9641d4f68cebcd538fa489754db8", + "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18.0 || ^5.0.0", + "php": "^8.1.0", + "phpdocumentor/reflection-docblock": "^5.3.0 || ^6.0.0", + "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0 || ^13.0.0", + "symfony/finder": "^6.4.0 || ^7.0.0 || ^8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.13.7", + "phpstan/phpstan": "^1.10.52" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPUnit\\Architecture\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ni Shi", + "email": "futik0ma011@gmail.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Methods for testing application architecture", + "keywords": [ + "architecture", + "phpunit", + "stucture", + "test", + "testing" + ], + "support": { + "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.7" + }, + "time": "2026-02-17T17:25:14+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-11-17T20:03:58+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.1.6", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/ff31ad6efc62e66e518fbab1cde3453d389bcdc8", + "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.1.6" + }, + "time": "2026-02-27T10:28:38+00:00" } ], "aliases": [], @@ -1534,7 +5841,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.2" + "php": "^8.2", + "ext-json": "*" }, "platform-dev": {}, "plugin-api-version": "2.9.0" diff --git a/example.php b/example.php index a12e728..c6d1a99 100644 --- a/example.php +++ b/example.php @@ -5,7 +5,8 @@ require_once "vendor/autoload.php"; $client = new Ivuorinen\BusinessDataFetcher\BusinessDataFetcher(); try { $results = $client->getBusinessInformation("1639413-9"); - print_r($results); + // Convert to JSON + echo json_encode($results, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); } catch (Exception $e) { var_dump($e); } diff --git a/gen/parser.php b/gen/parser.php index d8d3d1e..6d85edd 100644 --- a/gen/parser.php +++ b/gen/parser.php @@ -87,7 +87,7 @@ foreach ($classes as $className => $vars) { // Get name of the class from filename and split CamelCase to words. $classNameString = $className; $classNameString = str_replace("Bis", "", $classNameString); - $classNameString = preg_replace('/(? $vars) { } if (!empty($traits)) { - $usesHeader[] = "use Ivuorinen\BusinessDataFetcher\Traits;"; + $usesHeader[] = "use Ivuorinen\BusinessDataFetcher\\v1\Traits;"; } $usesString = implode("\n", $usesHeader); @@ -134,7 +134,7 @@ foreach ($classes as $className => $vars) { $file = " $file) { $filePath = sprintf('%s%s%s.php', $dtoDir, DS, $className); echo $filePath . "\n"; diff --git a/phpcs.xml b/phpcs.xml index 66173cf..7d00f4f 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,6 +1,7 @@ PSR12 + src diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..d7b8544 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + tests/Unit + + + + + src + + + diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..002641b --- /dev/null +++ b/rector.php @@ -0,0 +1,16 @@ +withPaths([ + __DIR__ . '/src', + ]) + ->withPhpSets(php82: true) + ->withPreparedSets( + deadCode: true, + codeQuality: true, + typeDeclarations: true, + ); diff --git a/src/BusinessDataFetcher.php b/src/BusinessDataFetcher.php index 8b5ca69..506e7b7 100644 --- a/src/BusinessDataFetcher.php +++ b/src/BusinessDataFetcher.php @@ -2,47 +2,37 @@ namespace Ivuorinen\BusinessDataFetcher; -use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; -use Ivuorinen\BusinessDataFetcher\Dto\BisCompanyDetails; -use Ivuorinen\BusinessDataFetcher\Exceptions\ApiResponseErrorException; +use Ivuorinen\BusinessDataFetcher\Http\AbstractClient; +use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyDetails; +use Ivuorinen\BusinessDataFetcher\v1\Exceptions\ApiResponseErrorException; use Psr\Http\Message\ResponseInterface; -/** - * Fetches and returns business data from avoindata - */ -class BusinessDataFetcher +/** Client for the PRH BIS v1 API (Finnish business data). */ +class BusinessDataFetcher extends AbstractClient { - /** - * @var \GuzzleHttp\Client - */ - private Client $httpClient; - - /** - * BusinessDataFetcher constructor. - */ - public function __construct() + /** @inheritDoc */ + protected function getBaseUri(): string { - $this->httpClient = new Client([ - 'base_uri' => 'https://avoindata.prh.fi', - 'timeout' => 2, - ]); + return 'https://avoindata.prh.fi'; + } + + /** @inheritDoc */ + protected function getTimeout(): int + { + return 2; } /** * Fetch Business Information. * - * @return BisCompanyDetails[] $response_data + * @return BisCompanyDetails[] * @throws \Exception|\GuzzleHttp\Exception\GuzzleException */ public function getBusinessInformation(string $businessId): array { - // Set request variables - $requestUrl = '/bis/v1'; - - // Get the business data try { - $uri = $requestUrl . '/' . $businessId; + $uri = '/bis/v1/' . rawurlencode($businessId); $response = $this->httpClient->get($uri); if ($response->getStatusCode() !== 200) { @@ -52,7 +42,7 @@ class BusinessDataFetcher ); } - $response_data = $this->parseResponse($response); + return $this->parseResponse($response); } catch (RequestException $exception) { throw new ApiResponseErrorException( $exception->getMessage(), @@ -60,8 +50,6 @@ class BusinessDataFetcher $exception ); } - - return $response_data; } /** @@ -69,8 +57,7 @@ class BusinessDataFetcher * * @return BisCompanyDetails[] * @throws \JsonException - * @throws \Spatie\DataTransferObject\Exceptions\UnknownProperties - * @throws \Ivuorinen\BusinessDataFetcher\Exceptions\ApiResponseErrorException + * @throws ApiResponseErrorException */ public function parseResponse(ResponseInterface $response): array { @@ -88,7 +75,7 @@ class BusinessDataFetcher ); } - if (!isset($data['results'])) { + if (!isset($data['results']) || !is_array($data['results'])) { throw new ApiResponseErrorException( 'Invalid response data', $response->getStatusCode() @@ -98,7 +85,7 @@ class BusinessDataFetcher $results = []; foreach ($data['results'] as $result) { - $results[] = new BisCompanyDetails($result); + $results[] = $this->mapper->map(BisCompanyDetails::class, $result); } return $results; diff --git a/src/Dto/BisAddress.php b/src/Dto/BisAddress.php deleted file mode 100644 index c3233e1..0000000 --- a/src/Dto/BisAddress.php +++ /dev/null @@ -1,56 +0,0 @@ -httpClient = $httpClient ?? HttpClientFactory::create( + $this->getBaseUri(), + $this->getTimeout() + ); + + $this->mapper = (new MapperBuilder()) + ->allowSuperfluousKeys() + ->mapper(); + } + + /** Get the base URI for the API. */ + abstract protected function getBaseUri(): string; + + /** Get the HTTP request timeout in seconds. */ + protected function getTimeout(): int + { + return 10; + } + + /** + * Perform a GET request and decode the JSON response. + * + * @param array $query + * @return array + * @throws \GuzzleHttp\Exception\GuzzleException + * @throws \JsonException + */ + protected function getJson(string $uri, array $query = []): array + { + $options = []; + if ($query !== []) { + $options['query'] = $query; + } + + $response = $this->httpClient->get($uri, $options); + + $data = json_decode( + $response->getBody()->getContents(), + true, + 512, + JSON_THROW_ON_ERROR + ); + + if (!is_array($data)) { + throw new \JsonException('Response is not a valid JSON object or array'); + } + + return $data; + } +} diff --git a/src/Http/HttpClientFactory.php b/src/Http/HttpClientFactory.php new file mode 100644 index 0000000..f3e1a43 --- /dev/null +++ b/src/Http/HttpClientFactory.php @@ -0,0 +1,18 @@ + $baseUri, + 'timeout' => $timeout, + ]); + } +} diff --git a/src/Traits/HasLanguage.php b/src/Traits/HasLanguage.php deleted file mode 100644 index 9929df8..0000000 --- a/src/Traits/HasLanguage.php +++ /dev/null @@ -1,26 +0,0 @@ -language) { - 'fi' => 'finnish', - 'en' => 'english', - 'sv' => 'swedish', - default => 'unknown:' . $this->language, - }; - } -} diff --git a/src/Traits/HasSource.php b/src/Traits/HasSource.php deleted file mode 100644 index 4315a3d..0000000 --- a/src/Traits/HasSource.php +++ /dev/null @@ -1,34 +0,0 @@ -source) { - 0 => 'common', - 1 => 'Finnish Patent and Registration Office', - 2 => 'Tax Administration', - 3 => 'Business Information System', - default => '', - }; - } -} diff --git a/src/Traits/HasVersion.php b/src/Traits/HasVersion.php deleted file mode 100644 index 19b308f..0000000 --- a/src/Traits/HasVersion.php +++ /dev/null @@ -1,11 +0,0 @@ -1 for historical contact details. - */ - public int $version = 0; -} diff --git a/src/v1/Dto/BisAddress.php b/src/v1/Dto/BisAddress.php new file mode 100644 index 0000000..5256c1e --- /dev/null +++ b/src/v1/Dto/BisAddress.php @@ -0,0 +1,27 @@ + $names + * @param list $auxiliaryNames + * @param list $addresses + * @param list $companyForms + * @param list $liquidations + * @param list $businessLines + * @param list $languages + * @param list $registeredOffices + * @param list $contactDetails + * @param list $registeredEntries + * @param list $businessIdChanges + */ + public function __construct( + public string $businessId = '', + public string $registrationDate = '', + public ?string $companyForm = null, + public ?string $detailsUri = null, + public string $name = '', + public array $names = [], + public array $auxiliaryNames = [], + public array $addresses = [], + public array $companyForms = [], + public array $liquidations = [], + public array $businessLines = [], + public array $languages = [], + public array $registeredOffices = [], + public array $contactDetails = [], + public array $registeredEntries = [], + public array $businessIdChanges = [], + ) { + } +} diff --git a/src/v1/Dto/BisCompanyForm.php b/src/v1/Dto/BisCompanyForm.php new file mode 100644 index 0000000..b7c120d --- /dev/null +++ b/src/v1/Dto/BisCompanyForm.php @@ -0,0 +1,23 @@ +authority) { diff --git a/src/Traits/HasChange.php b/src/v1/Traits/HasChange.php similarity index 63% rename from src/Traits/HasChange.php rename to src/v1/Traits/HasChange.php index 9ef68ae..5b1dd63 100644 --- a/src/Traits/HasChange.php +++ b/src/v1/Traits/HasChange.php @@ -1,19 +1,11 @@ change) { diff --git a/src/v1/Traits/HasLanguage.php b/src/v1/Traits/HasLanguage.php new file mode 100644 index 0000000..1b5b953 --- /dev/null +++ b/src/v1/Traits/HasLanguage.php @@ -0,0 +1,22 @@ +language === null) { + return 'unknown'; + } + + return match ($this->language) { + 'fi' => 'finnish', + 'en' => 'english', + 'sv' => 'swedish', + default => 'unknown:' . $this->language, + }; + } +} diff --git a/src/Traits/HasRegister.php b/src/v1/Traits/HasRegister.php similarity index 68% rename from src/Traits/HasRegister.php rename to src/v1/Traits/HasRegister.php index 9783b65..be671af 100644 --- a/src/Traits/HasRegister.php +++ b/src/v1/Traits/HasRegister.php @@ -1,18 +1,11 @@ register) { diff --git a/src/v1/Traits/HasSource.php b/src/v1/Traits/HasSource.php new file mode 100644 index 0000000..e75e062 --- /dev/null +++ b/src/v1/Traits/HasSource.php @@ -0,0 +1,19 @@ +source) { + 0 => 'common', + 1 => 'Finnish Patent and Registration Office', + 2 => 'Tax Administration', + 3 => 'Business Information System', + default => '', + }; + } +} diff --git a/src/v3/Client.php b/src/v3/Client.php new file mode 100644 index 0000000..02a59dd --- /dev/null +++ b/src/v3/Client.php @@ -0,0 +1,124 @@ + $name, + 'businessId' => $businessId, + 'location' => $location, + 'companyForm' => $companyForm, + 'mainBusinessLine' => $mainBusinessLine, + 'registrationDateStart' => $registrationDateStart, + 'registrationDateEnd' => $registrationDateEnd, + 'postCode' => $postCode, + 'businessIdRegistrationStart' => $businessIdRegistrationStart, + 'businessIdRegistrationEnd' => $businessIdRegistrationEnd, + 'page' => $page, + ], fn (string|int|null $v): bool => $v !== null); + + try { + $data = $this->getJson(self::API_PREFIX . '/companies', $query); + return $this->mapper->map(CompanySearchResult::class, $data); + } catch (RequestException | \JsonException $e) { + throw new V3ApiException($e->getMessage(), (int) $e->getCode(), $e); + } + } + + /** + * Retrieve code list description. + * + * @throws V3ApiException + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function getDescription(string $code, string $lang = 'en'): string + { + try { + $response = $this->httpClient->get(self::API_PREFIX . '/description', [ + 'query' => ['code' => $code, 'lang' => $lang], + ]); + + return $response->getBody()->getContents(); + } catch (RequestException $e) { + throw new V3ApiException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Retrieve postal code details. + * + * @return PostCodeEntry[] + * @throws V3ApiException + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function getPostCodes(string $lang = 'en'): array + { + try { + $data = $this->getJson(self::API_PREFIX . '/post_codes', ['lang' => $lang]); + + $results = []; + foreach ($data as $entry) { + $results[] = $this->mapper->map(PostCodeEntry::class, $entry); + } + + return $results; + } catch (RequestException | \JsonException $e) { + throw new V3ApiException($e->getMessage(), (int) $e->getCode(), $e); + } + } + + /** + * Get all companies as a ZIP download stream. + * + * @throws V3ApiException + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function getAllCompanies(): StreamInterface + { + try { + $response = $this->httpClient->get(self::API_PREFIX . '/all_companies', [ + 'stream' => true, + ]); + + return $response->getBody(); + } catch (RequestException $e) { + throw new V3ApiException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/src/v3/Dto/Address.php b/src/v3/Dto/Address.php new file mode 100644 index 0000000..607fa7e --- /dev/null +++ b/src/v3/Dto/Address.php @@ -0,0 +1,28 @@ + $postOffices + */ + public function __construct( + public int $type = 0, + public string $source = '', + public ?string $street = null, + public ?string $postCode = null, + public array $postOffices = [], + public ?string $postOfficeBox = null, + public ?string $buildingNumber = null, + public ?string $entrance = null, + public ?string $apartmentNumber = null, + public ?string $apartmentIdSuffix = null, + public ?string $co = null, + public ?string $country = null, + public ?string $freeAddressLine = null, + public ?string $registrationDate = null, + ) { + } +} diff --git a/src/v3/Dto/BusinessId.php b/src/v3/Dto/BusinessId.php new file mode 100644 index 0000000..f4427a9 --- /dev/null +++ b/src/v3/Dto/BusinessId.php @@ -0,0 +1,14 @@ + $descriptions + */ + public function __construct( + public string $code, + public array $descriptions = [], + public ?string $typeCodeSet = null, + public ?string $registrationDate = null, + public ?string $source = null, + ) { + } +} diff --git a/src/v3/Dto/Company.php b/src/v3/Dto/Company.php new file mode 100644 index 0000000..f843266 --- /dev/null +++ b/src/v3/Dto/Company.php @@ -0,0 +1,32 @@ + $names + * @param list $companyForms + * @param list $companySituations + * @param list $registeredEntries + * @param list
$addresses + */ + public function __construct( + public BusinessId $businessId, + public string $tradeRegisterStatus = '', + public string $lastModified = '', + public ?EuId $euId = null, + public array $names = [], + public ?BusinessLine $mainBusinessLine = null, + public ?Website $website = null, + public array $companyForms = [], + public array $companySituations = [], + public array $registeredEntries = [], + public array $addresses = [], + public ?string $status = null, + public ?string $registrationDate = null, + public ?string $endDate = null, + ) { + } +} diff --git a/src/v3/Dto/CompanyForm.php b/src/v3/Dto/CompanyForm.php new file mode 100644 index 0000000..b261c6c --- /dev/null +++ b/src/v3/Dto/CompanyForm.php @@ -0,0 +1,20 @@ + $descriptions + */ + public function __construct( + public string $type = '', + public string $source = '', + public int $version = 0, + public array $descriptions = [], + public ?string $registrationDate = null, + public ?string $endDate = null, + ) { + } +} diff --git a/src/v3/Dto/CompanySearchResult.php b/src/v3/Dto/CompanySearchResult.php new file mode 100644 index 0000000..3e9c42f --- /dev/null +++ b/src/v3/Dto/CompanySearchResult.php @@ -0,0 +1,16 @@ + $companies + */ + public function __construct( + public int $totalResults = 0, + public array $companies = [], + ) { + } +} diff --git a/src/v3/Dto/CompanySituation.php b/src/v3/Dto/CompanySituation.php new file mode 100644 index 0000000..fc47b90 --- /dev/null +++ b/src/v3/Dto/CompanySituation.php @@ -0,0 +1,15 @@ + $descriptions + */ + public function __construct( + public string $type = '', + public string $register = '', + public string $authority = '', + public array $descriptions = [], + public ?string $registrationDate = null, + public ?string $endDate = null, + ) { + } +} diff --git a/src/v3/Dto/Website.php b/src/v3/Dto/Website.php new file mode 100644 index 0000000..a4a4952 --- /dev/null +++ b/src/v3/Dto/Website.php @@ -0,0 +1,14 @@ +in('Feature'); diff --git a/tests/Unit/BusinessDataFetcherTest.php b/tests/Unit/BusinessDataFetcherTest.php new file mode 100644 index 0000000..65c9ab6 --- /dev/null +++ b/tests/Unit/BusinessDataFetcherTest.php @@ -0,0 +1,52 @@ + HandlerStack::create($mock)]); + return new BusinessDataFetcher($client); +} + +it('returns BisCompanyDetails array on success', function () { + $json = file_get_contents(__DIR__ . '/../Fixtures/sample-response.json'); + $fetcher = createFetcherWithMock(new Response(200, [], $json)); + + $results = $fetcher->getBusinessInformation('1639413-9'); + + expect($results)->toBeArray() + ->and($results)->toHaveCount(1) + ->and($results[0])->toBeInstanceOf(BisCompanyDetails::class) + ->and($results[0]->businessId)->toBe('1639413-9'); +}); + +it('throws on non-200 status code', function () { + $fetcher = createFetcherWithMock( + new Response(500, [], '{"error": "Internal Server Error"}') + ); + + $fetcher->getBusinessInformation('1639413-9'); +})->throws(ApiResponseErrorException::class); + +it('throws on malformed JSON', function () { + $fetcher = createFetcherWithMock( + new Response(200, [], 'not-json') + ); + + $fetcher->getBusinessInformation('1639413-9'); +})->throws(JsonException::class); + +it('throws when results key is missing', function () { + $fetcher = createFetcherWithMock( + new Response(200, [], '{"type": "test"}') + ); + + $fetcher->getBusinessInformation('1639413-9'); +})->throws(ApiResponseErrorException::class); diff --git a/tests/Unit/v1/Dto/BisCompanyDetailsTest.php b/tests/Unit/v1/Dto/BisCompanyDetailsTest.php new file mode 100644 index 0000000..3967008 --- /dev/null +++ b/tests/Unit/v1/Dto/BisCompanyDetailsTest.php @@ -0,0 +1,43 @@ +allowSuperfluousKeys() + ->mapper(); + + $result = $mapper->map(BisCompanyDetails::class, $json['results'][0]); + + expect($result) + ->toBeInstanceOf(BisCompanyDetails::class) + ->and($result->businessId)->toBe('1639413-9') + ->and($result->registrationDate)->toBe('2001-04-03') + ->and($result->companyForm)->toBe('OY') + ->and($result->name)->toBe('Oy Example Ab') + ->and($result->names)->toHaveCount(1) + ->and($result->names[0]->name)->toBe('Oy Example Ab') + ->and($result->names[0]->getLanguageString())->toBe('finnish') + ->and($result->names[0]->getSourceText())->toBe('common') + ->and($result->addresses)->toHaveCount(1) + ->and($result->addresses[0]->street)->toBe('Testitie 1') + ->and($result->addresses[0]->postCode)->toBe('00100') + ->and($result->addresses[0]->city)->toBe('HELSINKI') + ->and($result->companyForms)->toHaveCount(1) + ->and($result->companyForms[0]->name)->toBe('Osakeyhtiö') + ->and($result->businessLines)->toHaveCount(1) + ->and($result->businessLines[0]->name)->toBe('Computer programming activities') + ->and($result->registeredEntries)->toHaveCount(1) + ->and($result->registeredEntries[0]->getAuthorityString())->toBe('Finnish Patent and Registration Office') + ->and($result->registeredEntries[0]->getRegisterString())->toBe('Trade Register') + ->and($result->contactDetails)->toHaveCount(1) + ->and($result->contactDetails[0]->value)->toBe('www.example.fi'); +}); diff --git a/tests/Unit/v1/Dto/DtoHydrationTest.php b/tests/Unit/v1/Dto/DtoHydrationTest.php new file mode 100644 index 0000000..132ac59 --- /dev/null +++ b/tests/Unit/v1/Dto/DtoHydrationTest.php @@ -0,0 +1,87 @@ +allowSuperfluousKeys() + ->mapper(); +} + +it('hydrates all DTOs from arrays', function (string $class, array $data, string $property, mixed $expected) { + $dto = valinorMapper()->map($class, $data); + expect($dto)->toBeInstanceOf($class) + ->and($dto->$property)->toBe($expected); +})->with([ + 'BisAddress' => [ + BisAddress::class, + ['type' => 1, 'street' => 'Katu 1', 'postCode' => '00100', 'city' => 'HELSINKI', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'], + 'street', + 'Katu 1', + ], + 'BisCompanyName' => [ + BisCompanyName::class, + ['order' => 0, 'name' => 'Test Oy', 'source' => 1, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'], + 'name', + 'Test Oy', + ], + 'BisCompanyForm' => [ + BisCompanyForm::class, + ['name' => 'Osakeyhtiö', 'type' => 'OY', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'], + 'type', + 'OY', + ], + 'BisCompanyBusinessLine' => [ + BisCompanyBusinessLine::class, + ['order' => 0, 'name' => 'IT', 'source' => 2, 'version' => 1, 'language' => 'en', 'registrationDate' => '2020-01-01'], + 'name', + 'IT', + ], + 'BisCompanyLanguage' => [ + BisCompanyLanguage::class, + ['name' => 'Finnish', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'], + 'language', + 'fi', + ], + 'BisCompanyRegisteredEntry' => [ + BisCompanyRegisteredEntry::class, + ['authority' => 2, 'register' => 1, 'status' => 2, 'description' => 'Registered', 'language' => 'en', 'registrationDate' => '2020-01-01'], + 'description', + 'Registered', + ], + 'BisCompanyLiquidation' => [ + BisCompanyLiquidation::class, + ['name' => 'Bankruptcy', 'type' => 'K', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'], + 'name', + 'Bankruptcy', + ], + 'BisCompanyRegisteredOffice' => [ + BisCompanyRegisteredOffice::class, + ['order' => 0, 'name' => 'Helsinki', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'], + 'name', + 'Helsinki', + ], + 'BisCompanyContactDetail' => [ + BisCompanyContactDetail::class, + ['value' => 'www.test.fi', 'type' => 'Website', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'], + 'value', + 'www.test.fi', + ], + 'BisCompanyBusinessIdChange' => [ + BisCompanyBusinessIdChange::class, + ['description' => 'Changed', 'reason' => '1', 'oldBusinessId' => '1234567-8', 'newBusinessId' => '8765432-1', 'source' => 0, 'language' => 'fi', 'change' => 5], + 'oldBusinessId', + '1234567-8', + ], +]); diff --git a/tests/Unit/v1/Traits/HasAuthorityTest.php b/tests/Unit/v1/Traits/HasAuthorityTest.php new file mode 100644 index 0000000..e39938a --- /dev/null +++ b/tests/Unit/v1/Traits/HasAuthorityTest.php @@ -0,0 +1,30 @@ +getAuthorityString())->toBe('Tax Administration'); +}); + +it('returns Finnish Patent and Registration Office for authority 2', function () { + expect(makeAuthorityObject(2)->getAuthorityString())->toBe('Finnish Patent and Registration Office'); +}); + +it('returns Population Register for authority 3', function () { + expect(makeAuthorityObject(3)->getAuthorityString())->toBe('Population Register'); +}); + +it('returns unknown for unknown authority', function () { + expect(makeAuthorityObject(99)->getAuthorityString())->toBe('unknown:99'); +}); diff --git a/tests/Unit/v1/Traits/HasChangeTest.php b/tests/Unit/v1/Traits/HasChangeTest.php new file mode 100644 index 0000000..4e45f46 --- /dev/null +++ b/tests/Unit/v1/Traits/HasChangeTest.php @@ -0,0 +1,41 @@ +getChangeString())->toBe($expected); +})->with([ + [2, 'Business ID removal'], + [3, 'Combining of double IDs'], + [5, 'ID changed'], + [44, 'Fusion'], + [45, 'Operator continuing VAT activities'], + [46, 'Relation to predecessor'], + [47, 'Division'], + [48, 'Bankruptcy relationship'], + [49, 'Operations continued by a private trader'], + [57, 'Partial division'], +]); + +it('maps FUU string to Fusion', function () { + expect(makeChangeObject('FUU')->getChangeString())->toBe('Fusion'); +}); + +it('maps DIF string to Division', function () { + expect(makeChangeObject('DIF')->getChangeString())->toBe('Division'); +}); + +it('returns unknown for unrecognized change', function () { + expect(makeChangeObject(999)->getChangeString())->toBe('unknown:999'); +}); diff --git a/tests/Unit/v1/Traits/HasLanguageTest.php b/tests/Unit/v1/Traits/HasLanguageTest.php new file mode 100644 index 0000000..8d6b0f7 --- /dev/null +++ b/tests/Unit/v1/Traits/HasLanguageTest.php @@ -0,0 +1,30 @@ +getLanguageString())->toBe('finnish'); +}); + +it('returns english for en', function () { + expect(makeLanguageObject('en')->getLanguageString())->toBe('english'); +}); + +it('returns swedish for sv', function () { + expect(makeLanguageObject('sv')->getLanguageString())->toBe('swedish'); +}); + +it('returns unknown for unrecognized language', function () { + expect(makeLanguageObject('de')->getLanguageString())->toBe('unknown:de'); +}); diff --git a/tests/Unit/v1/Traits/HasRegisterTest.php b/tests/Unit/v1/Traits/HasRegisterTest.php new file mode 100644 index 0000000..2d90b92 --- /dev/null +++ b/tests/Unit/v1/Traits/HasRegisterTest.php @@ -0,0 +1,35 @@ +getRegisterString())->toBe($expected); +})->with([ + [1, 'Trade Register'], + [2, 'Register of Foundations'], + [3, 'Register of Associations'], + [4, 'Tax Administration'], + [5, 'Prepayment Register'], + [6, 'VAT Register'], + [7, 'Employer Register'], + [8, 'Register of bodies liable for tax on insurance premiums'], +]); + +it('returns unknown for unrecognized register', function () { + expect(makeRegisterObject(99)->getRegisterString())->toBe('unknown:99'); +}); + +it('returns unknown for null register', function () { + expect(makeRegisterObject(null)->getRegisterString())->toBe('unknown:'); +}); diff --git a/tests/Unit/v1/Traits/HasSourceTest.php b/tests/Unit/v1/Traits/HasSourceTest.php new file mode 100644 index 0000000..9ca83fc --- /dev/null +++ b/tests/Unit/v1/Traits/HasSourceTest.php @@ -0,0 +1,34 @@ +getSourceText())->toBe('common'); +}); + +it('returns Finnish Patent and Registration Office for source 1', function () { + expect(makeSourceObject(1)->getSourceText())->toBe('Finnish Patent and Registration Office'); +}); + +it('returns Tax Administration for source 2', function () { + expect(makeSourceObject(2)->getSourceText())->toBe('Tax Administration'); +}); + +it('returns Business Information System for source 3', function () { + expect(makeSourceObject(3)->getSourceText())->toBe('Business Information System'); +}); + +it('returns empty string for unknown source', function () { + expect(makeSourceObject(99)->getSourceText())->toBe(''); +}); diff --git a/tests/Unit/v3/ClientTest.php b/tests/Unit/v3/ClientTest.php new file mode 100644 index 0000000..848d8d7 --- /dev/null +++ b/tests/Unit/v3/ClientTest.php @@ -0,0 +1,73 @@ + HandlerStack::create($mock)]); + return new V3Client($client); +} + +it('searches companies successfully', function () { + $json = file_get_contents(__DIR__ . '/../../Fixtures/v3-search-response.json'); + $client = createV3ClientWithMock(new Response(200, [], $json)); + + $result = $client->searchCompanies(name: 'Example'); + + expect($result) + ->toBeInstanceOf(CompanySearchResult::class) + ->and($result->totalResults)->toBe(1) + ->and($result->companies)->toHaveCount(1) + ->and($result->companies[0]->businessId->value)->toBe('1639413-9') + ->and($result->companies[0]->names[0]->name)->toBe('Oy Example Ab') + ->and($result->companies[0]->tradeRegisterStatus)->toBe('2') + ->and($result->companies[0]->addresses[0]->street)->toBe('Testitie 1'); +}); + +it('throws V3ApiException on request failure for search', function () { + $client = createV3ClientWithMock(new Response(500, [], '{"message":"error"}')); + $client->searchCompanies(name: 'Test'); +})->throws(V3ApiException::class); + +it('retrieves description as plain text', function () { + $client = createV3ClientWithMock(new Response(200, [], 'Trade Register')); + $result = $client->getDescription('REK', 'en'); + + expect($result)->toBe('Trade Register'); +}); + +it('throws V3ApiException on description failure', function () { + $client = createV3ClientWithMock(new Response(400, [], '{"message":"bad request"}')); + $client->getDescription('INVALID'); +})->throws(V3ApiException::class); + +it('retrieves post codes', function () { + $json = (string) json_encode([ + ['postCode' => '00100', 'city' => 'HELSINKI', 'active' => true, 'languageCode' => '1', 'municipalityCode' => '091'], + ['postCode' => '00200', 'city' => 'HELSINKI', 'active' => true, 'languageCode' => '1', 'municipalityCode' => '091'], + ]); + $client = createV3ClientWithMock(new Response(200, [], $json)); + + $results = $client->getPostCodes('fi'); + + expect($results)->toHaveCount(2) + ->and($results[0])->toBeInstanceOf(PostCodeEntry::class) + ->and($results[0]->postCode)->toBe('00100') + ->and($results[0]->city)->toBe('HELSINKI') + ->and($results[1]->postCode)->toBe('00200'); +}); + +it('returns stream for all companies download', function () { + $client = createV3ClientWithMock(new Response(200, [], 'zip-content')); + $stream = $client->getAllCompanies(); + + expect($stream->getContents())->toBe('zip-content'); +}); diff --git a/tests/Unit/v3/Dto/CompanyTest.php b/tests/Unit/v3/Dto/CompanyTest.php new file mode 100644 index 0000000..830356f --- /dev/null +++ b/tests/Unit/v3/Dto/CompanyTest.php @@ -0,0 +1,53 @@ +allowSuperfluousKeys() + ->mapper(); + + $result = $mapper->map(CompanySearchResult::class, $json); + + expect($result->totalResults)->toBe(1) + ->and($result->companies)->toHaveCount(1); + + $company = $result->companies[0]; + + expect($company)->toBeInstanceOf(Company::class) + ->and($company->businessId->value)->toBe('1639413-9') + ->and($company->businessId->registrationDate)->toBe('2001-04-03') + ->and($company->euId)->not->toBeNull() + ->and($company->euId->value)->toBe('FIFOO123456') + ->and($company->names)->toHaveCount(1) + ->and($company->names[0]->name)->toBe('Oy Example Ab') + ->and($company->names[0]->type)->toBe('TOIM') + ->and($company->mainBusinessLine)->not->toBeNull() + ->and($company->mainBusinessLine->code)->toBe('62010') + ->and($company->mainBusinessLine->descriptions)->toHaveCount(1) + ->and($company->mainBusinessLine->descriptions[0]->description)->toBe('Computer programming activities') + ->and($company->website)->not->toBeNull() + ->and($company->website->url)->toBe('www.example.fi') + ->and($company->companyForms)->toHaveCount(1) + ->and($company->companyForms[0]->type)->toBe('OY') + ->and($company->companyForms[0]->descriptions)->toHaveCount(2) + ->and($company->registeredEntries)->toHaveCount(1) + ->and($company->registeredEntries[0]->register)->toBe('1') + ->and($company->registeredEntries[0]->authority)->toBe('2') + ->and($company->addresses)->toHaveCount(1) + ->and($company->addresses[0]->street)->toBe('Testitie 1') + ->and($company->addresses[0]->postOffices)->toHaveCount(1) + ->and($company->addresses[0]->postOffices[0]->city)->toBe('HELSINKI') + ->and($company->tradeRegisterStatus)->toBe('2') + ->and($company->status)->toBe('ACTIVE') + ->and($company->lastModified)->toBe('2023-06-15T10:30:00Z'); +});