diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..6fed997
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,34 @@
+## Description
+
+
+
+**Motivation:**
+
+**Related issue(s):** Closes #
+
+---
+
+## Type of Change
+
+- 🐛 Bug fix (non-breaking change that fixes an issue) [ ]
+- ✨ New feature (non-breaking change that adds functionality) [ ]
+- 💥 Breaking change (fix or feature that causes existing functionality to change and that could impact other libs) [ ]
+- 🔧 Refactor (no functional changes, code improvement only) [ ]
+- 📦 Dependency update [ ]
+- 🔒 Security fix [ ]
+- 📝 Documentation update [ ]
+
+---
+
+## Checklist
+### Code Quality
+- [ ] Code is linted and formatted
+- [ ] No unnecessary commented-out code or debug logs
+- [ ] No hardcoded values (use env variables or config)
+
+### Testing
+- [ ] Unit tests added / updated
+
+### Security & Ops
+- [ ] No sensitive data or secrets introduced
+- [ ] Logging and error handling are appropriate
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..86c6f0e
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,110 @@
+name: CI
+
+on:
+ push:
+ branches-ignore:
+ - main # main is never pushed to directly; releases are tag-driven
+ tags:
+ - '*'
+ pull_request:
+ branches:
+ - develop
+ types: [opened, synchronize, reopened, closed]
+
+jobs:
+ # -----------------------------------------------------------------------
+ # 1. QUALITY — static analysis & code style on PHP 8.4 source
+ # Runs on: push to feature branch | PR opened/updated → develop
+ # -----------------------------------------------------------------------
+ quality:
+ if: github.ref_type != 'tag' && github.event.action != 'closed'
+ uses: payplug/template-ci/.github/workflows/php-quality.yml@main
+ with:
+ php-version: '8.4'
+
+ # -----------------------------------------------------------------------
+ # 2. DOWNGRADE — transpile src/ + tests/ to PHP 7.2
+ # Runs on: push to feature branch | PR opened/updated → develop
+ # -----------------------------------------------------------------------
+ downgrade:
+ if: github.ref_type != 'tag' && github.event.action != 'closed'
+ needs: quality
+ uses: payplug/template-ci/.github/workflows/php-downgrade.yml@main
+ with:
+ php-version: '8.4'
+ artifact-name: 'payplug-core-php72'
+ artifact-retention-days: 7
+
+ # -----------------------------------------------------------------------
+ # 3. VALIDATE — install the downgraded package under PHP 7.2
+ # Runs on: push to feature branch | PR opened/updated → develop
+ # -----------------------------------------------------------------------
+ validate:
+ if: github.ref_type != 'tag' && github.event.action != 'closed'
+ needs: downgrade
+ uses: payplug/template-ci/.github/workflows/php-validate.yml@main
+ with:
+ php-version: '7.2'
+ artifact-name: 'payplug-core-php72'
+
+ # -----------------------------------------------------------------------
+ # 4. TEST — run unit tests against the downgraded PHP 7.2 code
+ # Runs on: push to feature branch | PR opened/updated → develop
+ # -----------------------------------------------------------------------
+ test-php72:
+ if: github.ref_type != 'tag' && github.event.action != 'closed'
+ needs: validate
+ uses: payplug/template-ci/.github/workflows/php-test-php72.yml@main
+ with:
+ php-version: '7.2'
+ artifact-name: 'payplug-core-php72'
+ test-namespace: 'PayplugPluginCore\tests\'
+
+ # -----------------------------------------------------------------------
+ # 5. PUSH-DIST — push transpiled code to dist/{php-target}/{branch}
+ # Runs in parallel with validate, as soon as downgrade succeeds.
+ # On feature branch push → dist/php72/feature/xxxx
+ # On push to develop → dist/php72/develop
+ # -----------------------------------------------------------------------
+ push-dist:
+ if: github.ref_type != 'tag' && github.event.action != 'closed'
+ needs: validate
+ permissions:
+ contents: write
+ uses: payplug/template-ci/.github/workflows/php72-push-dist.yml@main
+ with:
+ artifact-name: 'payplug-core-php72'
+ source-branch: ${{ github.head_ref || github.ref_name }}
+ php-target: 'php72'
+
+ # -----------------------------------------------------------------------
+ # 6. PACKAGE — zip and store the artifact on PR merged → develop
+ # -----------------------------------------------------------------------
+ package:
+ if: github.ref_type != 'tag' && github.event.action != 'closed'
+ uses: payplug/template-ci/.github/workflows/php-package.yml@main
+ with:
+ php-version: '8.4'
+ zip-prefix: 'payplug-plugin-core-php72'
+
+ # -----------------------------------------------------------------------
+ # 7. CLEANUP-DIST — delete dist/php72/feature/xxxx when PR is merged
+ # -----------------------------------------------------------------------
+ cleanup-dist:
+ if: github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true
+ permissions:
+ contents: write
+ uses: payplug/template-ci/.github/workflows/php72-delete-dist.yml@main
+ with:
+ source-branch: ${{ github.head_ref }}
+ php-target: 'php72'
+
+ # -----------------------------------------------------------------------
+ # 8. RELEASE — create a GitHub Release from a tag on main
+ # -----------------------------------------------------------------------
+ release:
+ if: github.event_name == 'push' && github.ref_type == 'tag'
+ uses: payplug/template-ci/.github/workflows/php-release.yml@main
+ with:
+ php-version: '8.4'
+ zip-prefix: 'payplug-plugin-core-php72'
diff --git a/.gitignore b/.gitignore
index aec1b8a..2f3bd80 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,5 +31,11 @@ node_modules
# Vendor
vendor
-# unit tests
-tests
+# PHP CS Fixer
+.php-cs-fixer.php
+.php-cs-fixer.cache
+
+# PHPStan
+var/
+payplug-core/
+.claude
\ No newline at end of file
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..2ac9e9c
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,24 @@
+in([
+ __DIR__ . '/src',
+ __DIR__ . '/tests',
+ ])
+ ->name('*.php');
+
+return (new PhpCsFixer\Config())
+ ->setRiskyAllowed(true)
+ ->setRules([
+ '@PSR12' => true,
+ '@PHP84Migration' => true,
+ 'array_syntax' => ['syntax' => 'short'],
+ 'no_unused_imports' => true,
+ 'ordered_imports' => ['sort_algorithm' => 'alpha'],
+ 'single_quote' => true,
+ 'trailing_comma_in_multiline' => true,
+ 'declare_strict_types' => true,
+ 'void_return' => true,
+ 'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced'],
+ ])
+ ->setFinder($finder);
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..57adc3e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,24 @@
+FROM php:8.1-cli
+
+# Install system dependencies
+RUN apt-get update && apt-get install -y \
+ git \
+ unzip \
+ curl \
+ && rm -rf /var/lib/apt/lists/*
+
+# Install additional extensions via pecl
+RUN pecl install xdebug \
+ && docker-php-ext-enable xdebug
+
+# Configure Xdebug — mode is controlled at runtime via XDEBUG_MODE env var
+RUN echo "xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
+ && echo "xdebug.client_port=9003" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
+ && echo "xdebug.start_with_request=trigger" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+
+# Install Composer
+COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
+
+WORKDIR /app
+
+CMD ["php", "-a"]
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9f522c2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,70 @@
+.PHONY: build up down shell install update test stan lint fix debug release
+
+DC = docker compose
+PHP = $(DC) run --rm php
+PHP_DEBUG = XDEBUG_MODE=debug $(DC) run --rm php
+
+## Docker
+build:
+ $(DC) build
+
+up:
+ $(DC) up -d
+
+down:
+ $(DC) down
+
+shell:
+ $(PHP) bash
+
+## Composer
+comp-install:
+ $(PHP) composer install
+
+update:
+ $(PHP) composer update
+
+## Quality
+test-unit:
+ $(PHP) vendor/bin/phpunit tests
+
+test-unit-inte:
+ $(PHP) vendor/bin/phpunit tests --group integration
+
+test-unit-units:
+ $(PHP) vendor/bin/phpunit tests --group units
+
+tu-dep:
+ $(PHP) vendor/bin/phpunit tests --display-phpunit-deprecations
+
+stan:
+ $(PHP) vendor/bin/phpstan analyse
+
+cs-lint:
+ $(PHP) vendor/bin/php-cs-fixer fix --diff --dry-run
+
+cs-fix:
+ $(PHP) vendor/bin/php-cs-fixer fix
+
+rector-dry:
+ $(PHP) vendor/bin/rector process --dry-run
+
+## Release (downgrade src/ to PHP 7.2 into payplug-core/)
+release:
+ rm -rf payplug-core && mkdir payplug-core && cp -r src payplug-core/ && cp -r tests payplug-core/
+ $(PHP) vendor/bin/rector process --config rector.php
+ $(PHP) php scripts/generate-release-composer.php
+
+## Debug (Xdebug step-debug enabled)
+debug:
+ $(PHP_DEBUG) bash
+
+## CI (runs all checks)
+ci: stan cs-lint test-unit
+
+install: build update comp-install
+
+audit:
+ $(PHP) composer audit
+
+security: audit
\ No newline at end of file
diff --git a/Readme.md b/Readme.md
index 0ad08eb..daa7627 100644
--- a/Readme.md
+++ b/Readme.md
@@ -101,3 +101,17 @@ Module core for Payplug integration. Enables management of payments, merchants,
## Next step Dev :
- [ ] Develop endpoint to create a payment link
+
+
+## Quick start:
+
+```bash
+composer install # Hooks are installed automatically
+composer cs:fix # Manual code formatting
+composer hooks:install # Reinstall hooks if needed
+composer hooks:install # Reinstall hooks if needed
+
+composer phpunit:unit # Run only unitary phpUnit tests
+composer phpunit:inte # Run only integration phpUnit tests
+composer phpunit:all # Run all phpUnit tests
+```
\ No newline at end of file
diff --git a/Security.md b/Security.md
new file mode 100644
index 0000000..c187ad0
--- /dev/null
+++ b/Security.md
@@ -0,0 +1,73 @@
+# Security Report
+
+## ⚠️ _This report is internal only_ ⚠️
+
+### Critical
+1. API Bearer Token Handling — `src/Utilities/Services/Api.php`
+ The secret API key is stored as a plain string property, passed through getter methods, and could leak in stack traces if exceptions occur.
+
+Recommendation: Never store tokens as plain strings. Obfuscate in any debug output (show last 4 chars only). Clear from memory when no longer needed.
+
+### High
+2. Unvalidated URLs Passed to Payment API — src/Gateways/AbstractPaymentGateway.php:28-39
+ return_url, cancel_url, and notification_url from the DTO are forwarded to the Payplug API without any validation. This opens the door to open redirect and phishing attacks.
+``` PHP
+'return_url' => $urls['return'], // no validation
+'cancel_url' => $urls['cancel'],
+```
+Recommendation: Validate with ``filter_var($url, FILTER_VALIDATE_URL)``, enforce HTTPS, and optionally restrict to your domain.
+
+3. Empty Whitelist Arrays — `src/Utilities/Traits/DependenciesLoader.php:11-15`
+ `$allowed_services` and `$allowed_gateways` are initialized empty and never populated, so in_array() checks always evaluate against an empty array — the whitelist mechanism is effectively disabled.
+
+Recommendation: Populate these arrays with the actual allowed values at initialization.
+
+4. Dynamic Class Instantiation — `src/Gateways/PaymentGateway.php:17-20`
+ The payment method name (user input) is used directly to construct a class name, with no whitelist enforced.
+``` PHP
+$class = '\PayplugPluginCore\Gateways\Payment\\'
+. str_replace('_', '', ucwords($payment_method_name, '_'))
+. 'PaymentGateway';
+```
+Recommendation: Validate against an explicit whitelist (e.g., ['standard', 'apple_pay', 'google_pay']) before building the class name.
+
+5. Exception Message Information Disclosure — src/Utilities/Services/Api.php:82
+ Raw exception messages are re-thrown to callers, potentially leaking internal configuration details or API information.
+
+Recommendation: Log full errors server-side, expose only generic messages to callers.
+
+### Medium
+6. Unvalidated Array Key Access — `src/Gateways/AbstractPaymentGateway.php:33-39`
+ Direct array access (`$customer['billing']`, `$urls['return']`, etc.) without checking key existence. Will cause PHP warnings/errors on incomplete data.
+
+Recommendation: Use `$array['key'] ?? null` or `array_key_exists()`.
+
+7. Missing Input Validation on Payment Method — `src/Actions/PaymentAction.php:25-27`
+ Only a null check is performed. No format, length, or character validation.
+
+`// todo: add a validator to check if the given paymentDTO is usable ← still a TODO`
+
+Recommendation: Implement the noted validator, enforce an allowed-values whitelist.
+
+8. Silent Type Coercion in DTO Hydration — `src/Models/Entities/PaymentInputDTO.php:64-71`
+ Invalid values are silently cast (e.g., `(int) "abc" → 0`) instead of being rejected.
+
+Recommendation: Validate before casting; return meaningful errors for type mismatches.
+
+Low / Informational
+
+| Issue | File |
+|-------|---------------------------------------------------------------------------------------------------------------|
+| 9 | Internal parameter names exposed in exception messages src/Gateways/Payment/StandardPaymentGateway.php:40 |
+| 10 | Generic \Exception used everywhere (hard to distinguish errors) src/Utilities/Exceptions/PayplugException.php |
+| 11 | Xdebug included in Docker build (should be dev-only) Dockerfile |
+
+
+### Priority Action Plan
+
+- Immediate: Add URL validation for `return_url / cancel_url / notification_url`
+- Immediate: Populate and enforce service/gateway whitelists
+- Short term: Add payment method whitelist validation
+- Short term: Implement the `TODO` validator in `PaymentAction`
+- Short term: Replace raw exception propagation with a safe error translation layer
+- Medium term: Custom exception hierarchy + secure token handling
\ No newline at end of file
diff --git a/captainhook.json b/captainhook.json
new file mode 100644
index 0000000..248e361
--- /dev/null
+++ b/captainhook.json
@@ -0,0 +1,33 @@
+{
+ "commit-msg": {
+ "enabled": true,
+ "actions": [
+ {
+ "action": "\\CaptainHook\\App\\Hook\\Message\\Action\\Regex",
+ "options": {
+ "regex": "/^(PRE|MAG|SYL|SMP)-\\d+: .+/"
+ }
+ }
+ ]
+ },
+ "pre-commit": {
+ "actions": [
+ {
+ "action": "\\CaptainHook\\App\\Hook\\Branch\\Action\\EnsureNaming",
+ "options": {
+ "regex": "/^(feature|fix|hotfix|refactor|release)\\/(PRE|MAG|SYL|SMP)-\\d+(-[a-z0-9]+)*$/"
+ }
+ },
+ {
+ "action": "vendor/bin/phpstan analyse --configuration=phpstan.neon",
+ "options": {}
+ },
+ {
+ "action": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff",
+ "options": {}
+ }
+ ],
+ "enabled": true
+ }
+}
+
diff --git a/composer.json b/composer.json
index ccb3f6c..9ed17d6 100644
--- a/composer.json
+++ b/composer.json
@@ -11,12 +11,47 @@
}
],
"require": {
- "php": ">=7.2",
+ "php": ">=8.1",
+ "captainhook/captainhook": "^5.28",
"payplug/payplug-php": "^4.0"
},
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.0",
+ "mockery/mockery": "^1.6",
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-mockery": "*",
+ "phpstan/phpstan-phpunit": "*",
+ "phpunit/phpunit": "^10.0",
+ "rector/rector": "^2.0"
+ },
"autoload": {
"psr-4": {
- "Payplug\\PluginCore\\": "src/"
+ "PayplugPluginCore\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "PayplugPluginCore\\Tests\\": "tests/"
}
+ },
+ "scripts": {
+ "post-install-cmd": "vendor/bin/captainhook install --force --skip-existing",
+ "post-update-cmd": "vendor/bin/captainhook install --force --skip-existing",
+ "test": "vendor/bin/phpunit tests",
+ "test-unit": "vendor/bin/phpunit tests --group unit",
+ "test-inte": "vendor/bin/phpunit tests --group integration",
+ "stan": "vendor/bin/phpstan analyse --configuration=phpstan.neon",
+ "cs-lint": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --dry-run --diff",
+ "cs-fix": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php",
+ "rector": "vendor/bin/rector process",
+ "rector-dry": "vendor/bin/rector process --dry-run",
+ "release": [
+ "rm -rf payplug-core && mkdir payplug-core && cp -r src payplug-core/ && cp -r tests payplug-core/",
+ "vendor/bin/rector process --config rector.php",
+ "php scripts/generate-release-composer.php"
+ ]
+ },
+ "config": {
+ "sort-packages": true
}
}
diff --git a/composer.lock b/composer.lock
index 014dacf..5f30683 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,148 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "fa8942fa712eabaed6cf93cca7cd288a",
+ "content-hash": "ddd39cd74169a138281142f4e87ebe51",
"packages": [
+ {
+ "name": "captainhook/captainhook",
+ "version": "5.29.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/captainhook-git/captainhook.git",
+ "reference": "805d44b0de8ed143f4acfb8c6bcb788cdbc42963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/captainhook-git/captainhook/zipball/805d44b0de8ed143f4acfb8c6bcb788cdbc42963",
+ "reference": "805d44b0de8ed143f4acfb8c6bcb788cdbc42963",
+ "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.29.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/sebastianfeldmann",
+ "type": "github"
+ }
+ ],
+ "time": "2026-03-25T19:32:25+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": "payplug/payplug-php",
"version": "4.0.0",
@@ -56,17 +196,4890 @@
"source": "https://github.com/payplug/payplug-php/tree/4.0.0"
},
"time": "2015-05-06T00:00:00+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
+ },
+ {
+ "name": "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": "symfony/console",
+ "version": "v6.4.39",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "c132f1215fe4aa45b70173cc00ce9a755dd31ec5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/c132f1215fe4aa45b70173cc00ce9a755dd31ec5",
+ "reference": "c132f1215fe4aa45b70173cc00ce9a755dd31ec5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "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"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<5.4",
+ "symfony/dotenv": "<5.4",
+ "symfony/event-dispatcher": "<5.4",
+ "symfony/lock": "<5.4",
+ "symfony/process": "<5.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^5.4|^6.0|^7.0",
+ "symfony/dependency-injection": "^5.4|^6.0|^7.0",
+ "symfony/event-dispatcher": "^5.4|^6.0|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/lock": "^5.4|^6.0|^7.0",
+ "symfony/messenger": "^5.4|^6.0|^7.0",
+ "symfony/process": "^5.4|^6.0|^7.0",
+ "symfony/stopwatch": "^5.4|^6.0|^7.0",
+ "symfony/var-dumper": "^5.4|^6.0|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v6.4.39"
+ },
+ "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-05-12T06:50:03+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b",
+ "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.7-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-13T15:52:40+00:00"
+ },
+ {
+ "name": "symfony/filesystem",
+ "version": "v6.4.39",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "c507b077756b4e3e09adbbe7975fac81cd3722ca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/c507b077756b4e3e09adbbe7975fac81cd3722ca",
+ "reference": "c507b077756b4e3e09adbbe7975fac81cd3722ca",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
+ },
+ "require-dev": {
+ "symfony/process": "^5.4|^6.4|^7.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/v6.4.39"
+ },
+ "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-05-07T13:11:42+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.37.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "141046a8f9477948ff284fa65be2095baafb94f2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2",
+ "reference": "141046a8f9477948ff284fa65be2095baafb94f2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-10T16:19:22+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.37.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e",
+ "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-26T13:13:48+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.37.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.37.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315",
+ "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315",
+ "shasum": ""
+ },
+ "require": {
+ "ext-iconv": "*",
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-10T17:25:58+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v6.4.39",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "6c93071cb8c91dce5a41960d125e019e64ef6cb5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/6c93071cb8c91dce5a41960d125e019e64ef6cb5",
+ "reference": "6c93071cb8c91dce5a41960d125e019e64ef6cb5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Executes commands in sub-processes",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v6.4.39"
+ },
+ "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-05-11T16:53:15+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a",
+ "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.7.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-03-28T09:44:51+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v6.4.39",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "62e3c927de664edadb5bef260987eb047a17a113"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/62e3c927de664edadb5bef260987eb047a17a113",
+ "reference": "62e3c927de664edadb5bef260987eb047a17a113",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/http-client": "^5.4|^6.0|^7.0",
+ "symfony/intl": "^6.2|^7.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^5.4|^6.0|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v6.4.39"
+ },
+ "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-05-12T11:44:19+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "clue/ndjson-react",
+ "version": "v1.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/clue/reactphp-ndjson.git",
+ "reference": "392dc165fce93b5bb5c637b67e59619223c931b0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0",
+ "reference": "392dc165fce93b5bb5c637b67e59619223c931b0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "react/stream": "^1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35",
+ "react/event-loop": "^1.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Clue\\React\\NDJson\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering"
+ }
+ ],
+ "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.",
+ "homepage": "https://github.com/clue/reactphp-ndjson",
+ "keywords": [
+ "NDJSON",
+ "json",
+ "jsonlines",
+ "newline",
+ "reactphp",
+ "streaming"
+ ],
+ "support": {
+ "issues": "https://github.com/clue/reactphp-ndjson/issues",
+ "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://clue.engineering/support",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2022-12-23T10:58:28+00:00"
+ },
+ {
+ "name": "composer/pcre",
+ "version": "3.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<1.11.10"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.12 || ^2",
+ "phpstan/phpstan-strict-rules": "^1 || ^2",
+ "phpunit/phpunit": "^8 || ^9"
+ },
+ "type": "library",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Pcre\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "PCRE wrapping library that offers type-safe preg_* replacements.",
+ "keywords": [
+ "PCRE",
+ "preg",
+ "regex",
+ "regular expression"
+ ],
+ "support": {
+ "issues": "https://github.com/composer/pcre/issues",
+ "source": "https://github.com/composer/pcre/tree/3.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-12T16:29:46+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "3.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95",
+ "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.11",
+ "symfony/phpunit-bridge": "^3 || ^7"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Semver\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl"
+ }
+ ],
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "keywords": [
+ "semantic",
+ "semver",
+ "validation",
+ "versioning"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/tree/3.4.4"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-20T19:15:30+00:00"
+ },
+ {
+ "name": "composer/xdebug-handler",
+ "version": "3.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/xdebug-handler.git",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "shasum": ""
+ },
+ "require": {
+ "composer/pcre": "^1 || ^2 || ^3",
+ "php": "^7.2.5 || ^8.0",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Composer\\XdebugHandler\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "John Stevenson",
+ "email": "john-stevenson@blueyonder.co.uk"
+ }
+ ],
+ "description": "Restarts a process without Xdebug.",
+ "keywords": [
+ "Xdebug",
+ "performance"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues",
+ "source": "https://github.com/composer/xdebug-handler/tree/3.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-06T16:37:16+00:00"
+ },
+ {
+ "name": "ergebnis/agent-detector",
+ "version": "1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ergebnis/agent-detector.git",
+ "reference": "e211f17928c8b95a51e06040792d57f5462fb271"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ergebnis/agent-detector/zipball/e211f17928c8b95a51e06040792d57f5462fb271",
+ "reference": "e211f17928c8b95a51e06040792d57f5462fb271",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0"
+ },
+ "require-dev": {
+ "ergebnis/composer-normalize": "^2.51.0",
+ "ergebnis/license": "^2.7.0",
+ "ergebnis/php-cs-fixer-config": "^6.60.2",
+ "ergebnis/phpstan-rules": "^2.13.1",
+ "ergebnis/phpunit-slow-test-detector": "^2.24.0",
+ "ergebnis/rector-rules": "^1.18.1",
+ "fakerphp/faker": "^1.24.1",
+ "infection/infection": "^0.26.6",
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^2.1.54",
+ "phpstan/phpstan-deprecation-rules": "^2.0.4",
+ "phpstan/phpstan-phpunit": "^2.0.16",
+ "phpstan/phpstan-strict-rules": "^2.0.10",
+ "phpunit/phpunit": "^9.6.34",
+ "rector/rector": "^2.4.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.2-dev"
+ },
+ "composer-normalize": {
+ "indent-size": 2,
+ "indent-style": "space"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Ergebnis\\AgentDetector\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Andreas Möller",
+ "email": "am@localheinz.com",
+ "homepage": "https://localheinz.com"
+ }
+ ],
+ "description": "Provides a detector for detecting the presence of an agent.",
+ "homepage": "https://github.com/ergebnis/agent-detector",
+ "support": {
+ "issues": "https://github.com/ergebnis/agent-detector/issues",
+ "security": "https://github.com/ergebnis/agent-detector/blob/main/.github/SECURITY.md",
+ "source": "https://github.com/ergebnis/agent-detector"
+ },
+ "time": "2026-05-07T08:19:07+00:00"
+ },
+ {
+ "name": "evenement/evenement",
+ "version": "v3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/igorw/evenement.git",
+ "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc",
+ "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9 || ^6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Evenement\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch"
+ }
+ ],
+ "description": "Événement is a very simple event dispatching library for PHP",
+ "keywords": [
+ "event-dispatcher",
+ "event-emitter"
+ ],
+ "support": {
+ "issues": "https://github.com/igorw/evenement/issues",
+ "source": "https://github.com/igorw/evenement/tree/v3.0.2"
+ },
+ "time": "2023-08-08T05:53:35+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": "friendsofphp/php-cs-fixer",
+ "version": "v3.95.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
+ "reference": "a28d88a5e172b27e78d0816992b15a9df3da20f1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a28d88a5e172b27e78d0816992b15a9df3da20f1",
+ "reference": "a28d88a5e172b27e78d0816992b15a9df3da20f1",
+ "shasum": ""
+ },
+ "require": {
+ "clue/ndjson-react": "^1.3",
+ "composer/semver": "^3.4",
+ "composer/xdebug-handler": "^3.0.5",
+ "ergebnis/agent-detector": "^1.1.1",
+ "ext-filter": "*",
+ "ext-hash": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "fidry/cpu-core-counter": "^1.3",
+ "php": "^7.4 || ^8.0",
+ "react/child-process": "^0.6.6",
+ "react/event-loop": "^1.5",
+ "react/socket": "^1.16",
+ "react/stream": "^1.4",
+ "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0",
+ "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0",
+ "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
+ "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
+ "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
+ "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.33",
+ "symfony/polyfill-php80": "^1.33",
+ "symfony/polyfill-php81": "^1.33",
+ "symfony/polyfill-php84": "^1.33",
+ "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0",
+ "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "facile-it/paraunit": "^1.3.1 || ^2.11.0",
+ "infection/infection": "^0.32.7",
+ "justinrainbow/json-schema": "^6.8.0",
+ "keradus/cli-executor": "^2.3",
+ "mikey179/vfsstream": "^1.6.12",
+ "php-coveralls/php-coveralls": "^2.9.1",
+ "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.8",
+ "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.8",
+ "phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.55",
+ "symfony/polyfill-php85": "^1.33",
+ "symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.8",
+ "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.8"
+ },
+ "suggest": {
+ "ext-dom": "For handling output formats in XML",
+ "ext-mbstring": "For handling non-UTF8 characters."
+ },
+ "bin": [
+ "php-cs-fixer"
+ ],
+ "type": "application",
+ "autoload": {
+ "psr-4": {
+ "PhpCsFixer\\": "src/"
+ },
+ "exclude-from-classmap": [
+ "src/**/Internal/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Dariusz Rumiński",
+ "email": "dariusz.ruminski@gmail.com"
+ }
+ ],
+ "description": "A tool to automatically fix PHP code style",
+ "keywords": [
+ "Static code analysis",
+ "fixer",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
+ "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/keradus",
+ "type": "github"
+ }
+ ],
+ "time": "2026-05-15T09:20:44+00:00"
+ },
+ {
+ "name": "hamcrest/hamcrest-php",
+ "version": "v2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hamcrest/hamcrest-php.git",
+ "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
+ "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "replace": {
+ "cordoval/hamcrest-php": "*",
+ "davedevelopment/hamcrest-php": "*",
+ "kodova/hamcrest-php": "*"
+ },
+ "require-dev": {
+ "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "hamcrest"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "This is the PHP port of Hamcrest Matchers",
+ "keywords": [
+ "test"
+ ],
+ "support": {
+ "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+ "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1"
+ },
+ "time": "2025-04-30T06:54:44+00:00"
+ },
+ {
+ "name": "mockery/mockery",
+ "version": "1.6.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mockery/mockery.git",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "shasum": ""
+ },
+ "require": {
+ "hamcrest/hamcrest-php": "^2.0.1",
+ "lib-pcre": ">=7.0",
+ "php": ">=7.3"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5 || ^9.6.17",
+ "symplify/easy-coding-standard": "^12.1.14"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "library/helpers.php",
+ "library/Mockery.php"
+ ],
+ "psr-4": {
+ "Mockery\\": "library/Mockery"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Pádraic Brady",
+ "email": "padraic.brady@gmail.com",
+ "homepage": "https://github.com/padraic",
+ "role": "Author"
+ },
+ {
+ "name": "Dave Marshall",
+ "email": "dave.marshall@atstsolutions.co.uk",
+ "homepage": "https://davedevelopment.co.uk",
+ "role": "Developer"
+ },
+ {
+ "name": "Nathanael Esayeas",
+ "email": "nathanael.esayeas@protonmail.com",
+ "homepage": "https://github.com/ghostwriter",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Mockery is a simple yet flexible PHP mock object framework",
+ "homepage": "https://github.com/mockery/mockery",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "library",
+ "mock",
+ "mock objects",
+ "mockery",
+ "stub",
+ "test",
+ "test double",
+ "testing"
+ ],
+ "support": {
+ "docs": "https://docs.mockery.io/",
+ "issues": "https://github.com/mockery/mockery/issues",
+ "rss": "https://github.com/mockery/mockery/releases.atom",
+ "security": "https://github.com/mockery/mockery/security/advisories",
+ "source": "https://github.com/mockery/mockery"
+ },
+ "time": "2024-05-16T03:13:13+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": "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": "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": "phpstan/phpstan",
+ "version": "2.1.55",
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9eaac3826ed5e9b8427350a43cac825eeca3f566",
+ "reference": "9eaac3826ed5e9b8427350a43cac825eeca3f566",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "time": "2026-05-18T11:57:34+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-mockery",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-mockery.git",
+ "reference": "89a949d0ac64298e88b7c7fa00caee565c198394"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-mockery/zipball/89a949d0ac64298e88b7c7fa00caee565c198394",
+ "reference": "89a949d0ac64298e88b7c7fa00caee565c198394",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.6.11",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^9.6"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan Mockery extension",
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-mockery/issues",
+ "source": "https://github.com/phpstan/phpstan-mockery/tree/2.0.0"
+ },
+ "time": "2024-10-14T03:18:12+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-phpunit",
+ "version": "2.0.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-phpunit.git",
+ "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6ab598e1bc106e6827fd346ae4a12b4a5d634c32",
+ "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.1.32"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<7.0"
+ },
+ "require-dev": {
+ "nikic/php-parser": "^5",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-deprecation-rules": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^9.6"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon",
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPUnit extensions and rules for PHPStan",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
+ "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.16"
+ },
+ "time": "2026-02-14T09:05:21+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "10.1.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "7e308268858ed6baedc8704a304727d20bc07c77"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77",
+ "reference": "7e308268858ed6baedc8704a304727d20bc07c77",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.19.1 || ^5.1.0",
+ "php": ">=8.1",
+ "phpunit/php-file-iterator": "^4.1.0",
+ "phpunit/php-text-template": "^3.0.1",
+ "sebastian/code-unit-reverse-lookup": "^3.0.0",
+ "sebastian/complexity": "^3.2.0",
+ "sebastian/environment": "^6.1.0",
+ "sebastian/lines-of-code": "^2.0.2",
+ "sebastian/version": "^4.0.1",
+ "theseer/tokenizer": "^1.2.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.1"
+ },
+ "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": "10.1.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/10.1.16"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-22T04:31:57+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "4.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "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/4.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-31T06:24:48+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^10.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "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": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:56:09+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748",
+ "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "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/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-31T14:07:24+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d",
+ "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.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",
+ "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",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:57:52+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "10.5.63",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "33198268dad71e926626b618f3ec3966661e4d90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33198268dad71e926626b618f3ec3966661e4d90",
+ "reference": "33198268dad71e926626b618f3ec3966661e4d90",
+ "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.1",
+ "phpunit/php-code-coverage": "^10.1.16",
+ "phpunit/php-file-iterator": "^4.1.0",
+ "phpunit/php-invoker": "^4.0.0",
+ "phpunit/php-text-template": "^3.0.1",
+ "phpunit/php-timer": "^6.0.0",
+ "sebastian/cli-parser": "^2.0.1",
+ "sebastian/code-unit": "^2.0.0",
+ "sebastian/comparator": "^5.0.5",
+ "sebastian/diff": "^5.1.1",
+ "sebastian/environment": "^6.1.0",
+ "sebastian/exporter": "^5.1.4",
+ "sebastian/global-state": "^6.0.2",
+ "sebastian/object-enumerator": "^5.0.0",
+ "sebastian/recursion-context": "^5.0.1",
+ "sebastian/type": "^4.0.0",
+ "sebastian/version": "^4.0.1"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "10.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/10.5.63"
+ },
+ "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:48:37+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+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": "react/cache",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/cache.git",
+ "reference": "d47c472b64aa5608225f47965a484b75c7817d5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b",
+ "reference": "d47c472b64aa5608225f47965a484b75c7817d5b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/promise": "^3.0 || ^2.0 || ^1.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async, Promise-based cache interface for ReactPHP",
+ "keywords": [
+ "cache",
+ "caching",
+ "promise",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/cache/issues",
+ "source": "https://github.com/reactphp/cache/tree/v1.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2022-11-30T15:59:55+00:00"
+ },
+ {
+ "name": "react/child-process",
+ "version": "v0.6.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/child-process.git",
+ "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/child-process/zipball/970f0e71945556422ee4570ccbabaedc3cf04ad3",
+ "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/event-loop": "^1.2",
+ "react/stream": "^1.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/socket": "^1.16",
+ "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\ChildProcess\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Event-driven library for executing child processes with ReactPHP.",
+ "keywords": [
+ "event-driven",
+ "process",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/child-process/issues",
+ "source": "https://github.com/reactphp/child-process/tree/v0.6.7"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-12-23T15:25:20+00:00"
+ },
+ {
+ "name": "react/dns",
+ "version": "v1.14.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/dns.git",
+ "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3",
+ "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/cache": "^1.0 || ^0.6 || ^0.5",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.2 || ^2.7 || ^1.2.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/async": "^4.3 || ^3 || ^2",
+ "react/promise-timer": "^1.11"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Dns\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async DNS resolver for ReactPHP",
+ "keywords": [
+ "async",
+ "dns",
+ "dns-resolver",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/dns/issues",
+ "source": "https://github.com/reactphp/dns/tree/v1.14.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-11-18T19:34:28+00:00"
+ },
+ {
+ "name": "react/event-loop",
+ "version": "v1.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/event-loop.git",
+ "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a",
+ "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "suggest": {
+ "ext-pcntl": "For signal handling support when using the StreamSelectLoop"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\EventLoop\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+ "keywords": [
+ "asynchronous",
+ "event-loop"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/event-loop/issues",
+ "source": "https://github.com/reactphp/event-loop/tree/v1.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-11-17T20:46:25+00:00"
+ },
+ {
+ "name": "react/promise",
+ "version": "v3.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise.git",
+ "reference": "23444f53a813a3296c1368bb104793ce8d88f04a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a",
+ "reference": "23444f53a813a3296c1368bb104793ce8d88f04a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "1.12.28 || 1.4.10",
+ "phpunit/phpunit": "^9.6 || ^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "keywords": [
+ "promise",
+ "promises"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/promise/issues",
+ "source": "https://github.com/reactphp/promise/tree/v3.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-08-19T18:57:03+00:00"
+ },
+ {
+ "name": "react/socket",
+ "version": "v1.17.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/socket.git",
+ "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08",
+ "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/dns": "^1.13",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.2 || ^2.6 || ^1.2.1",
+ "react/stream": "^1.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/async": "^4.3 || ^3.3 || ^2",
+ "react/promise-stream": "^1.4",
+ "react/promise-timer": "^1.11"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Socket\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+ "keywords": [
+ "Connection",
+ "Socket",
+ "async",
+ "reactphp",
+ "stream"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/socket/issues",
+ "source": "https://github.com/reactphp/socket/tree/v1.17.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-11-19T20:47:34+00:00"
+ },
+ {
+ "name": "react/stream",
+ "version": "v1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/stream.git",
+ "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+ "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.8",
+ "react/event-loop": "^1.2"
+ },
+ "require-dev": {
+ "clue/stream-filter": "~1.2",
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Stream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+ "keywords": [
+ "event-driven",
+ "io",
+ "non-blocking",
+ "pipe",
+ "reactphp",
+ "readable",
+ "stream",
+ "writable"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/stream/issues",
+ "source": "https://github.com/reactphp/stream/tree/v1.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2024-06-11T12:45:25+00:00"
+ },
+ {
+ "name": "rector/rector",
+ "version": "2.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/rectorphp/rector.git",
+ "reference": "4661c582a20f03df585d2e3fdc4af1b83d67a091"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/rectorphp/rector/zipball/4661c582a20f03df585d2e3fdc4af1b83d67a091",
+ "reference": "4661c582a20f03df585d2e3fdc4af1b83d67a091",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0",
+ "phpstan/phpstan": "^2.1.48"
+ },
+ "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.4.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/tomasvotruba",
+ "type": "github"
+ }
+ ],
+ "time": "2026-05-20T19:30:21+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084",
+ "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.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/2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:12:49+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "a81fee9eef0b7a76af11d121767abc44c104e503"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503",
+ "reference": "a81fee9eef0b7a76af11d121767abc44c104e503",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.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",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:58:43+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "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",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:59:15+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "5.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d",
+ "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.1",
+ "sebastian/diff": "^5.0",
+ "sebastian/exporter": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "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/5.0.5"
+ },
+ "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:25:16+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "68ff824baeae169ec9f2137158ee529584553799"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
+ "reference": "68ff824baeae169ec9f2137158ee529584553799",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.2-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/3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-12-21T08:37:17+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "5.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0",
+ "symfony/process": "^6.4"
+ },
+ "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"
+ },
+ {
+ "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/5.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:15:17+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "6.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
+ "reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.1-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/6.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-23T08:47:14+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "5.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "0735b90f4da94969541dac1da743446e276defa6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6",
+ "reference": "0735b90f4da94969541dac1da743446e276defa6",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.1",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.5"
+ },
+ "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"
+ },
+ {
+ "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/5.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-09-24T06:09:11+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
+ "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "sebastian/object-reflector": "^3.0",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^10.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": "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/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:19:19+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
+ "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.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/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-12-21T08:38:20+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "sebastian/object-reflector": "^3.0",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:08:32+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "24ed13d98130f0e7122df55d06c5c4942a577957"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957",
+ "reference": "24ed13d98130f0e7122df55d06c5c4942a577957",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:06:18+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "5.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a",
+ "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-10T07:50:56+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "462699a16464c3944eefc02ebdd77882bd3925bf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf",
+ "reference": "462699a16464c3944eefc02ebdd77882bd3925bf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:10:45+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "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 that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-07T11:34:05+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v6.4.37",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "2e3bf817ba9347341ab15926700fb6320367c0e1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2e3bf817ba9347341ab15926700fb6320367c0e1",
+ "reference": "2e3bf817ba9347341ab15926700fb6320367c0e1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/event-dispatcher-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<5.4",
+ "symfony/service-contracts": "<2.5"
+ },
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0",
+ "symfony/event-dispatcher-implementation": "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/error-handler": "^5.4|^6.0|^7.0",
+ "symfony/expression-language": "^5.4|^6.0|^7.0",
+ "symfony/http-foundation": "^5.4|^6.0|^7.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/stopwatch": "^5.4|^6.0|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\EventDispatcher\\": ""
+ },
+ "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 tools that allow your application components to communicate with each other by dispatching events and listening to them",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.37"
+ },
+ "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-04-13T14:11:12+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v3.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32",
+ "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/event-dispatcher": "^1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\EventDispatcher\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to dispatching event",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-01-05T13:30:16+00:00"
+ },
+ {
+ "name": "symfony/finder",
+ "version": "v6.4.34",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "9590e86be1d1c57bfbb16d0dd040345378c20896"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/9590e86be1d1c57bfbb16d0dd040345378c20896",
+ "reference": "9590e86be1d1c57bfbb16d0dd040345378c20896",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "symfony/filesystem": "^6.0|^7.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/v6.4.34"
+ },
+ "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-28T15:16:37+00:00"
+ },
+ {
+ "name": "symfony/options-resolver",
+ "version": "v6.4.30",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/options-resolver.git",
+ "reference": "eeaa8cabe54c7b3516938c72a4a161c0cc80a34f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/eeaa8cabe54c7b3516938c72a4a161c0cc80a34f",
+ "reference": "eeaa8cabe54c7b3516938c72a4a161c0cc80a34f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\OptionsResolver\\": ""
+ },
+ "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 an improved replacement for the array_replace PHP function",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "config",
+ "configuration",
+ "options"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/options-resolver/tree/v6.4.30"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-11-12T13:06:53+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.37.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
+ "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-10T16:19:22+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php81",
+ "version": "v1.37.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php81.git",
+ "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
+ "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php81\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php81/tree/v1.37.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php84",
+ "version": "v1.37.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php84.git",
+ "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/88486db2c389b290bf87ff1de7ebc1e13e42bb06",
+ "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php84\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-10T18:47:49+00:00"
+ },
+ {
+ "name": "symfony/stopwatch",
+ "version": "v6.4.24",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/stopwatch.git",
+ "reference": "b67e94e06a05d9572c2fa354483b3e13e3cb1898"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b67e94e06a05d9572c2fa354483b3e13e3cb1898",
+ "reference": "b67e94e06a05d9572c2fa354483b3e13e3cb1898",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Stopwatch\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides a way to profile code",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/stopwatch/tree/v6.4.24"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-10T08:14: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"
}
],
- "packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": ">=7.2"
+ "php": ">=8.1"
},
- "platform-dev": [],
- "plugin-api-version": "2.1.0"
+ "platform-dev": {},
+ "plugin-api-version": "2.9.0"
}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..d089c82
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,16 @@
+services:
+ php:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ volumes:
+ - .:/app
+ - composer-cache:/root/.composer/cache
+ working_dir: /app
+ environment:
+ XDEBUG_MODE: "${XDEBUG_MODE:-off}"
+ extra_hosts:
+ - "host.docker.internal:host-gateway"
+
+volumes:
+ composer-cache:
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..bd382ff
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,10 @@
+includes:
+ - vendor/phpstan/phpstan-mockery/extension.neon
+ - vendor/phpstan/phpstan-phpunit/extension.neon
+
+parameters:
+ level: 8
+ phpVersion: 80400
+ paths:
+ - src
+ - tests
diff --git a/rector.php b/rector.php
new file mode 100644
index 0000000..04d723f
--- /dev/null
+++ b/rector.php
@@ -0,0 +1,24 @@
+withPaths([
+ __DIR__ . '/payplug-core',
+ ])
+
+ // Target PHP 7.2 — Rector applies all downgrade rules from current version down to 7.2
+ ->withDowngradeSets(php72: true)
+
+ ->withSkip([
+ __DIR__ . '/vendor',
+ ]);
diff --git a/scripts/generate-release-composer.php b/scripts/generate-release-composer.php
new file mode 100644
index 0000000..72c0100
--- /dev/null
+++ b/scripts/generate-release-composer.php
@@ -0,0 +1,74 @@
+getMessage() . "\n");
+ exit(1);
+}
+
+// Downgrade PHP requirement
+$data['require']['php'] = '^7.2';
+
+// Remove dev-only tools from require and scripts
+unset($data['require']['captainhook/captainhook'], $data['scripts']);
+
+// Replace require-dev with PHP 7.2-compatible test dependencies
+// (phpunit ^11 requires PHP >=8.2; mockery ^1.6 requires PHP >=7.3)
+$data['require-dev'] = [
+ 'phpunit/phpunit' => '^8.5', // PHPUnit 8.x requires PHP >=7.2
+ 'mockery/mockery' => '^1.3', // Mockery 1.3.x requires PHP >=5.4
+];
+
+// Fix autoload: src/ is now a proper subdirectory of the package root.
+// The Tests\ prefix is more specific and must be declared before PayplugPluginCore\ so that
+// PSR-4 resolves test classes to tests/ regardless of whether composer install runs with --no-dev.
+$data['autoload']['psr-4'] = [
+ 'PayplugPluginCore\\Tests\\' => 'tests/',
+ 'PayplugPluginCore\\' => 'src/',
+];
+
+// Mirror in autoload-dev for tools that rely on it
+// $data['autoload-dev'] = ['psr-4' => ['PayplugPluginCore\\Tests\\' => 'tests/']];
+
+$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n";
+
+$bytesWritten = file_put_contents($target, $json);
+
+if ($bytesWritten === false || $bytesWritten < strlen($json)) {
+ fwrite(STDERR, "Error: Failed to write composer.json to {$target}\n");
+ exit(1);
+}
+
+echo "Generated {$target} (PHP ^7.2)\n";
diff --git a/src/Actions/PaymentAction.php b/src/Actions/PaymentAction.php
new file mode 100644
index 0000000..71904ab
--- /dev/null
+++ b/src/Actions/PaymentAction.php
@@ -0,0 +1,74 @@
+allowed_gateways = ['payment'];
+ $this->allowed_services = ['api'];
+ }
+
+ /**
+ * @param PaymentInputDTO $payment_inputDTO
+ *
+ * @return ?PaymentOutputDTO
+ *@throws \Exception
+ *
+ */
+ public function createAction(PaymentInputDTO $payment_inputDTO): ?PaymentOutputDTO
+ {
+ // todo: add a validator to check if the given paymentDTO is usable
+
+ if (null === $payment_inputDTO->getPaymentMethod()) {
+ throw new \Exception('Invalid parameter, payment method is required.');
+ }
+
+ // get payment method from given arg
+ $payment_method = $this
+ ->get_payment_gateway()
+ ->load($payment_inputDTO->getPaymentMethod());
+
+ // get payment tab appropriate or return error if need to
+ $formated_attributes = $payment_method->formatPaymentAttributes($payment_inputDTO);
+
+ // load api service with given then return the fallback
+ /** @var Api $api_service */
+ $api_service = $this->get_service('api');
+ $api = $api_service->load((string) $payment_inputDTO->getApiBearer());
+ $resource = $api->createPaymentResource($formated_attributes);
+
+ return PaymentOutputDTO::create($resource);
+ }
+
+ /**
+ * @description
+ */
+ public function abortAction(): void
+ {
+ }
+
+ /**
+ * @description
+ */
+ public function captureAction(): void
+ {
+ }
+
+ /**
+ * @description
+ */
+ public function refundAction(): void
+ {
+ }
+}
diff --git a/src/Gateways/AbstractPaymentGateway.php b/src/Gateways/AbstractPaymentGateway.php
new file mode 100644
index 0000000..acfb576
--- /dev/null
+++ b/src/Gateways/AbstractPaymentGateway.php
@@ -0,0 +1,45 @@
+ */
+ protected array $expected_context;
+
+ /**
+ * @return array
+ * @throws \Exception
+ */
+ abstract public function formatPaymentAttributes(PaymentInputDTO $payment_inputDTO): array;
+
+ /**
+ * @return array
+ */
+ public function getDefaultAttributeFromDTO(PaymentInputDTO $payment_inputDTO): array
+ {
+ $customer = $payment_inputDTO->getCustomer() ?? [];
+ $urls = $payment_inputDTO->getUrls() ?? [];
+
+ return [
+ 'amount' => $payment_inputDTO->getAmount(),
+ 'currency' => $payment_inputDTO->getCurrencyIsoCode(),
+ 'billing' => $customer['billing'],
+ 'shipping' => $customer['shipping'] + ['delivery_type' => 'BILLING'],
+ 'hosted_payment' => [
+ 'return_url' => $urls['return'],
+ 'cancel_url' => $urls['cancel'],
+ ],
+ 'notification_url' => $urls['notification'],
+ 'metadata' => $payment_inputDTO->getMetadata(),
+ 'allow_save_card' => false,
+ 'force_3ds' => false,
+ ];
+ }
+}
diff --git a/src/Gateways/Payment/EmailLinkPaymentGateway.php b/src/Gateways/Payment/EmailLinkPaymentGateway.php
new file mode 100644
index 0000000..8a97e36
--- /dev/null
+++ b/src/Gateways/Payment/EmailLinkPaymentGateway.php
@@ -0,0 +1,48 @@
+id = 'email_link';
+ $this->expected_context = [];
+ }
+
+ /**
+ * @param PaymentInputDTO $payment_inputDTO
+ * @return array
+ * @throws \Exception
+ */
+ public function formatPaymentAttributes(PaymentInputDTO $payment_inputDTO): array
+ {
+ $attributes = $this->getDefaultAttributeFromDTO($payment_inputDTO);
+ if (empty($attributes)) {
+ throw new \Exception('Can\'t generate default payment attributes');
+ }
+
+ // todo : Set validator to check this point
+ // Vérification des clés attendues dans $context
+ $context = $payment_inputDTO->getContext() ?? [];
+ foreach ($this->expected_context as $key) {
+ if (!\array_key_exists($key, $context)) {
+ throw new \Exception('Resource attribe can\'t be formated, excepted parameter " ' . $key . '" is missing.');
+ }
+ }
+
+ $attributes['billing']['landline_phone_number'] = $attributes['billing']['landline_phone_number'] ?: $attributes['shipping']['landline_phone_number'];
+ $attributes['billing']['mobile_phone_number'] = $attributes['billing']['mobile_phone_number'] ?: $attributes['shipping']['mobile_phone_number'];
+ $attributes['allow_save_card'] = false;
+
+ $attributes['hosted_payment']['sent_by'] = 'EMAIL';
+ unset($attributes['hosted_payment']['cancel_url'], $attributes['hosted_payment']['return_url']);
+
+ return $attributes;
+ }
+}
diff --git a/src/Gateways/Payment/StandardPaymentGateway.php b/src/Gateways/Payment/StandardPaymentGateway.php
new file mode 100644
index 0000000..1609f36
--- /dev/null
+++ b/src/Gateways/Payment/StandardPaymentGateway.php
@@ -0,0 +1,63 @@
+id = 'standard';
+ $this->expected_context = [
+ 'is_deferred',
+ 'is_integrated',
+ 'is_guest',
+ ];
+ }
+
+ /**
+ * @param PaymentInputDTO $payment_inputDTO
+ * @return array
+ * @throws \Exception
+ */
+ public function formatPaymentAttributes(PaymentInputDTO $payment_inputDTO): array
+ {
+ $attributes = $this->getDefaultAttributeFromDTO($payment_inputDTO);
+ if (empty($attributes)) {
+ throw new \Exception('Can\'t generate default payment attributes');
+ }
+
+ // todo : Set validator to check this point
+ // Vérification des clés attendues dans $context
+ $context = $payment_inputDTO->getContext() ?? [];
+
+ foreach ($this->expected_context as $key) {
+ if (!\array_key_exists($key, $context)) {
+ //@TODO: Create Exception System for the Library to avoid trigerring basic Exception all the time
+ throw new \Exception('Resource attribe can\'t be formated, excepted parameter " ' . $key . '" is missing.');
+ }
+ }
+
+ // Update if deferred Payment is enable
+ if (isset($context['is_deferred']) && $context['is_deferred']) {
+ $attributes['authorized_amount'] = $attributes['amount'];
+ unset($attributes['amount']);
+ }
+
+ // Update if current display is integrated
+ if (isset($context['is_integrated']) && $context['is_integrated']) {
+ $attributes['integration'] = 'INTEGRATED_PAYMENT';
+ unset($attributes['hosted_payment']['cancel_url']);
+ }
+
+ // Update Payment card could be saved
+ $is_guest = isset($context['is_guest']) && $context['is_guest'];
+ $attributes['allow_save_card'] = !(bool) $is_guest;
+
+ return $attributes;
+ }
+}
diff --git a/src/Gateways/PaymentGatewayManager.php b/src/Gateways/PaymentGatewayManager.php
new file mode 100644
index 0000000..fe78bab
--- /dev/null
+++ b/src/Gateways/PaymentGatewayManager.php
@@ -0,0 +1,33 @@
+|null */
+ public ?array $customer = null; // ['billing' => [...], 'shipping' => [...], 'identifier' => ...]
+
+ /** @var array|null */
+ public ?array $urls = null; // ['return' => ..., 'cancel' => ..., 'notification' => ...]
+
+ /** @var array|null */
+ public ?array $metadata = null;
+
+ /** @var array|null */
+ public ?array $context = null;
+
+ /**
+ * @var array
+ */
+ private array $definitions = [
+ 'api_bearer' => ['type' => 'string', 'required' => true],
+ 'payment_method' => ['type' => 'string', 'required' => true],
+ 'amount' => ['type' => 'int', 'required' => true],
+ 'currency_iso_code' => ['type' => 'string', 'required' => true],
+ 'customer' => ['type' => 'array', 'required' => true],
+ 'urls' => ['type' => 'array', 'required' => true],
+ 'metadata' => ['type' => 'array', 'required' => false],
+ 'context' => ['type' => 'array', 'required' => false],
+ ];
+
+ /**
+ * @param array $props
+ * @return $this|self|null
+ * @throws Exception
+ */
+ public function hydrate(array $props): ?self
+ {
+ // todo: move this check in validators
+ foreach ($this->getDefinitions() as $key => $field) {
+ if (!$field['required']) {
+ continue;
+ }
+
+ if (!\array_key_exists($key, $props) || null === $props[$key]) {
+ $this->resetProperties();
+ throw new Exception('PaymentInputDTO can\'t be hydrated, required field is invalid.');
+ }
+ }
+
+ $this->setApiBearer((string) $props['api_bearer']);
+ $this->setPaymentMethod((string) $props['payment_method']);
+ $this->setAmount((int) $props['amount']);
+ $this->setCurrencyIsoCode((string) $props['currency_iso_code']);
+ $this->setCustomer((array) $props['customer']);
+ $this->setUrls((array) $props['urls']);
+ $this->setMetadata((array) $props['metadata']);
+ $this->setContext((array) $props['context']);
+
+ return $this;
+ }
+
+ private function resetProperties(): void
+ {
+ $this->api_bearer = null;
+ $this->payment_method = null;
+ $this->amount = null;
+ $this->currency_iso_code = null;
+ $this->customer = null;
+ $this->urls = null;
+ $this->metadata = null;
+ $this->context = null;
+ }
+
+ // Getters
+
+ public function getAmount(): ?int
+ {
+ return $this->amount;
+ }
+
+ public function getApiBearer(): ?string
+ {
+ return $this->api_bearer;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getContext(): ?array
+ {
+ return $this->context;
+ }
+
+ public function getCurrencyIsoCode(): ?string
+ {
+ return $this->currency_iso_code;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getCustomer(): ?array
+ {
+ return $this->customer;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getMetadata(): ?array
+ {
+ return $this->metadata;
+ }
+
+ public function getPaymentMethod(): ?string
+ {
+ return $this->payment_method;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getUrls(): ?array
+ {
+ return $this->urls;
+ }
+
+ /**
+ * @return array
+ */
+ public function getDefinitions(): array
+ {
+ return $this->definitions;
+ }
+
+ // Setters
+
+ public function setAmount(int $amount): void
+ {
+ $this->amount = $amount;
+ }
+
+ public function setApiBearer(string $api_bearer): void
+ {
+ $this->api_bearer = $api_bearer;
+ }
+
+ /**
+ * @param array $context
+ */
+ public function setContext(array $context): void
+ {
+ $this->context = $context;
+ }
+
+ public function setCurrencyIsoCode(string $currency_iso_code): void
+ {
+ $this->currency_iso_code = $currency_iso_code;
+ }
+
+ /**
+ * @param array $customer
+ */
+ public function setCustomer(array $customer): void
+ {
+ $this->customer = $customer;
+ }
+
+ /**
+ * @param array $metadata
+ */
+ public function setMetadata(array $metadata): void
+ {
+ $this->metadata = $metadata;
+ }
+
+ public function setPaymentMethod(string $payment_method): void
+ {
+ $this->payment_method = $payment_method;
+ }
+
+ /**
+ * @param array $urls
+ */
+ public function setUrls(array $urls): void
+ {
+ $this->urls = $urls;
+ }
+
+ /**
+ * @param array $props
+ * @return self|null
+ */
+ public static function create(array $props): ?self
+ {
+ return new self()->hydrate($props);
+ }
+}
diff --git a/src/Models/Entities/PaymentOutputDTO.php b/src/Models/Entities/PaymentOutputDTO.php
new file mode 100644
index 0000000..804a6d7
--- /dev/null
+++ b/src/Models/Entities/PaymentOutputDTO.php
@@ -0,0 +1,135 @@
+
+ */
+ private $definitions = [
+ 'result' => ['type' => 'boolean', 'required' => true],
+ 'code' => ['type' => 'string', 'required' => true],
+ 'message' => ['type' => 'string', 'required' => false],
+ 'resource' => ['type' => 'object', 'required' => false],
+ ];
+
+ /**
+ * @param array $props
+ * @return $this|self|null
+ */
+ public function hydrate(array $props): ?self
+ {
+ // todo: move this check in validators
+ foreach ($this->getDefinitions() as $key => $field) {
+ if (!$field['required']) {
+ continue;
+ }
+
+ if (!\array_key_exists($key, $props) || null === $props[$key]) {
+ $this->resetProperties();
+ throw new \Exception('PaymentOutputDTO can\'t be hydrated, required field is invalid.');
+ }
+ }
+
+ $this->setResult((bool) $props['result']);
+ $this->setCode((string) $props['code']);
+ $this->setMessage(isset($props['message']) ? (string) $props['message'] : '');
+ $this->setResource($props['resource']);
+
+ return $this;
+ }
+
+ private function resetProperties(): void
+ {
+ $this->result = null;
+ $this->code = null;
+ $this->message = null;
+ $this->resource = null;
+ }
+
+ // Getters
+
+ public function getResult(): ?bool
+ {
+ return $this->result;
+ }
+
+ public function getCode(): ?string
+ {
+ return $this->code;
+ }
+
+ public function getMessage(): ?string
+ {
+ return $this->message;
+ }
+
+ /**
+ * @return array
+ */
+ public function getDefinitions(): array
+ {
+ return $this->definitions;
+ }
+
+ public function getResource(): ?Payment
+ {
+ return $this->resource;
+ }
+
+ // Setters
+
+ public function setResult(bool $result): void
+ {
+ $this->result = $result;
+ }
+
+ public function setCode(string $code): void
+ {
+ $this->code = $code;
+ }
+
+ public function setMessage(string $message): void
+ {
+ $this->message = $message;
+ }
+
+ public function setResource(?Payment $resource): void
+ {
+ $this->resource = $resource;
+ }
+
+ /**
+ * @param array $props
+ * @return self|null
+ */
+ public static function create(array $props): ?self
+ {
+ return new self()->hydrate($props);
+ }
+}
diff --git a/src/Utilities/Services/Api.php b/src/Utilities/Services/Api.php
new file mode 100644
index 0000000..50941d6
--- /dev/null
+++ b/src/Utilities/Services/Api.php
@@ -0,0 +1,81 @@
+ $datas
+ * @return array
+ */
+ public function createPaymentResource(array $datas): array
+ {
+ try {
+ if (null === $this->payplug_api) {
+ throw new RuntimeException('API Payplug must be initialized.');
+ }
+ $response = [
+ 'code' => 200,
+ 'message' => 'OK',
+ 'resource' => Payment::create($datas, $this->payplug_api),
+ 'result' => true,
+ ];
+ } catch (\Exception $e) {
+ $response = [
+ 'code' => $e->getCode(),
+ 'message' => $e->getMessage(),
+ 'resource' => null,
+ 'result' => false,
+ ];
+ }
+
+ return $response;
+ }
+
+ public function getBearerToken(): string
+ {
+ return $this->bearer_token;
+ }
+
+ private function setBearerToken(string $bearer_token): void
+ {
+ $this->bearer_token = $bearer_token;
+ }
+
+ /**
+ * @return $this
+ * @throws \Payplug\Exception\ConfigurationException
+ */
+ public function load(string $bearer_token): self
+ {
+ $this->setBearerToken($bearer_token);
+ $this->initialize();
+
+ return $this;
+ }
+
+ /**
+ * @throws \Exception
+ */
+ private function initialize(): void
+ {
+ try {
+ $this->payplug_api = Payplug::init([
+ 'secretKey' => $this->getBearerToken(),
+ 'apiVersion' => '2019-08-06',
+ ]);
+ } catch (\Exception $e) {
+ throw new \Exception('Payplug API can\'t be initialized. Error thrown: ' . $e->getMessage());
+ }
+ }
+}
diff --git a/src/Utilities/Traits/DependenciesLoader.php b/src/Utilities/Traits/DependenciesLoader.php
new file mode 100644
index 0000000..462c524
--- /dev/null
+++ b/src/Utilities/Traits/DependenciesLoader.php
@@ -0,0 +1,66 @@
+ */
+ private array $allowed_services = [];
+
+ /** @var array */
+ private array $allowed_gateways = [];
+
+ /**
+ * @return object
+ * @throws \Exception
+ */
+ public function get_service(string $name = ''): object
+ {
+ if (empty($name)) {
+ throw new \Exception('Invalid parameter, $name given should be a non empty string.');
+ }
+
+ if (!\in_array($name, $this->allowed_services)) {
+ throw new \Exception('Given $name is not allower Services.');
+ }
+
+ $service_name = '\PayplugPluginCore\Utilities\Services\\' . str_replace('_', '', ucwords($name, '_'));
+ if (!class_exists($service_name)) {
+ throw new \Exception('Service can\'t be found.');
+ }
+
+ return new $service_name();
+ }
+
+ /**
+ * @param string $name
+ * @return object
+ * @throws \Exception
+ */
+ public function get_gateway(string $name = ''): object
+ {
+ if (empty($name)) {
+ throw new \Exception('Invalid parameter, $name given should be a non empty string.');
+ }
+
+ if (!\in_array($name, $this->allowed_gateways, true)) {
+ throw new \Exception('Given $name is not allower Gateways.');
+ }
+
+ $gateway_name = '\PayplugPluginCore\Gateways\\' . str_replace('_', '', ucwords($name, '_')) . 'Gateway';
+ if (!class_exists($gateway_name)) {
+ throw new \Exception('Gateway can\'t be found.');
+ }
+
+ return new $gateway_name();
+ }
+
+ public function get_payment_gateway(): PaymentGatewayManager
+ {
+ return new PaymentGatewayManager();
+ }
+}
diff --git a/tests/Integration/PaymentAction/createActionTest.php b/tests/Integration/PaymentAction/createActionTest.php
new file mode 100644
index 0000000..ec6a87f
--- /dev/null
+++ b/tests/Integration/PaymentAction/createActionTest.php
@@ -0,0 +1,82 @@
+action = Mockery::mock(PaymentAction::class, [])->makePartial();
+ $this->payment_api = \Mockery::mock('alias:Payplug\Payment');
+ $this->default_payment_input_DTO = PaymentInputDTOMock::get([
+ 'payment_method' => 'standard',
+ 'context' => [
+ 'is_deferred' => false,
+ 'is_integrated' => false,
+ 'is_guest' => false,
+ ],
+ ]);
+ }
+
+ public function tearDown(): void
+ {
+ Mockery::close();
+ }
+
+ public function testWhenResourceCantBeCreated(): void
+ {
+ $error_msg = 'An error occurred during the process';
+ $error_code = 500;
+ $this->payment_api
+ ->shouldReceive('create')
+ ->once()
+ ->andThrow(new \Exception($error_msg, $error_code));
+
+ $error_output_props = [
+ 'code' => $error_code,
+ 'message' => $error_msg,
+ 'result' => false,
+ 'resource' => null,
+ ];
+ $this->assertEquals(
+ PaymentOutputDTOMock::get($error_output_props),
+ $this->action->createAction($this->default_payment_input_DTO)
+ );
+ }
+
+ public function testWhenResourceIsCreated(): void
+ {
+
+ $resource = PaymentMock::getStandard();
+ $this->payment_api
+ ->shouldReceive('create')
+ ->once()
+ ->andReturn($resource);
+
+ $success_output_props = [
+ 'resource' => $resource,
+ ];
+ $payment_output_DTO = PaymentOutputDTOMock::get($success_output_props);
+ $return = $this->action->createAction($this->default_payment_input_DTO);
+
+ $this->assertEquals($payment_output_DTO, $return);
+ }
+}
diff --git a/tests/Mock/PaymentInputDTOMock.php b/tests/Mock/PaymentInputDTOMock.php
new file mode 100644
index 0000000..5c926ce
--- /dev/null
+++ b/tests/Mock/PaymentInputDTOMock.php
@@ -0,0 +1,81 @@
+|null $custom_props
+ * @throws \Exception
+ */
+ public static function get(?array $custom_props): PaymentInputDTO
+ {
+ // get default
+ $props = [
+ 'api_bearer' => 'token_bearer',
+ 'payment_method' => 'payment method',
+ 'amount' => 4242,
+ 'currency_iso_code' => 'EUR',
+ 'customer' => [
+ 'identifier' => 42,
+ 'billing' => [
+ 'title' => 'mr',
+ 'first_name' => 'John',
+ 'last_name' => 'Watson',
+ 'mobile_phone_number' => '+33612345678',
+ 'email' => 'john.watson@example.net',
+ 'address1' => '221B Baker Street',
+ 'postcode' => 'NW16XE',
+ 'city' => 'London',
+ 'country' => 'GB',
+ 'language' => 'en',
+ ],
+ 'shipping' => [
+ 'title' => 'mr',
+ 'first_name' => 'John',
+ 'last_name' => 'Watson',
+ 'mobile_phone_number' => '+33612345678',
+ 'email' => 'john.watson@example.net',
+ 'address1' => '221B Baker Street',
+ 'postcode' => 'NW16XE',
+ 'city' => 'London',
+ 'country' => 'GB',
+ 'language' => 'en',
+ ],
+ ],
+ 'urls' => [
+ 'return' => 'https://example.net/success?id=42',
+ 'cancel' => 'https://example.net/cancel?id=42',
+ 'notification' => 'https://example.net/notifications?id=42',
+ ],
+ 'metadata' => [
+ 'cart ID' => '42',
+ 'custom data' => 'lorem ipsum',
+ ],
+ 'context' => [],
+ ];
+
+ // set custom props
+ if (!empty($custom_props)) {
+ foreach ($custom_props as $prop => $value) {
+ $props[$prop] = $value;
+ }
+ }
+
+ //
+ $paymentInputDTO = new PaymentInputDTO();
+ $result = $paymentInputDTO->hydrate($props);
+ if ($result === null) {
+ throw new \RuntimeException('PaymentInputDTOMock failed to hydrate DTO.');
+ }
+
+ return $result;
+ }
+}
diff --git a/tests/Mock/PaymentMock.php b/tests/Mock/PaymentMock.php
new file mode 100644
index 0000000..fde49c1
--- /dev/null
+++ b/tests/Mock/PaymentMock.php
@@ -0,0 +1,143 @@
+> */
+ public static array $payment_parameters = [
+ 'oneclick' => [
+ 'is_paid' => true,
+ 'paid_at' => 1614949567,
+ 'is_3ds' => false,
+ 'is_live' => true,
+ 'card' => [
+ 'last4' => '0001',
+ 'country' => 'FR',
+ 'exp_year' => 2030,
+ 'exp_month' => 9,
+ 'brand' => 'CB',
+ 'id' => 'card_3EOJHyQXNCG8gZ452cUA0y',
+ 'metadata' => null,
+ ],
+ 'hosted_payment' => [
+ 'paid_at' => 1614949567,
+ ],
+ 'refundable_after' => 1614949567,
+ 'refundable_until' => 1630501567,
+ 'metadata' => [
+ 'ID Client' => 4,
+ 'ID Cart' => 17,
+ 'Website' => 'http://localhost',
+ ],
+ ],
+ ];
+
+ /**
+ * @param array $parameters
+ */
+ public static function getStandard(array $parameters = []): Payment
+ {
+ $resource = self::get($parameters);
+
+ return Payment::fromAttributes($resource);
+ }
+
+ /**
+ * @param array $parameters
+ * @return array
+ */
+ public static function get(array $parameters): array
+ {
+ $defaultConfiguration = [
+ 'id' => 'pay_azerty',
+ 'object' => 'payment',
+ 'is_live' => true,
+ 'amount' => 42420,
+ 'amount_refunded' => 0,
+ 'currency' => 'EUR',
+ 'created_at' => time(),
+ 'description' => null,
+ 'is_paid' => false,
+ 'paid_at' => null,
+ 'is_refunded' => false,
+ 'is_3ds' => null,
+ 'save_card' => false,
+ 'card' => [
+ 'last4' => null,
+ 'country' => null,
+ 'exp_year' => null,
+ 'exp_month' => null,
+ 'brand' => null,
+ 'id' => null,
+ 'metadata' => null,
+ ],
+ 'hosted_payment' => [
+ 'payment_url' => 'payment_url',
+ 'return_url' => 'return_url',
+ 'cancel_url' => 'cancel_url',
+ 'paid_at' => null,
+ 'sent_by' => null,
+ ],
+ 'notification' => [
+ 'url' => 'notification_url',
+ 'response_code' => null,
+ ],
+ 'metadata' => [
+ 'ID Client' => 1,
+ 'ID Cart' => 1,
+ 'Website' => 'http://my.localhost.com',
+ ],
+ 'failure' => null,
+ 'installment_plan_id' => null,
+ 'authorization' => null,
+ 'refundable_after' => null,
+ 'refundable_until' => null,
+ 'billing' => [
+ 'title' => null,
+ 'first_name' => 'John',
+ 'last_name' => 'Doe',
+ 'address1' => 'my address mock',
+ 'address2' => null,
+ 'company_name' => 'Mock Inc.',
+ 'postcode' => '75000',
+ 'city' => 'Paris',
+ 'state' => null,
+ 'country' => 'FR',
+ 'email' => 'jdoe@payplug.com',
+ 'mobile_phone_number' => '+33612345678',
+ 'landline_phone_number' => '+33112345678',
+ 'language' => 'fr',
+ ],
+ 'shipping' => [
+ 'title' => null,
+ 'first_name' => 'John',
+ 'last_name' => 'Doe',
+ 'address1' => 'my address mock',
+ 'address2' => null,
+ 'company_name' => 'Mock Inc.',
+ 'postcode' => '75000',
+ 'city' => 'Paris',
+ 'state' => null,
+ 'country' => 'FR',
+ 'email' => 'jdoe@payplug.com',
+ 'mobile_phone_number' => '+33612345678',
+ 'landline_phone_number' => '+33112345678',
+ 'language' => 'fr',
+ 'delivery_type' => 'BILLING',
+ ],
+ ];
+
+ if (!empty($parameters)) {
+ foreach ($parameters as $key => $value) {
+ $defaultConfiguration[$key] = $value;
+ }
+ }
+
+ return $defaultConfiguration;
+ }
+}
diff --git a/tests/Mock/PaymentOutputDTOMock.php b/tests/Mock/PaymentOutputDTOMock.php
new file mode 100644
index 0000000..0e26358
--- /dev/null
+++ b/tests/Mock/PaymentOutputDTOMock.php
@@ -0,0 +1,116 @@
+|null $custom_props
+ * @throws \Exception
+ */
+ public static function get(?array $custom_props): PaymentOutputDTO
+ {
+ // get default
+ $payment_attributes = [
+ 'id' => 'pay_id',
+ 'object' => 'payment',
+ 'is_live' => true,
+ 'amount' => 4200,
+ 'amount_refunded' => 0,
+ 'currency' => 'EUR',
+ 'created_at' => time(),
+ 'description' => null,
+ 'is_paid' => false,
+ 'paid_at' => null,
+ 'is_refunded' => false,
+ 'is_3ds' => null,
+ 'save_card' => false,
+ 'card' => [
+ 'last4' => null,
+ 'country' => null,
+ 'exp_year' => null,
+ 'exp_month' => null,
+ 'brand' => null,
+ 'id' => null,
+ 'metadata' => null,
+ ],
+ 'hosted_payment' => [
+ 'return_url' => 'https://example.net/success?id=42',
+ 'cancel_url' => 'https://example.net/cancel?id=42',
+ 'paid_at' => null,
+ 'sent_by' => null,
+ ],
+ 'notification' => [
+ 'url' => 'https://example.net/notifications?id=42',
+ 'response_code' => null,
+ ],
+ 'metadata' => [
+ 'ID Client' => 1,
+ 'ID Cart' => 1,
+ 'Website' => 'website.com',
+ ],
+ 'failure' => null,
+ 'installment_plan_id' => null,
+ 'authorization' => null,
+ 'refundable_after' => null,
+ 'refundable_until' => null,
+ 'billing' => [
+ 'title' => 'mr',
+ 'first_name' => 'John',
+ 'last_name' => 'Watson',
+ 'mobile_phone_number' => '+33612345678',
+ 'email' => 'john.watson@example.net',
+ 'address1' => '221B Baker Street',
+ 'postcode' => 'NW16XE',
+ 'city' => 'London',
+ 'country' => 'GB',
+ 'language' => 'en',
+ ],
+ 'shipping' => [
+ 'title' => 'mr',
+ 'first_name' => 'John',
+ 'last_name' => 'Watson',
+ 'mobile_phone_number' => '+33612345678',
+ 'email' => 'john.watson@example.net',
+ 'address1' => '221B Baker Street',
+ 'postcode' => 'NW16XE',
+ 'city' => 'London',
+ 'country' => 'GB',
+ 'language' => 'en',
+ 'delivery_type' => 'BILLING',
+ ],
+ ];
+ $props = [
+ 'result' => true,
+ 'code' => 200,
+ 'message' => 'OK',
+ 'resource' => Payment::fromAttributes($payment_attributes),
+ ];
+
+ // set custom props
+ if ($custom_props !== null) {
+ foreach ($custom_props as $prop => $value) {
+ $props[$prop] = $value;
+ }
+ }
+
+ //
+ $paymentOutputDTO = new PaymentOutputDTO();
+ $result = $paymentOutputDTO->hydrate($props);
+ if ($result === null) {
+ throw new \RuntimeException('PaymentOutputDTOMock failed to hydrate DTO.');
+ }
+
+ return $result;
+ }
+}
diff --git a/tests/Traits/FormatDataProvider.php b/tests/Traits/FormatDataProvider.php
new file mode 100644
index 0000000..2e8e4a1
--- /dev/null
+++ b/tests/Traits/FormatDataProvider.php
@@ -0,0 +1,120 @@
+ 'value']];
+
+ yield [null];
+ }
+
+ public function invalidIntegerFormatDataProvider(): \Generator
+ {
+ yield [null];
+
+ yield [['key' => 'value']];
+
+ yield [true];
+
+ yield ['lorem ipsum'];
+ }
+
+ public function invalidFloatFormatDataProvider(): \Generator
+ {
+ yield [null];
+
+ yield [['key' => 'value']];
+
+ yield [true];
+
+ yield ['lorem ipsum'];
+
+ yield [42];
+ }
+
+ public function invalidNumericFormatDataProvider(): \Generator
+ {
+ yield [null];
+
+ yield [['key' => 'value']];
+
+ yield [true];
+
+ yield ['lorem ipsum'];
+
+ yield ['123abc'];
+ }
+
+ public function invalidJSONFormatDataProvider(): \Generator
+ {
+ yield [''];
+
+ yield ['{"feature": \'value\'}'];
+
+ yield ['{"feature": "value", }'];
+
+ yield ['{{}}'];
+ }
+
+ public function invalidObjectFormatDataProvider(): \Generator
+ {
+ yield [42];
+
+ yield [['key' => 'value']];
+
+ yield [true];
+
+ yield ['lorem ipsum'];
+ }
+
+ public function invalidStringFormatDataProvider(): \Generator
+ {
+ yield [42];
+
+ yield [['key' => 'value']];
+
+ yield [false];
+
+ yield [null];
+ }
+
+ public function invalidEmailFormatDataProvider(): \Generator
+ {
+ yield ['@test.com'];
+
+ yield ['email@test'];
+
+ yield ['emailtest.com'];
+ }
+
+ public function invalidPhoneFormatDataProvider(): \Generator
+ {
+ yield [42];
+
+ yield ['invalid phone number'];
+
+ yield [false];
+
+ yield [null];
+ }
+}
diff --git a/tests/Traits/TestingTools.php b/tests/Traits/TestingTools.php
new file mode 100644
index 0000000..37e61cf
--- /dev/null
+++ b/tests/Traits/TestingTools.php
@@ -0,0 +1,34 @@
+{$name})) {
+ return $this->{$name};
+ }
+
+ $mock = '\\PayplugPluginCore\\Tests\\Mock\\' . str_replace('_', '', ucwords($name, '_')) . 'Mock';
+ if (!class_exists($mock)) {
+ throw new \Exception('Mock can\'t be found. Given: ' . $mock);
+ }
+
+ $this->{$name} = new $mock();
+
+ return $this->{$name};
+ }
+}
diff --git a/tests/Units/Actions/PaymentAction/createActionTest.php b/tests/Units/Actions/PaymentAction/createActionTest.php
new file mode 100644
index 0000000..b289fec
--- /dev/null
+++ b/tests/Units/Actions/PaymentAction/createActionTest.php
@@ -0,0 +1,212 @@
+ */
+ private array $payment_attributes = [];
+
+ public function setUp(): void
+ {
+ parent::setUp();
+ $this->input_dto = PaymentInputDTOMock::get([]);
+
+ $this->gateway_loader = \Mockery::mock(PaymentGatewayManager::class);
+ $this->action->shouldReceive('get_payment_gateway')
+ ->andReturn($this->gateway_loader);
+
+ $this->service_loader = \Mockery::mock('ServiceLoader');
+ $this->action->shouldReceive('get_service')
+ ->with('api')
+ ->andReturn($this->service_loader);
+
+ $this->api_service = \Mockery::mock('ApiService');
+ $this->payment_gateway = \Mockery::mock(AbstractPaymentGateway::class);
+
+ $customer = $this->input_dto->getCustomer() ?? [];
+ $urls = $this->input_dto->getUrls() ?? [];
+ $this->payment_attributes = [
+ 'amount' => $this->input_dto->getAmount(),
+ 'currency' => $this->input_dto->getCurrencyIsoCode(),
+ 'billing' => $customer['billing'] ?? null,
+ 'shipping' => ($customer['shipping'] ?? []) + ['delivery_type' => 'BILLING'],
+ 'hosted_payment' => [
+ 'return_url' => $urls['return'] ?? null,
+ 'cancel_url' => $urls['cancel'] ?? null,
+ ],
+ 'notification_url' => $urls['notification'] ?? null,
+ 'metadata' => $this->input_dto->getMetadata(),
+ 'allow_save_card' => false,
+ 'force_3ds' => false,
+ ];
+ }
+
+ public function testWhenGivenDTOIsInvalid(): void
+ {
+ $this->expectException(\TypeError::class);
+ new \ReflectionMethod($this->action, 'createAction')->invoke($this->action, null);
+ }
+
+ public function testWhenPaymentGatewayLoadingThrowsException(): void
+ {
+ $exception_msg = 'Payment Gateway can\'t be found.';
+ $this->gateway_loader
+ ->shouldReceive('load')
+ ->once()
+ ->with($this->input_dto->getPaymentMethod())
+ ->andThrow(new \Exception($exception_msg));
+
+ // Le flux doit s'arrêter avant tout appel API
+ $this->action->shouldNotReceive('get_service');
+
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage($exception_msg);
+
+ $this->action->createAction($this->input_dto);
+ }
+
+ public function testWhenContextInDTOIsMissing(): void
+ {
+ $this->gateway_loader
+ ->shouldReceive('load')
+ ->once()
+ ->with($this->input_dto->getPaymentMethod())
+ ->andReturn($this->payment_gateway);
+
+ $exception_msg = 'Resource attribe can\'t be formated, excepted parameter is missing.';
+ $this->payment_gateway
+ ->shouldReceive('formatPaymentAttributes')
+ ->once()
+ ->andThrow(new \Exception($exception_msg));
+
+ // Le flux doit s'arrêter avant tout appel API
+ $this->action->shouldNotReceive('get_service');
+
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage($exception_msg);
+
+ $this->action->createAction($this->input_dto);
+ }
+
+ public function testWhenApiServiceLoadingThrowsException(): void
+ {
+ $this->gateway_loader
+ ->shouldReceive('load')
+ ->once()
+ ->with($this->input_dto->getPaymentMethod())
+ ->andReturn($this->payment_gateway);
+
+ $this->payment_gateway
+ ->shouldReceive('formatPaymentAttributes')
+ ->once()
+ ->andReturn($this->payment_attributes);
+
+ $exception_msg = 'Payplug API can\'t be initialized.';
+ $this->service_loader
+ ->shouldReceive('load')
+ ->once()
+ ->with($this->input_dto->getApiBearer())
+ ->andThrow(new \Exception($exception_msg));
+
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage($exception_msg);
+
+ $this->action->createAction($this->input_dto);
+ }
+
+ public function testWhenPaymentResourceCantBeCreated(): void
+ {
+ $this->gateway_loader
+ ->shouldReceive('load')
+ ->once()
+ ->with($this->input_dto->getPaymentMethod())
+ ->andReturn($this->payment_gateway);
+
+ $this->payment_gateway
+ ->shouldReceive('formatPaymentAttributes')
+ ->once()
+ ->andReturn($this->payment_attributes);
+
+ $this->service_loader
+ ->shouldReceive('load')
+ ->once()
+ ->with($this->input_dto->getApiBearer())
+ ->andReturn($this->api_service);
+
+ $error_code = 500;
+ $error_msg = 'An error occurred during payment creation.';
+ $api_return = [
+ 'code' => $error_code,
+ 'message' => $error_msg,
+ 'resource' => null,
+ 'result' => false,
+ ];
+ $this->api_service
+ ->shouldReceive('createPaymentResource')
+ ->once()
+ ->andReturn($api_return);
+ $this->assertEquals(
+ PaymentOutputDTOMock::get($api_return),
+ $this->action->createAction($this->input_dto)
+ );
+ }
+
+ public function testWhenPaymentOutputDTOIsReturnWithSuccess(): void
+ {
+ $this->gateway_loader
+ ->shouldReceive('load')
+ ->once()
+ ->with($this->input_dto->getPaymentMethod())
+ ->andReturn($this->payment_gateway);
+
+ $this->payment_gateway
+ ->shouldReceive('formatPaymentAttributes')
+ ->once()
+ ->andReturn($this->payment_attributes);
+
+ $this->service_loader
+ ->shouldReceive('load')
+ ->once()
+ ->with($this->input_dto->getApiBearer())
+ ->andReturn($this->api_service);
+
+ $api_return = [
+ 'code' => 200,
+ 'message' => 'OK',
+ 'resource' => PaymentMock::getStandard(),
+ 'result' => true,
+ ];
+ $this->api_service
+ ->shouldReceive('createPaymentResource')
+ ->once()
+ ->andReturn($api_return);
+ $this->assertEquals(
+ PaymentOutputDTOMock::get([
+ 'resource' => PaymentMock::getStandard(),
+ ]),
+ $this->action->createAction($this->input_dto)
+ );
+ }
+}
diff --git a/tests/Units/Actions/PaymentAction/paymentActionBase.php b/tests/Units/Actions/PaymentAction/paymentActionBase.php
new file mode 100644
index 0000000..89e90a6
--- /dev/null
+++ b/tests/Units/Actions/PaymentAction/paymentActionBase.php
@@ -0,0 +1,24 @@
+action = Mockery::mock(PaymentAction::class, [])->makePartial();
+ }
+
+ protected function tearDown(): void
+ {
+ Mockery::close();
+ }
+}
diff --git a/tests/Units/Gateways/Payment/StandardPaymentGateway/formatPaymentAttributesTest.php b/tests/Units/Gateways/Payment/StandardPaymentGateway/formatPaymentAttributesTest.php
new file mode 100644
index 0000000..578417b
--- /dev/null
+++ b/tests/Units/Gateways/Payment/StandardPaymentGateway/formatPaymentAttributesTest.php
@@ -0,0 +1,142 @@
+expectException(\TypeError::class);
+ new \ReflectionMethod($this->gateway, 'formatPaymentAttributes')->invoke($this->gateway, null);
+ }
+
+ public function testWhenDefaultAttributesCanBeSet(): void
+ {
+ $exception_msg = 'Can\'t generate default payment attributes';
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage($exception_msg);
+ $this->gateway
+ ->shouldReceive('getDefaultAttributeFromDTO')
+ ->once()
+ ->andReturn([]);
+ $this->gateway->formatPaymentAttributes(PaymentInputDTOMock::get([]));
+ }
+
+ public static function StandardPaymentRequiredContext(): \Generator
+ {
+ yield ['is_deferred'];
+ yield ['is_integrated'];
+ yield ['is_guest'];
+ }
+ /**
+ * @dataProvider StandardPaymentRequiredContext
+ *
+ * @param mixed $key
+ */
+ public function testWhenRequiredContextIsMissing($key): void
+ {
+ $exception_msg = 'Resource attribe can\'t be formated, excepted parameter " ' . $key . '" is missing.';
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage($exception_msg);
+ $this->gateway
+ ->shouldReceive('getDefaultAttributeFromDTO')
+ ->once()
+ ->andReturn([
+ 'amount' => 4242,
+ 'currency' => 'EUR',
+ ]);
+
+ $context = [
+ 'is_deferred' => false,
+ 'is_integrated' => false,
+ 'is_guest' => false,
+ ];
+ unset($context[$key]);
+ $this->gateway->formatPaymentAttributes(PaymentInputDTOMock::get([
+ 'context' => $context,
+ ]));
+ }
+
+ /**
+ * @dataProvider StandardPaymentRequiredContext
+ *
+ * @param mixed $key
+ */
+ public function testWhenPaymentAttributesIsReturned($key): void
+ {
+ $this->gateway
+ ->shouldReceive('getDefaultAttributeFromDTO')
+ ->once()
+ ->andReturn([
+ 'amount' => 4242,
+ 'currency' => 'EUR',
+ 'hosted_payment' => [
+ 'cancel_url' => 'cancel_url',
+ ],
+ 'allow_save_card' => true,
+ ]);
+
+ $context = [
+ 'is_deferred' => false,
+ 'is_integrated' => false,
+ 'is_guest' => false,
+ ];
+ $context[$key] = true;
+ $expected = [];
+ switch ($key) {
+ case 'is_deferred':
+ $expected = [
+ 'authorized_amount' => 4242,
+ 'currency' => 'EUR',
+ 'hosted_payment' => [
+ 'cancel_url' => 'cancel_url',
+ ],
+ 'allow_save_card' => true,
+ ];
+ break;
+ case 'is_integrated':
+ $expected = [
+ 'amount' => 4242,
+ 'currency' => 'EUR',
+ 'hosted_payment' => [],
+ 'integration' => 'INTEGRATED_PAYMENT',
+ 'allow_save_card' => true,
+ ];
+ break;
+ case 'is_guest':
+ $expected = [
+ 'amount' => 4242,
+ 'currency' => 'EUR',
+ 'hosted_payment' => [
+ 'cancel_url' => 'cancel_url',
+ ],
+ 'allow_save_card' => false,
+ ];
+ break;
+ }
+ $this->assertEquals(
+ $expected,
+ $this->gateway->formatPaymentAttributes(PaymentInputDTOMock::get([
+ 'context' => $context,
+ ]))
+ );
+ }
+}
diff --git a/tests/Units/Gateways/Payment/StandardPaymentGateway/standardPaymentGatewayBase.php b/tests/Units/Gateways/Payment/StandardPaymentGateway/standardPaymentGatewayBase.php
new file mode 100644
index 0000000..10ee162
--- /dev/null
+++ b/tests/Units/Gateways/Payment/StandardPaymentGateway/standardPaymentGatewayBase.php
@@ -0,0 +1,24 @@
+gateway = Mockery::mock(StandardPaymentGateway::class, [])->makePartial();
+ }
+
+ protected function tearDown(): void
+ {
+ Mockery::close();
+ }
+}
diff --git a/tests/Units/Gateways/PaymentGateway.php b/tests/Units/Gateways/PaymentGateway.php
new file mode 100644
index 0000000..5aca3d5
--- /dev/null
+++ b/tests/Units/Gateways/PaymentGateway.php
@@ -0,0 +1,66 @@
+ */
+ protected array $expected_context;
+
+ /**
+ * @param string $payment_gateway_name
+ *
+ * @throws \Exception
+ */
+ public function load(string $payment_gateway_name): self
+ {
+ if (empty($payment_gateway_name)) {
+ throw new \Exception('Invalid parameter, $payment_gateway_name given should be a non empty string.');
+ }
+
+ $payment_gateway_path = '\PayplugPluginCore\gateways\payment\\'
+ . str_replace('_', '', ucwords($payment_gateway_name, '_'))
+ . 'PaymentGateway';
+ if (!class_exists($payment_gateway_path)) {
+ throw new \Exception('Payment Gateway can\'t be found.');
+ }
+
+ $instance = new $payment_gateway_path();
+ if (!$instance instanceof self) {
+ throw new \Exception('Payment Gateway class does not extend PaymentGateway.');
+ }
+
+ return $instance;
+ }
+
+ /**
+ * @param PaymentInputDTO $payment_inputDTO
+ *
+ * @return array
+ */
+ public function getDefaultAttributeFromDTO(PaymentInputDTO $payment_inputDTO): array
+ {
+ $customer = $payment_inputDTO->getCustomer() ?? [];
+ $urls = $payment_inputDTO->getUrls() ?? [];
+
+ return [
+ 'amount' => $payment_inputDTO->getAmount(),
+ 'currency' => $payment_inputDTO->getCurrencyIsoCode(),
+ 'billing' => $customer['billing'] ?? null,
+ 'shipping' => ($customer['shipping'] ?? []) + ['delivery_type' => 'BILLING'],
+ 'hosted_payment' => [
+ 'return_url' => $urls['return'] ?? null,
+ 'cancel_url' => $urls['cancel'] ?? null,
+ ],
+ 'notification_url' => $urls['notification'] ?? null,
+ 'metadata' => $payment_inputDTO->getMetadata(),
+ 'allow_save_card' => false,
+ 'force_3ds' => false,
+ ];
+ }
+}