From 9a6065f4a1250140cee5333d559962a35ac5c9fe Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Mon, 20 Apr 2026 16:37:08 +0200 Subject: [PATCH 01/21] chore(ci): add GitHub workflows for CGL, tests and TER release --- .github/workflows/cgl.yml | 9 +++++++++ .github/workflows/release.yml | 13 +++++++++++++ .github/workflows/tests.yml | 13 +++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 .github/workflows/cgl.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/cgl.yml b/.github/workflows/cgl.yml new file mode 100644 index 0000000..4fc0c92 --- /dev/null +++ b/.github/workflows/cgl.yml @@ -0,0 +1,9 @@ +name: CGL +on: + push: + branches: + - '**' + +jobs: + cgl: + uses: konradmichalik/reusable-github-actions/.github/workflows/cgl-test.yml@main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1c23831 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,13 @@ +name: Release +on: + push: + tags: + - '*' + +jobs: + ter-publish: + uses: konradmichalik/reusable-github-actions/.github/workflows/release-typo3.yml@main + secrets: + typo3-api-token: ${{ secrets.TYPO3_API_TOKEN }} + with: + typo3-extension-key: 'repeatable_form_elements' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..d6462fc --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,13 @@ +name: Tests +on: + push: + branches: + - '**' + +jobs: + tests: + uses: konradmichalik/reusable-github-actions/.github/workflows/tests-typo3.yml@main + with: + php-versions: '["8.2", "8.3", "8.4"]' + typo3-versions: '["13.4", "14.2"]' + dependencies: '["highest", "lowest"]' From a55be2b64496eff2a5de9d218f321fcc79b3e294 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Mon, 20 Apr 2026 16:37:14 +0200 Subject: [PATCH 02/21] chore: add CGL toolchain (PHP CS Fixer, PHPStan, Rector, EditorConfig) --- Tests/CGL/.php-cs-fixer.php | 37 +++++++++++++++++ Tests/CGL/composer.json | 73 +++++++++++++++++++++++++++++++++ Tests/CGL/phpstan-baseline.neon | 2 + Tests/CGL/phpstan.neon | 20 +++++++++ Tests/CGL/rector.php | 55 +++++++++++++++++++++++++ 5 files changed, 187 insertions(+) create mode 100644 Tests/CGL/.php-cs-fixer.php create mode 100644 Tests/CGL/composer.json create mode 100644 Tests/CGL/phpstan-baseline.neon create mode 100644 Tests/CGL/phpstan.neon create mode 100644 Tests/CGL/rector.php diff --git a/Tests/CGL/.php-cs-fixer.php b/Tests/CGL/.php-cs-fixer.php new file mode 100644 index 0000000..35036db --- /dev/null +++ b/Tests/CGL/.php-cs-fixer.php @@ -0,0 +1,37 @@ +registerCustomFixers([ + new DocBlockHeaderFixer(), + ]) + ->withRule( + Header::create( + 'repeatable_form_elements', + Type::TYPO3Extension, + Author::create('Ralf Zimmermann', 'r.zimmermann@dreistrom.land'), + CopyrightRange::from(2018), + ), + ) + ->withRule( + Set::fromArray( + DocBlockHeader::fromComposer()->__toArray(), + ), + ) + ->withFinder( + static fn (Finder $finder) => $finder + ->in($rootPath) + ->notPath(['ext_emconf.php']), + ) +; diff --git a/Tests/CGL/composer.json b/Tests/CGL/composer.json new file mode 100644 index 0000000..5ebd0b8 --- /dev/null +++ b/Tests/CGL/composer.json @@ -0,0 +1,73 @@ +{ + "authors": [ + { + "name": "Konrad Michalik", + "email": "hej@konradmichalik.dev", + "role": "Maintainer" + } + ], + "require-dev": { + "armin/editorconfig-cli": "^2.2.1", + "ergebnis/composer-normalize": "^2.50.0", + "konradmichalik/php-cs-fixer-preset": "^0.1.1", + "konradmichalik/php-doc-block-header-fixer": "^0.3.3", + "konradmichalik/phpstan-typo3-preset": "^0.2", + "phpstan/phpstan-phpunit": "^1.0 || ^2.0", + "psr/http-message": "^1.0 || ^2.0", + "shipmonk/composer-dependency-analyser": "1.8.4", + "ssch/typo3-rector": "^2.10 || ^3.0", + "typo3/coding-standards": "^0.7 || ^0.8" + }, + "replace": { + "typo3/cms-core": "*", + "typo3/cms-extbase": "*" + }, + "config": { + "allow-plugins": { + "ergebnis/composer-normalize": true, + "php-http/discovery": true, + "typo3/class-alias-loader": false, + "typo3/cms-composer-installers": false + }, + "lock": false, + "sort-packages": true + }, + "extra": { + "konradmichalik/php-cs-fixer-preset": { + "copyright": 2018 + } + }, + "scripts": { + "analyze": [ + "@analyze:dependencies" + ], + "analyze:dependencies": "composer-dependency-analyser --composer-json ../../composer.json", + "fix": [ + "@fix:composer", + "@fix:editorconfig", + "@fix:php" + ], + "fix:composer": [ + "@composer normalize", + "@composer normalize ../../composer.json" + ], + "fix:editorconfig": "@lint:editorconfig --fix", + "fix:php": "@php vendor/bin/php-cs-fixer --config=.php-cs-fixer.php fix", + "lint": [ + "@lint:composer", + "@lint:editorconfig", + "@lint:php" + ], + "lint:composer": "@fix:composer --dry-run", + "lint:editorconfig": "ec --git-only", + "lint:php": "@fix:php --dry-run", + "migration": [ + "@migration:rector" + ], + "migration:rector": "rector process -c rector.php", + "sca": [ + "@sca:php" + ], + "sca:php": "phpstan analyse --memory-limit=2G" + } +} diff --git a/Tests/CGL/phpstan-baseline.neon b/Tests/CGL/phpstan-baseline.neon new file mode 100644 index 0000000..aab4991 --- /dev/null +++ b/Tests/CGL/phpstan-baseline.neon @@ -0,0 +1,2 @@ +parameters: + ignoreErrors: [] diff --git a/Tests/CGL/phpstan.neon b/Tests/CGL/phpstan.neon new file mode 100644 index 0000000..edce533 --- /dev/null +++ b/Tests/CGL/phpstan.neon @@ -0,0 +1,20 @@ +includes: + - %rootDir%/../../konradmichalik/phpstan-typo3-preset/phpstan.neon.dist + - phpstan-baseline.neon + +parameters: + level: 8 + + scanDirectories: + - ../../vendor + paths: + - ../../Classes + - ../../Configuration + - ../../Resources + - ../../Tests/Unit + + excludePaths: + - ../../.Build (?) + + type_coverage: + constant: 0 # TODO: Set to 100, when PHP 8.3 is minimum requirement diff --git a/Tests/CGL/rector.php b/Tests/CGL/rector.php new file mode 100644 index 0000000..154debc --- /dev/null +++ b/Tests/CGL/rector.php @@ -0,0 +1,55 @@ +withPaths([ + $rootPath.'/Classes', + $rootPath.'/Configuration', + $rootPath.'/ext_emconf.php', + $rootPath.'/ext_localconf.php', + $rootPath.'/Tests/Unit', + ]) + ->withPhpVersion(PhpVersion::PHP_82) + ->withSets([ + Typo3SetList::CODE_QUALITY, + Typo3SetList::GENERAL, + Typo3LevelSetList::UP_TO_TYPO3_13, + LevelSetList::UP_TO_PHP_82, + ]) + ->withPHPStanConfigs([ + Typo3Option::PHPSTAN_FOR_RECTOR_PATH, + ]) + ->withRules([ + AddVoidReturnTypeWhereNoReturnRector::class, + ]) + ->withConfiguredRule(ExtEmConfRector::class, [ + ExtEmConfRector::PHP_VERSION_CONSTRAINT => '8.2.0-8.4.99', + ExtEmConfRector::TYPO3_VERSION_CONSTRAINT => '13.4.0-14.99.99', + ExtEmConfRector::ADDITIONAL_VALUES_TO_BE_REMOVED => [], + ]) + ->withSkip([ + __DIR__.'/**/Configuration/ExtensionBuilder/*', + NameImportingPostRector::class => [ + 'ext_localconf.php', + 'ext_tables.php', + 'ClassAliasMap.php', + ], + NullToStrictStringFuncCallArgRector::class, + ]) + ->withTypeCoverageLevel(0) + ->withDeadCodeLevel(0) + ->withCodeQualityLevel(0) +; From 215a62528b8f6db70cf21dab0f23309fd40735c7 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Mon, 20 Apr 2026 16:37:21 +0200 Subject: [PATCH 03/21] chore: add PHPUnit configuration and unit test directory --- phpunit.xml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 phpunit.xml diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..82776b6 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,33 @@ + + + + + + + + Tests/Unit + + + + + + + + + + + + + + + Classes + + + From 38abfa403bbffcb4439c44a96204c4a6fa624570 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Mon, 20 Apr 2026 16:37:26 +0200 Subject: [PATCH 04/21] chore: add EditorConfig, packaging excludes and version bumper config --- .editorconfig | 77 +++++++++++++++++++++++++++++++++++++++++++ packaging_exclude.php | 32 ++++++++++++++++++ version-bumper.yaml | 5 +++ 3 files changed, 114 insertions(+) create mode 100644 .editorconfig create mode 100644 packaging_exclude.php create mode 100644 version-bumper.yaml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2cf7ca2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,77 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +# JS files +[*.js] +indent_size = 2 + +# JSON files +[*.json] +indent_style = tab + +# package.json +[package.json] +indent_size = 2 + +# ReST files +[{*.rst,*.rst.txt}] +indent_size = 4 +max_line_length = 80 + +# SQL files +[*.sql] +indent_style = tab +indent_size = 2 + +# TypoScript files +[*.{typoscript,tsconfig}] +indent_size = 2 + +# YAML files +[{*.yml,*.yaml}] +indent_size = 2 + +# XLF files +[*.xlf] +indent_style = tab + +# .htaccess +[.htaccess] +indent_style = tab + +# Markdown files +[*.md] +indent_style = unset +indent_size = unset +trim_trailing_whitespace = unset +insert_final_newline = unset + +# phpstan.neon +[*.neon] +indent_style = tab + +# Ignore paths +[/.ddev/**] +charset = unset +end_of_line = unset +insert_final_newline = unset +trim_trailing_whitespace = unset +indent_style = unset +indent_size = unset + +[/Documentation/**] +charset = unset +end_of_line = unset +insert_final_newline = unset +trim_trailing_whitespace = unset +indent_style = unset +indent_size = unset diff --git a/packaging_exclude.php b/packaging_exclude.php new file mode 100644 index 0000000..bcd2868 --- /dev/null +++ b/packaging_exclude.php @@ -0,0 +1,32 @@ + [ + '.Build', + '.ddev', + '.git', + '.github', + 'Documentation', + 'docs', + 'public', + 'Tests', + 'var', + 'vendor', + ], + 'files' => [ + 'DS_Store', + 'CODE_OF_CONDUCT.md', + 'CODEOWNERS', + 'composer.lock', + 'CONTRIBUTING.md', + 'editorconfig', + 'gitattributes', + 'gitignore', + 'packaging_exclude.php', + 'phpunit.xml', + 'renovate.json', + 'version-bumper.yaml', + ], +]; diff --git a/version-bumper.yaml b/version-bumper.yaml new file mode 100644 index 0000000..53327b4 --- /dev/null +++ b/version-bumper.yaml @@ -0,0 +1,5 @@ +presets: + - name: typo3-extension + +releaseOptions: + commitMessage: 'release: version {%version%}' From a85428eb7b1278fe8603ca02e40f64d3f7cdef91 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Mon, 20 Apr 2026 16:37:33 +0200 Subject: [PATCH 05/21] docs: add contributing guidelines --- CONTRIBUTING.md | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cb4c33b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,97 @@ +# Contributing + +Thank you for considering contributing to this project! Every contribution is welcome and helps improve the quality of the project. To ensure a smooth process and maintain high code quality, please follow the steps below. + +Please note that this project adheres to the [TYPO3 Code of Conduct](https://typo3.org/community/values/code-of-conduct). By participating, you are expected to uphold this code. + +## Requirements + +- [DDEV](https://ddev.readthedocs.io/en/stable/) + +## Preparation + +```bash +# Clone repository +git clone https://github.com/tritum/repeatable_form_elements.git +cd repeatable_form_elements + +# Start the project with DDEV +ddev start + +# Install dependencies +ddev composer install +``` + +## Run linters + +```bash +# All linters +ddev cgl lint + +# Specific linters +ddev cgl lint:composer +ddev cgl lint:editorconfig +ddev cgl lint:php + +# Fix all CGL issues +ddev cgl fix + +# Fix specific CGL issues +ddev cgl fix:composer +ddev cgl fix:editorconfig +ddev cgl fix:php +``` + +## Run static code analysis + +```bash +# All static code analyzers +ddev cgl sca + +# Specific static code analyzers +ddev cgl sca:php +``` + +## Run tests + +```bash +# All tests +ddev composer test + +# All tests with code coverage +ddev composer test:coverage +``` + +## TYPO3 Setup + +For testing the extension, you need to set up the TYPO3 instances. + +```bash +# Install all TYPO3 versions, which are supported by the extension +ddev install all + +# Or install specific TYPO3 versions +ddev install 13 + +# Open the overview page +ddev launch + +# Run TYPO3 specific commands +ddev 13 typo3 cache:flush +ddev 13 composer install +ddev all typo3 database:updateschema +``` + +## Workflow + +1. Fork the repository and create a feature branch from `master`. +2. Make your changes and ensure all linters and tests pass. +3. Use descriptive commit messages following the conventional commits format: `: ` (e.g. `feat: add nested repeatable container support`, `fix: resolve copy button state after removal`). + +## Submit a pull request + +After completing your work, **open a pull request** and provide a description of your changes. Ideally, your PR should reference an issue that explains the problem you are addressing. + +All mentioned code quality tools will run automatically on every pull request. For more details, see the relevant [workflows][1]. + +[1]: .github/workflows From 6b017b1ebfca4a344544cc3a50fb73c453e48df3 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Mon, 20 Apr 2026 16:37:45 +0200 Subject: [PATCH 06/21] chore: add composer scripts, dev-dependencies and update gitignore --- .gitignore | 6 ++++-- composer.json | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index bd5576a..58c35f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ +.Build/ +.claude +/composer.lock /public /vendor -/composer.lock -.Build/ +/Tests/CGL/vendor diff --git a/composer.json b/composer.json index 2fbd6d2..b8aaab7 100644 --- a/composer.json +++ b/composer.json @@ -34,6 +34,13 @@ "typo3/cms-extbase": "^13.4 || ^14.0", "typo3/cms-form": "^13.4 || ^14.0" }, + "require-dev": { + "eliashaeussler/version-bumper": "^2.4 || ^3.0", + "phpunit/phpunit": "^11.0 || ^12.0", + "typo3/cms-base-distribution": "^13.4 || ^14.0" + }, + "minimum-stability": "dev", + "prefer-stable": true, "autoload": { "psr-4": { "TRITUM\\RepeatableFormElements\\": "Classes/" @@ -41,14 +48,26 @@ }, "config": { "allow-plugins": { + "eliashaeussler/version-bumper": true, "typo3/class-alias-loader": true, "typo3/cms-composer-installers": true }, "sort-packages": true }, "extra": { + "konradmichalik/php-cs-fixer-preset": { + "copyright": 2018 + }, "typo3/cms": { "extension-key": "repeatable_form_elements" } + }, + "scripts": { + "post-install-cmd": [ + "@cgl install" + ], + "cgl": "@composer -d Tests/CGL --", + "test": "@test:coverage --no-coverage", + "test:coverage": "XDEBUG_MODE=coverage phpunit -c phpunit.xml" } } From 729ceb2bf4c74b41c1308b556d29ba2f46ca8662 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Mon, 20 Apr 2026 16:38:44 +0200 Subject: [PATCH 07/21] chore(ci): remove TER release workflow --- .github/workflows/release.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 1c23831..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Release -on: - push: - tags: - - '*' - -jobs: - ter-publish: - uses: konradmichalik/reusable-github-actions/.github/workflows/release-typo3.yml@main - secrets: - typo3-api-token: ${{ secrets.TYPO3_API_TOKEN }} - with: - typo3-extension-key: 'repeatable_form_elements' From dce0500964e19f33b48c925543c464ad1f352c2e Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Mon, 20 Apr 2026 16:55:07 +0200 Subject: [PATCH 08/21] docs: add fork notice to README --- .ddev/commands/web/cgl | 8 ++++++++ README.md | 2 ++ 2 files changed, 10 insertions(+) create mode 100755 .ddev/commands/web/cgl diff --git a/.ddev/commands/web/cgl b/.ddev/commands/web/cgl new file mode 100755 index 0000000..abdcc0b --- /dev/null +++ b/.ddev/commands/web/cgl @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +## Description: Run CGL script for the package files +## Usage: cgl [command] [options] +## Example: ddev cgl lint\nddev cgl fix\nddev cgl migration\nddev cgl sca\nddev cgl lint:composer\nddev cgl fix:composer + +composer -d /var/www/html/Tests/CGL "$@" \ No newline at end of file diff --git a/README.md b/README.md index c4f3b78..68c0d47 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ ![TYPO3 versions](https://typo3-badges.dev/badge/repeatable_form_elements/typo3/shields.svg) ![Latest version](https://typo3-badges.dev/badge/repeatable_form_elements/version/shields.svg) +> **Fork notice:** This is a fork of [tritum/repeatable_form_elements](https://github.com/tritum/repeatable_form_elements), the original extension by Ralf Zimmermann / dreistrom.land. This fork adds TYPO3 v14 compatibility, PSR-14 event migration, CI/CD infrastructure and a DDEV-based multi-version test environment. + # Custom form element "Repeatable container" This TYPO3 extension adds a custom form element "Repeatable container" to the From 305aecce3569713b4f8a6735450600c4cf8b2072 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Mon, 20 Apr 2026 16:55:49 +0200 Subject: [PATCH 09/21] docs: restructure README with GitHub highlights and sections --- README.md | 93 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 68c0d47..91901f6 100644 --- a/README.md +++ b/README.md @@ -5,62 +5,83 @@ ![TYPO3 versions](https://typo3-badges.dev/badge/repeatable_form_elements/typo3/shields.svg) ![Latest version](https://typo3-badges.dev/badge/repeatable_form_elements/version/shields.svg) -> **Fork notice:** This is a fork of [tritum/repeatable_form_elements](https://github.com/tritum/repeatable_form_elements), the original extension by Ralf Zimmermann / dreistrom.land. This fork adds TYPO3 v14 compatibility, PSR-14 event migration, CI/CD infrastructure and a DDEV-based multi-version test environment. +> [!NOTE] +> This is a fork of [tritum/repeatable_form_elements](https://github.com/tritum/repeatable_form_elements), the original extension by Ralf Zimmermann / dreistrom.land. This fork adds TYPO3 v14 compatibility, PSR-14 event migration, CI/CD infrastructure and a DDEV-based multi-version test environment. -# Custom form element "Repeatable container" +# πŸ“¦ Repeatable Form Elements -This TYPO3 extension adds a custom form element "Repeatable container" to the -TYPO3 form framework. It displays one/ a set of fields which can be duplicated -and removed if desired. Any existing validation is copied as well. All form -finishers will be aware of the copied field(s). +A TYPO3 extension that adds a **Repeatable container** element to the TYPO3 form framework. It allows editors to create container elements with any type of fields. In the frontend, users can dynamically add and remove copies of the container. Validation is copied automatically and all form finishers are aware of the duplicated fields. -## Preferred installation +## πŸ“‹ Requirements -1. Require the extension via composer. -2. Add the site set tritum/form-element-linked-checkbox to the dependencies of - your site packages site set (TYPO3 v13). Or add the static TypoScript - configuration to your TypoScript template (TYPO3 v12 and TYPO3 v13). +| Requirement | Version | +|-------------|---------| +| PHP | 8.2 – 8.4 | +| TYPO3 | 13.4 LTS, 14.x | -## Usage +## πŸš€ Installation -Open the TYPO3 form editor and create a new form/ open an existing one. Add a -new element to your form. The modal will list the new custom form element -"Repeatable container". +```bash +composer require tritum/repeatable-form-elements +``` -Add the desired fields with the favored validators to the "Repeatable container". +Add the site set `tritum/repeatable-form-elements` to the dependencies of your site package's site set: -The frontend will render the "Repeatable container" as fieldset. In addition to the -included form elements it will display buttons for copying/ removing new sets of fields. +```yaml +# Configuration/Sets/YourSitePackage/config.yaml +dependencies: + - tritum/repeatable-form-elements +``` -The newly implemented extended version of SaveToDatabaseFinisher can be used as seen [here](Resources/Private/ExampleFormDefinitions/extended-save-to-database-finisher.form.yaml). +## πŸ’‘ Usage -## Configuration +1. Open the TYPO3 **form editor** and create or open a form. +2. Add a new element β€” the modal lists the **Repeatable container**. +3. Add fields with validators to the container. +4. In the frontend, the container renders as a `
` with **copy** and **remove** buttons. -To deactivate the copying of variants, the feature `repeatableFormElements.copyVariants` can be used +### Extended SaveToDatabaseFinisher -## Extendability +An extended version of the `SaveToDatabaseFinisher` is included for persisting repeatable container data. See the [example form definition](Resources/Private/ExampleFormDefinitions/extended-save-to-database-finisher.form.yaml). -The following options can be used to extend the behavior when copying. +## βš™οΈ Configuration -| Name | Description | -|------------------|------------------------------------------------------------------| -| CopyVariantEvent | Extend manipulation of copied variants or disable specific ones. | +To deactivate the copying of variants, disable the feature flag: -## Credits +```php +$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['repeatableFormElements.copyVariants'] = false; +``` -This TYPO3 extension was created by Ralf Zimmermann (https://dreistrom.land). +## πŸ”Œ Extendability -## Thank you +| Event | Description | +|-------|-------------| +| `CopyVariantEvent` | Modify or disable specific copied variants during container duplication. | +| `AfterBuildingFinishedEvent` | React after a form renderable has been built/copied by the repeatable container logic. Replaces the removed `afterBuildingFinished` SC_OPTIONS hook. | -Nora Winter - "Faktenkopf" at www.faktenhaus.de - sponsored this great extension. -The fine people at www.b13.de connected all the people involved. +## 🀝 Contributing -Elias HΓ€ußler - haeussler.dev - for helping with TYPO3v11 compatability and providing -the beautiful [TYPO3 badges](https://typo3-badges.dev). Use them. Give him some kudos! +Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, linting, testing and the PR workflow. -Uwe - Hawkeye1909 - for removing jQuery as dependency. +## πŸ“ Changelog -Alexander Opitz @ extrameile-gehen.de - for his work on saving repeatable elements to database. +See [CHANGELOG.md](CHANGELOG.md) for a list of changes. +## πŸ† Credits -especially to all others who have contributed to the improvement of the extension. +This extension was originally created by [Ralf Zimmermann](https://dreistrom.land). + +**Thank you to all contributors:** + +- **Nora Winter** (Faktenkopf / faktenhaus.de) β€” sponsored the original extension +- **b13.de** β€” connected all the people involved +- **Elias HΓ€ußler** (haeussler.dev) β€” TYPO3 v11 compatibility and [TYPO3 badges](https://typo3-badges.dev) +- **Uwe** (Hawkeye1909) β€” removed jQuery as a dependency +- **Alexander Opitz** (extrameile-gehen.de) β€” SaveToDatabaseFinisher for repeatable elements +- **Falko Linke, Christian Seyfferth** (dreistrom.land) β€” ongoing development + +And everyone else who has contributed to improving this extension. + +## πŸ“„ License + +GPL-2.0-or-later β€” see [LICENSE](LICENSE.txt) for details. From d5eb9da73352e0808a46820ffc9010b8093f59f5 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Mon, 20 Apr 2026 16:57:00 +0200 Subject: [PATCH 10/21] docs: update composer name in install instructions, simplify credits --- README.md | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 91901f6..a9299b9 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ A TYPO3 extension that adds a **Repeatable container** element to the TYPO3 form ## πŸš€ Installation ```bash -composer require tritum/repeatable-form-elements +composer require move-elevator/typo3-repeatable-form-elements ``` Add the site set `tritum/repeatable-form-elements` to the dependencies of your site package's site set: @@ -69,18 +69,9 @@ See [CHANGELOG.md](CHANGELOG.md) for a list of changes. ## πŸ† Credits -This extension was originally created by [Ralf Zimmermann](https://dreistrom.land). +Originally created by [Ralf Zimmermann / dreistrom.land](https://dreistrom.land). See the [original repository](https://github.com/tritum/repeatable_form_elements) for the full list of contributors. -**Thank you to all contributors:** - -- **Nora Winter** (Faktenkopf / faktenhaus.de) β€” sponsored the original extension -- **b13.de** β€” connected all the people involved -- **Elias HΓ€ußler** (haeussler.dev) β€” TYPO3 v11 compatibility and [TYPO3 badges](https://typo3-badges.dev) -- **Uwe** (Hawkeye1909) β€” removed jQuery as a dependency -- **Alexander Opitz** (extrameile-gehen.de) β€” SaveToDatabaseFinisher for repeatable elements -- **Falko Linke, Christian Seyfferth** (dreistrom.land) β€” ongoing development - -And everyone else who has contributed to improving this extension. +This fork is maintained by [move:elevator](https://move-elevator.de). ## πŸ“„ License From 0475ec050c7e1ef5f2d6d99fd75259f9925ab65a Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 08:48:25 +0200 Subject: [PATCH 11/21] fix: resolve all PHPStan level 8 errors --- Classes/Event/CopyVariantEvent.php | 36 +- .../AfterCurrentPageIsResolvedListener.php | 20 +- Classes/Finisher/SaveToDatabaseFinisher.php | 190 +++++----- .../RepeatableContainerInterface.php | 30 +- Classes/Hooks/FormHooks.php | 139 ++++--- Classes/Service/CopyService.php | 353 ++++++++++-------- LICENSE.txt => LICENSE | 0 7 files changed, 442 insertions(+), 326 deletions(-) rename LICENSE.txt => LICENSE (100%) diff --git a/Classes/Event/CopyVariantEvent.php b/Classes/Event/CopyVariantEvent.php index c28db96..39355ab 100644 --- a/Classes/Event/CopyVariantEvent.php +++ b/Classes/Event/CopyVariantEvent.php @@ -2,9 +2,18 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace TRITUM\RepeatableFormElements\Event; -/** +/* * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. * * For the full copyright and license information, please read the @@ -14,33 +23,40 @@ use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; /** - * Use this event to manipulate variantOptions on copying form elements + * CopyVariantEvent. + * + * @author Konrad Michalik */ class CopyVariantEvent { - private array $options; - private FormElementInterface $originalFormElement; - private FormElementInterface $newFormElement; - private string $newIdentifier; + private readonly FormElementInterface $originalFormElement; + private readonly FormElementInterface $newFormElement; private bool $variantEnabled = true; + /** + * @param array $options + */ public function __construct( - array $options, + private array $options, FormElementInterface $originalFormElement, FormElementInterface $newFormElement, - string $newIdentifier, + private readonly string $newIdentifier, ) { - $this->options = $options; $this->originalFormElement = $originalFormElement; $this->newFormElement = $newFormElement; - $this->newIdentifier = $newIdentifier; } + /** + * @return array + */ public function getOptions(): array { return $this->options; } + /** + * @param array $options + */ public function setOptions(array $options): void { $this->options = $options; diff --git a/Classes/EventListener/AfterCurrentPageIsResolvedListener.php b/Classes/EventListener/AfterCurrentPageIsResolvedListener.php index 90ce413..7b7a5d9 100644 --- a/Classes/EventListener/AfterCurrentPageIsResolvedListener.php +++ b/Classes/EventListener/AfterCurrentPageIsResolvedListener.php @@ -2,24 +2,34 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace TRITUM\RepeatableFormElements\EventListener; use TRITUM\RepeatableFormElements\Hooks\FormHooks; use TYPO3\CMS\Form\Event\AfterCurrentPageIsResolvedEvent; /** - * PSR-14 replacement for the afterInitializeCurrentPage SC_OPTIONS hook. - * This event listener is used in TYPO3 v14+ where the hook was removed. + * AfterCurrentPageIsResolvedListener. + * + * @author Konrad Michalik */ -final class AfterCurrentPageIsResolvedListener +final readonly class AfterCurrentPageIsResolvedListener { public function __construct( - private readonly FormHooks $formHooks, + private FormHooks $formHooks, ) {} public function __invoke(AfterCurrentPageIsResolvedEvent $event): void { - $event->currentPage = $this->formHooks->afterInitializeCurrentPage( + $event->currentPage = $this->formHooks->afterInitializeCurrentPage( // @phpstan-ignore assign.propertyType $event->formRuntime, $event->currentPage, $event->lastDisplayedPage, diff --git a/Classes/Finisher/SaveToDatabaseFinisher.php b/Classes/Finisher/SaveToDatabaseFinisher.php index efe20fa..f9dd5e3 100644 --- a/Classes/Finisher/SaveToDatabaseFinisher.php +++ b/Classes/Finisher/SaveToDatabaseFinisher.php @@ -9,21 +9,36 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace TRITUM\RepeatableFormElements\Finisher; use DateTimeInterface; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\ArrayUtility; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Domain\Model\FileReference; use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; +use function is_array; +use function is_int; +use function is_string; + /** - * This finisher is an extension to the default SaveToDatabaseFinisher. - * It is intentionally registered as a new identifier to keep compatibility with existing forms. + * SaveToDatabaseFinisher. + * + * @author Konrad Michalik */ class SaveToDatabaseFinisher extends \TYPO3\CMS\Form\Domain\Finishers\SaveToDatabaseFinisher { + public function __construct(private readonly ConnectionPool $connectionPool) {} + protected function process(int $iterationCount): void { $this->throwExceptionOnInconsistentConfiguration(); @@ -31,16 +46,17 @@ protected function process(int $iterationCount): void $table = $this->parseOption('table'); $table = is_string($table) ? $table : ''; $elementsConfiguration = $this->parseOption('elements'); - $elementsConfiguration = is_array($elementsConfiguration) ? $elementsConfiguration : []; + $elementsConfiguration = is_array($elementsConfiguration) ? $elementsConfiguration : []; $databaseColumnMappingsConfiguration = $this->parseOption('databaseColumnMappings'); - $this->databaseConnection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); + $this->databaseConnection = $this->connectionPool->getConnectionForTable($table); $databaseData = []; + $databaseColumnMappingsConfiguration = is_array($databaseColumnMappingsConfiguration) ? $databaseColumnMappingsConfiguration : []; foreach ($databaseColumnMappingsConfiguration as $databaseColumnName => $databaseColumnConfiguration) { - $value = $this->parseOption('databaseColumnMappings.' . $databaseColumnName . '.value'); + $value = $this->parseOption('databaseColumnMappings.'.$databaseColumnName.'.value'); if ( - empty($value) + (null === $value || '' === $value) && ($databaseColumnConfiguration['skipIfValueIsEmpty'] ?? false) === true ) { continue; @@ -51,23 +67,23 @@ protected function process(int $iterationCount): void // decide which strategy to use $containerConfiguration = $this->parseOption('container'); - if (!empty($containerConfiguration) && is_string($containerConfiguration)) { + if (is_string($containerConfiguration) && '' !== $containerConfiguration) { $this->processContainer($containerConfiguration, $elementsConfiguration, $databaseData, $table, $iterationCount); } else { $databaseData = $this->prepareData($elementsConfiguration, $databaseData); $this->saveToDatabase($databaseData, $table, $iterationCount); } - } /** - * This action will do mostly the same processing as the default processing but we need to set prefix for the finisher to find the correct element - * @param string $containerPath the identifier of the container to process, can be for example `RootContainer` or `RootContainer.0.NestedContainer` - * @param array $elementsConfiguration finisher-element-configuration - * @param array $databaseData prepared data - * @param string $table Tablename to save data to - * @param int $iterationCount finisher iteration + * This action will do mostly the same processing as the default processing but we need to set prefix for the finisher to find the correct element. + * + * @param string $containerPath the identifier of the container to process, can be for example `RootContainer` or `RootContainer.0.NestedContainer` + * @param array $elementsConfiguration finisher-element-configuration + * @param array $databaseData prepared data + * @param string $table Tablename to save data to + * @param int $iterationCount finisher iteration */ protected function processContainer( string $containerPath, @@ -78,7 +94,7 @@ protected function processContainer( ): void { $containerValues = ArrayUtility::getValueByPath($this->getFormValues(), $containerPath, '.'); foreach ($containerValues as $copyId => $containerItem) { - $prefix = $containerPath . '.' . $copyId . '.'; + $prefix = $containerPath.'.'.$copyId.'.'; // store data inside new array to keep prepared $databaseData for all iterations $itemDatabaseData = $this->prepareData($elementsConfiguration, $databaseData, $containerItem, $prefix); @@ -88,19 +104,20 @@ protected function processContainer( /** * Adapted method for container data. - * @param array $elementsConfiguration finisher element configuration - * @param array $databaseData prepared data - * @param array $values optional filled Array with form values to use - * @param string $prefix prefix to get the form element object by a full identifier - * @return array the filled database data + * + * @param array $databaseData prepared data + * @param array $values optional filled Array with form values to use + * @param string $prefix prefix to get the form element object by a full identifier + * + * @return array the filled database data */ - protected function prepareData( + protected function prepareData(// @phpstan-ignore missingType.iterableValue array $elementsConfiguration, array $databaseData, array $values = [], string $prefix = '', ): array { - if (empty($values)) { + if ([] === $values) { $values = $this->getFormValues(); } @@ -109,25 +126,7 @@ protected function prepareData( continue; } - if ($elementValue instanceof FileReference) { - if (isset($elementsConfiguration[$elementIdentifier]['saveFileIdentifierInsteadOfUid'])) { - $saveFileIdentifierInsteadOfUid = (bool)$elementsConfiguration[$elementIdentifier]['saveFileIdentifierInsteadOfUid']; - } else { - $saveFileIdentifierInsteadOfUid = false; - } - - if ($saveFileIdentifierInsteadOfUid) { - $elementValue = $elementValue->getOriginalResource()->getCombinedIdentifier(); - } else { - $elementValue = $elementValue->getOriginalResource()->getProperty('uid_local'); - } - } elseif (is_array($elementValue)) { - $elementValue = implode(',', $elementValue); - } elseif ($elementValue instanceof DateTimeInterface) { - $format = $elementsConfiguration[$elementIdentifier]['dateFormat'] ?? 'U'; - $elementValue = $elementValue->format($format); - } - + $elementValue = $this->resolveElementValue($elementValue, $elementsConfiguration[$elementIdentifier] ?? []); $databaseData[$elementsConfiguration[$elementIdentifier]['mapOnDatabaseColumn']] = $elementValue; } @@ -137,56 +136,79 @@ protected function prepareData( /** * Save or insert the values from * $databaseData into the table $table - * and provide some finisher variables + * and provide some finisher variables. */ - protected function saveToDatabase( + protected function saveToDatabase(// @phpstan-ignore missingType.iterableValue array $databaseData, string $table, int $iterationCount, ?int $containerItemKey = null, ): void { - if (!empty($databaseData)) { - if ($this->parseOption('mode') === 'update') { - $whereClause = $this->parseOption('whereClause'); - foreach ($whereClause as $columnName => $columnValue) { - $whereClause[$columnName] = $this->parseOption('whereClause.' . $columnName); - } - $this->databaseConnection->update( - $table, - $databaseData, - $whereClause, - ); - } else { - $this->databaseConnection->insert($table, $databaseData); - $insertedUid = (int)$this->databaseConnection->lastInsertId($table); - $this->finisherContext->getFinisherVariableProvider()->add( - $this->shortFinisherIdentifier, - 'insertedUids.' . $iterationCount . (is_int($containerItemKey) ? '.' . $containerItemKey : ''), - $insertedUid, - ); - - $currentCount = $this->finisherContext->getFinisherVariableProvider()->get( - $this->shortFinisherIdentifier, - 'countInserts.', - $iterationCount, - 0, - ); - $this->finisherContext->getFinisherVariableProvider()->addOrUpdate( - $this->shortFinisherIdentifier, - 'countInserts.' . $iterationCount, - $currentCount++, - ); + if ([] === $databaseData) { + return; + } + + if ('update' === $this->parseOption('mode')) { + $whereClause = $this->parseOption('whereClause'); + $whereClause = is_array($whereClause) ? $whereClause : []; + /** @var array $resolvedWhereClause */ + $resolvedWhereClause = []; + foreach ($whereClause as $columnName => $columnValue) { + $resolvedWhereClause[(string) $columnName] = $this->parseOption('whereClause.'.$columnName); } + $this->databaseConnection->update( + $table, + $databaseData, + $resolvedWhereClause, + ); + } else { + $this->databaseConnection->insert($table, $databaseData); + $insertedUid = (int) $this->databaseConnection->lastInsertId(); + $this->finisherContext->getFinisherVariableProvider()->add( + $this->shortFinisherIdentifier, + 'insertedUids.'.$iterationCount.(is_int($containerItemKey) ? '.'.$containerItemKey : ''), + $insertedUid, + ); + + $currentCount = (int) $this->finisherContext->getFinisherVariableProvider()->get( + $this->shortFinisherIdentifier, + 'countInserts.'.$iterationCount, + ); + $this->finisherContext->getFinisherVariableProvider()->addOrUpdate( + $this->shortFinisherIdentifier, + 'countInserts.'.$iterationCount, + $currentCount + 1, + ); + } + } + + private function resolveElementValue(mixed $elementValue, mixed $elementConfig): mixed + { + if ($elementValue instanceof FileReference) { + $saveFileIdentifierInsteadOfUid = (bool) ($elementConfig['saveFileIdentifierInsteadOfUid'] ?? false); + + return $saveFileIdentifierInsteadOfUid + ? $elementValue->getOriginalResource()->getCombinedIdentifier() + : $elementValue->getOriginalResource()->getProperty('uid_local'); } + + if (is_array($elementValue)) { + return implode(',', $elementValue); + } + + if ($elementValue instanceof DateTimeInterface) { + $format = $elementConfig['dateFormat'] ?? 'U'; + + return $elementValue->format($format); + } + + return $elementValue; } /** - * This will check if a element shall or can be handled - * @param mixed $elementValue - * @param array $elementsConfiguration - * @param string $elementIdentifier - * @param string $prefix - * @return array + * This will check if a element shall or can be handled. + * + * @param array $elementsConfiguration */ private function canValueBeHandled(mixed $elementValue, array $elementsConfiguration, string $elementIdentifier, string $prefix): bool { @@ -196,14 +218,14 @@ private function canValueBeHandled(mixed $elementValue, array $elementsConfigura } if ( - ($elementValue === null || $elementValue === '') + (null === $elementValue || '' === $elementValue) && isset($elementConfig['skipIfValueIsEmpty']) - && $elementConfig['skipIfValueIsEmpty'] === true + && true === $elementConfig['skipIfValueIsEmpty'] ) { return false; } - $element = $this->getElementByIdentifier($prefix . $elementIdentifier); + $element = $this->getElementByIdentifier($prefix.$elementIdentifier); if (!($element instanceof FormElementInterface) || !isset($elementConfig['mapOnDatabaseColumn'])) { return false; } diff --git a/Classes/FormElements/RepeatableContainerInterface.php b/Classes/FormElements/RepeatableContainerInterface.php index 4707240..54a8a71 100644 --- a/Classes/FormElements/RepeatableContainerInterface.php +++ b/Classes/FormElements/RepeatableContainerInterface.php @@ -2,14 +2,40 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace TRITUM\RepeatableFormElements\FormElements; -/** +/* * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. * * For the full copyright and license information, please read the * LICENSE.txt file that was distributed with this source code. */ +use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface; -interface RepeatableContainerInterface extends CompositeRenderableInterface {} +/** + * RepeatableContainerInterface. + * + * @author Konrad Michalik + */ +interface RepeatableContainerInterface extends CompositeRenderableInterface +{ + /** + * @return array + */ + public function getElements(): array; + + /** + * @return array + */ + public function getProperties(): array; +} diff --git a/Classes/Hooks/FormHooks.php b/Classes/Hooks/FormHooks.php index a74a8fb..9c72753 100644 --- a/Classes/Hooks/FormHooks.php +++ b/Classes/Hooks/FormHooks.php @@ -2,9 +2,18 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace TRITUM\RepeatableFormElements\Hooks; -/** +/* * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. * * For the full copyright and license information, please read the @@ -17,22 +26,20 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface; use TYPO3\CMS\Form\Domain\Model\Exception\DuplicateFormElementException; -use TYPO3\CMS\Form\Domain\Model\FormElements\AbstractFormElement; -use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; -use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface; -use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface; -use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; +use TYPO3\CMS\Form\Domain\Model\FormElements\{AbstractFormElement, FormElementInterface}; +use TYPO3\CMS\Form\Domain\Model\Renderable\{AbstractRenderable, CompositeRenderableInterface, RenderableInterface, RootRenderableInterface}; use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; +/** + * FormHooks. + * + * @author Konrad Michalik + */ final class FormHooks { /** - * @param FormRuntime $formRuntime - * @param CompositeRenderableInterface|null $currentPage - * @param CompositeRenderableInterface|null $lastPage - * @param array $rawRequestArguments + * @param array $rawRequestArguments * - * @return CompositeRenderableInterface|null * @throws DuplicateFormElementException */ public function afterInitializeCurrentPage( @@ -46,7 +53,7 @@ public function afterInitializeCurrentPage( } // first request - if (!$lastPage) { + if (null === $lastPage) { return $currentPage; } @@ -60,10 +67,6 @@ public function afterInitializeCurrentPage( return $currentPage; } - /** - * @param FormRuntime $formRuntime - * @param RootRenderableInterface $renderable - */ public function beforeRendering(FormRuntime $formRuntime, RootRenderableInterface $renderable): void { if ($renderable instanceof FormElementInterface) { @@ -71,8 +74,8 @@ public function beforeRendering(FormRuntime $formRuntime, RootRenderableInterfac $fluidAdditionalAttributes = $properties['fluidAdditionalAttributes'] ?? []; $fluidAdditionalAttributes['data-element-type'] = $renderable->getType(); - if ($renderable->getType() === 'DatePicker') { - $fluidAdditionalAttributes['data-element-datepicker-enabled'] = (int)$renderable->getProperties()['enableDatePicker']; + if ('DatePicker' === $renderable->getType()) { + $fluidAdditionalAttributes['data-element-datepicker-enabled'] = (int) $renderable->getProperties()['enableDatePicker']; $fluidAdditionalAttributes['data-element-datepicker-date-format'] = $renderable->getProperties()['dateFormat']; } @@ -81,13 +84,11 @@ public function beforeRendering(FormRuntime $formRuntime, RootRenderableInterfac } /** - * @param RenderableInterface $renderable - * @param FormRuntime $formRuntime - * @param array $repeatableContainerIdentifiers + * @param array $repeatableContainerIdentifiers * * @throws DuplicateFormElementException */ - protected function setRootRepeatableContainerIdentifiers( + private function setRootRepeatableContainerIdentifiers( RenderableInterface $renderable, FormRuntime $formRuntime, array $repeatableContainerIdentifiers = [], @@ -98,71 +99,85 @@ protected function setRootRepeatableContainerIdentifiers( if ($isRepeatableContainer) { $repeatableContainerIdentifiers[] = $renderable->getIdentifier(); if (!$hasOriginalIdentifier) { - $renderable->setRenderingOption('_isRootRepeatableContainer', true); - $renderable->setRenderingOption('_isReferenceContainer', true); + $renderable->setRenderingOption('_isRootRepeatableContainer', true); // @phpstan-ignore method.notFound + $renderable->setRenderingOption('_isReferenceContainer', true); // @phpstan-ignore method.notFound } } - if (!empty($repeatableContainerIdentifiers) && !$hasOriginalIdentifier) { - $newIdentifier = implode('.0.', $repeatableContainerIdentifiers) . '.0'; - if (!$isRepeatableContainer) { - $newIdentifier .= '.' . $renderable->getIdentifier(); - } - $originalIdentifier = $renderable->getIdentifier(); - $renderable->setRenderingOption('_originalIdentifier', $originalIdentifier); + if ([] !== $repeatableContainerIdentifiers && !$hasOriginalIdentifier) { + $this->rewriteRenderableIdentifier($renderable, $formRuntime, $repeatableContainerIdentifiers, $isRepeatableContainer); + } - if ($renderable instanceof AbstractFormElement && $renderable->getDefaultValue()) { - $formRuntime->getFormDefinition()->addElementDefaultValue($newIdentifier, $renderable->getDefaultValue()); + if ($renderable instanceof RepeatableContainerInterface) { + foreach ($renderable->getElements() as $childRenderable) { + $this->setRootRepeatableContainerIdentifiers($childRenderable, $formRuntime, $repeatableContainerIdentifiers); } + } + } - $formRuntime->getFormDefinition()->unregisterRenderable($renderable); - $renderable->setIdentifier($newIdentifier); - $formRuntime->getFormDefinition()->registerRenderable($renderable); + /** + * @param array $repeatableContainerIdentifiers + * + * @throws DuplicateFormElementException + */ + private function rewriteRenderableIdentifier( + RenderableInterface $renderable, + FormRuntime $formRuntime, + array $repeatableContainerIdentifiers, + bool $isRepeatableContainer, + ): void { + if (!$renderable instanceof AbstractRenderable) { + return; + } + + $newIdentifier = implode('.0.', $repeatableContainerIdentifiers).'.0'; + if (!$isRepeatableContainer) { + $newIdentifier .= '.'.$renderable->getIdentifier(); + } + $originalIdentifier = $renderable->getIdentifier(); + $renderable->setRenderingOption('_originalIdentifier', $originalIdentifier); + + if ($renderable instanceof AbstractFormElement && null !== $renderable->getDefaultValue()) { + $formRuntime->getFormDefinition()->addElementDefaultValue($newIdentifier, $renderable->getDefaultValue()); + } - $copyService = GeneralUtility::makeInstance(CopyService::class, $formRuntime); - [$originalProcessingRule] = $copyService->copyProcessingRule($originalIdentifier, $newIdentifier); + $formRuntime->getFormDefinition()->unregisterRenderable($renderable); + $renderable->setIdentifier($newIdentifier); + $formRuntime->getFormDefinition()->registerRenderable($renderable); + $copyService = GeneralUtility::makeInstance(CopyService::class, $formRuntime); + [$originalProcessingRule] = $copyService->copyProcessingRule($originalIdentifier, $newIdentifier); + + if ($renderable instanceof FormElementInterface) { /** @var ValidatorInterface $validator */ foreach ($originalProcessingRule->getValidators() as $validator) { $renderable->addValidator($validator); } - - // Legacy hook (v13 compat, no-op in v14) - foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterBuildingFinished'] ?? [] as $className) { - $hookObj = GeneralUtility::makeInstance($className); - if (method_exists($hookObj, 'afterBuildingFinished')) { - $hookObj->afterBuildingFinished($renderable); - } - } - // PSR-14 event (v13 + v14) - GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch( - new AfterBuildingFinishedEvent($renderable) - ); } - if ($renderable instanceof CompositeRenderableInterface) { - foreach ($renderable->getElements() as $childRenderable) { - $this->setRootRepeatableContainerIdentifiers($childRenderable, $formRuntime, $repeatableContainerIdentifiers); + // Legacy hook (v13 compat, no-op in v14) + foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterBuildingFinished'] ?? [] as $className) { + $hookObj = GeneralUtility::makeInstance($className); // @phpstan-ignore argument.templateType + if (method_exists($hookObj, 'afterBuildingFinished')) { + $hookObj->afterBuildingFinished($renderable); } } + // PSR-14 event (v13 + v14) + GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch( + new AfterBuildingFinishedEvent($renderable), + ); } /** * returns TRUE if the user went back to any previous step in the form. - * - * @param FormRuntime $formRuntime - * @param CompositeRenderableInterface|null $currentPage - * @param CompositeRenderableInterface|null $lastPage - * - * @return bool */ - protected function userWentBackToPreviousStep( + private function userWentBackToPreviousStep( FormRuntime $formRuntime, ?CompositeRenderableInterface $currentPage = null, ?CompositeRenderableInterface $lastPage = null, ): bool { - return $currentPage !== null - && $lastPage !== null + return null !== $currentPage + && null !== $lastPage && $currentPage->getIndex() < $lastPage->getIndex(); } } diff --git a/Classes/Service/CopyService.php b/Classes/Service/CopyService.php index f592337..d01d5d4 100644 --- a/Classes/Service/CopyService.php +++ b/Classes/Service/CopyService.php @@ -2,9 +2,18 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace TRITUM\RepeatableFormElements\Service; -/** +/* * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. * * For the full copyright and license information, please read the @@ -12,42 +21,51 @@ */ use Psr\EventDispatcher\EventDispatcherInterface; -use TRITUM\RepeatableFormElements\Event\AfterBuildingFinishedEvent; -use TRITUM\RepeatableFormElements\Event\CopyVariantEvent; +use ReflectionClass; +use TRITUM\RepeatableFormElements\Event\{AfterBuildingFinishedEvent, CopyVariantEvent}; use TRITUM\RepeatableFormElements\FormElements\RepeatableContainerInterface; +use TypeError; use TYPO3\CMS\Core\Configuration\Features; -use TYPO3\CMS\Core\Utility\ArrayUtility; -use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\{ArrayUtility, GeneralUtility}; use TYPO3\CMS\Extbase\Error\Error; use TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration; use TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface; use TYPO3\CMS\Form\Domain\Model\FormDefinition; use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; -use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface; -use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface; -use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableVariant; -use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; -use TYPO3\CMS\Form\Domain\Runtime\FormState; +use TYPO3\CMS\Form\Domain\Model\Renderable\{CompositeRenderableInterface, RenderableInterface, RenderableVariant}; +use TYPO3\CMS\Form\Domain\Runtime\{FormRuntime, FormState}; use TYPO3\CMS\Form\Mvc\ProcessingRule; use TYPO3\CMS\Form\Service\TranslationService; -class CopyService +use function array_key_exists; +use function assert; +use function count; +use function in_array; +use function is_array; + +/** + * CopyService. + * + * @author Konrad Michalik + */ +class CopyService // @phpstan-ignore complexity.classLike { protected FormRuntime $formRuntime; - protected ?FormState $formState; + protected FormState $formState; protected FormDefinition $formDefinition; + /** @var array */ protected array $repeatableContainersByOriginalIdentifier = []; + /** @var array> */ protected array $typeDefinitions = []; protected Features $features; protected EventDispatcherInterface $eventDispatcher; - /** - * @param FormRuntime $formRuntime - */ public function __construct(FormRuntime $formRuntime) { $this->formRuntime = $formRuntime; - $this->formState = $formRuntime->getFormState(); + $formState = $formRuntime->getFormState(); + assert($formState instanceof FormState, 'FormState must be available when CopyService is used'); + $this->formState = $formState; $this->formDefinition = $formRuntime->getFormDefinition(); $this->typeDefinitions = $this->formDefinition->getTypeDefinitions(); $this->features = GeneralUtility::makeInstance(Features::class); @@ -55,10 +73,9 @@ public function __construct(FormRuntime $formRuntime) } /** - * @return CopyService * @api */ - public function createCopiesFromCurrentRequest(): CopyService + public function createCopiesFromCurrentRequest(): self { $requestArguments = $this->formRuntime->getRequest()->getArguments(); $this->removeDeletedRepeatableContainersFromFormValuesByRequest($requestArguments); @@ -68,23 +85,23 @@ public function createCopiesFromCurrentRequest(): CopyService ); $this->copyRepeatableContainersFromArguments($requestArguments); + return $this; } /** - * @return CopyService * @api */ - public function createCopiesFromFormState(): CopyService + public function createCopiesFromFormState(): self { $this->copyRepeatableContainersFromArguments($this->formState->getFormValues()); + return $this; } /** - * @param string $originalFormElement - * @param string $newElementCopy * @return ProcessingRule[] + * * @internal */ public function copyProcessingRule( @@ -98,82 +115,41 @@ public function copyProcessingRule( try { $newProcessingRule->setDataType($originalProcessingRule->getDataType()); - } catch (\TypeError $error) { + } catch (TypeError) { } return [$originalProcessingRule, $newProcessingRule]; } /** - * @param array $requestArguments - * @param array $argumentPath + * @param array $requestArguments + * @param array $argumentPath */ protected function copyRepeatableContainersFromArguments( array $requestArguments, array $argumentPath = [], ): void { foreach ($requestArguments as $argumentKey => $argumentValue) { - if (is_array($argumentValue)) { - $originalContainer = $this->getRepeatableContainerByOriginalIdentifier((string)$argumentKey); - $copyIndexes = array_keys($argumentValue); - unset($copyIndexes[0]); - $argumentPath[] = $argumentKey; - - if ( - $originalContainer - && count(array_filter(array_keys($copyIndexes), 'is_string')) === 0 - ) { - $copyIndexes = ArrayUtility::sortArrayWithIntegerKeys($copyIndexes); - - if (count($argumentPath) <= 1) { - $referenceContainer = $originalContainer; - } else { - $referenceContainerPath = $argumentPath; - $referenceContainerPath[] = 0; - $referenceContainerIdentifier = implode('.', $referenceContainerPath); - $referenceContainer = $this->formDefinition->getElementByIdentifier($referenceContainerIdentifier); - } - - $firstReferenceContainer = $referenceContainer; - $firstReferenceContainer->setRenderingOption('_isReferenceContainer', true); - $firstReferenceContainer->setRenderingOption('_copyMother', $originalContainer->getIdentifier()); - - $minimumCopies = (int)$firstReferenceContainer->getProperties()['minimumCopies']; - $maximumCopies = (int)$firstReferenceContainer->getProperties()['maximumCopies']; - - $copyNumber = 1; - foreach ($copyIndexes as $copyIndex) { - $contextPath = $argumentPath; - $contextPath[] = $copyIndex; - $newIdentifier = implode('.', $contextPath); - - $referenceContainer = $this->copyRepeatableContainer($originalContainer, $referenceContainer, $newIdentifier); - $referenceContainer->setRenderingOption('_copyReference', $firstReferenceContainer->getIdentifier()); - - if ($copyNumber > $maximumCopies) { - $this->addError($referenceContainer, 1518701681, 'The maximum number of copies has been reached'); - } - $copyNumber++; - } - - if ($copyNumber - 1 < $minimumCopies) { - $this->addError($firstReferenceContainer, 1518701682, 'The minimum number of copies has not yet been reached'); - } - } + if (!is_array($argumentValue)) { + continue; + } + + $originalContainer = $this->getRepeatableContainerByOriginalIdentifier($argumentKey); + $copyIndexes = array_keys($argumentValue); + unset($copyIndexes[0]); + $argumentPath[] = $argumentKey; - $this->copyRepeatableContainersFromArguments($argumentValue, $argumentPath); - array_pop($argumentPath); + if ($originalContainer instanceof RepeatableContainerInterface + && [] === array_filter(array_keys($copyIndexes), is_string(...)) // @phpstan-ignore identical.alwaysTrue + ) { + $this->processCopyIndexes($originalContainer, $copyIndexes, $argumentPath); } + + $this->copyRepeatableContainersFromArguments($argumentValue, $argumentPath); + array_pop($argumentPath); } } - /** - * @param RepeatableContainerInterface $copyFromContainer - * @param RepeatableContainerInterface $moveAfterContainer - * @param string $newIdentifier - * - * @return RepeatableContainerInterface - */ protected function copyRepeatableContainer( RepeatableContainerInterface $copyFromContainer, RepeatableContainerInterface $moveAfterContainer, @@ -181,13 +157,15 @@ protected function copyRepeatableContainer( ): RepeatableContainerInterface { $typeName = $copyFromContainer->getType(); $implementationClassName = $this->typeDefinitions[$typeName]['implementationClassName']; - $parentRenderableForNewContainer = $moveAfterContainer->getParentRenderable(); + $parentRenderable = $moveAfterContainer->getParentRenderable(); + assert($parentRenderable instanceof CompositeRenderableInterface); - $newContainer = GeneralUtility::makeInstance($implementationClassName, $newIdentifier, $typeName); - $this->copyOptions($newContainer, $copyFromContainer); + /** @var RepeatableContainerInterface $newContainer */ + $newContainer = GeneralUtility::makeInstance($implementationClassName, $newIdentifier, $typeName); // @phpstan-ignore argument.templateType + $this->copyOptions($newContainer, $copyFromContainer); // @phpstan-ignore argument.type, argument.type - $parentRenderableForNewContainer->addElement($newContainer); - $parentRenderableForNewContainer->moveElementAfter($newContainer, $moveAfterContainer); + $parentRenderable->addElement($newContainer); // @phpstan-ignore method.notFound + $parentRenderable->moveElementAfter($newContainer, $moveAfterContainer); // @phpstan-ignore method.notFound $this->dispatchAfterBuildingFinished($newContainer); @@ -198,24 +176,20 @@ protected function copyRepeatableContainer( return $newContainer; } - /** - * @param FormElementInterface $newElementCopy - * @param FormElementInterface $originalFormElement - */ protected function copyOptions( FormElementInterface $newElementCopy, FormElementInterface $originalFormElement, ): void { - $newElementCopy->setLabel($originalFormElement->getLabel()); + $newElementCopy->setLabel($originalFormElement->getLabel()); // @phpstan-ignore method.notFound $newElementCopy->setDefaultValue($originalFormElement->getDefaultValue()); foreach ($originalFormElement->getProperties() as $key => $value) { $newElementCopy->setProperty($key, $value); } foreach ($originalFormElement->getRenderingOptions() as $key => $value) { if ( - $key === '_isRootRepeatableContainer' - || $key === '_originalIdentifier' - || $key === '_isReferenceContainer' + '_isRootRepeatableContainer' === $key + || '_originalIdentifier' === $key + || '_isReferenceContainer' === $key ) { continue; } @@ -230,12 +204,6 @@ protected function copyOptions( } } - /** - * @param FormElementInterface $originalFormElement - * @param CompositeRenderableInterface $parentFormElementCopy - * @param string $identifierOriginal - * @param string $identifierReplacement - */ protected function createNestedElements( FormElementInterface $originalFormElement, CompositeRenderableInterface $parentFormElementCopy, @@ -243,7 +211,7 @@ protected function createNestedElements( string $identifierReplacement, ): void { $newIdentifier = str_replace($identifierOriginal, $identifierReplacement, $originalFormElement->getIdentifier()); - $newFormElement = $parentFormElementCopy->createElement( + $newFormElement = $parentFormElementCopy->createElement( // @phpstan-ignore method.notFound $newIdentifier, $originalFormElement->getType(), ); @@ -254,47 +222,38 @@ protected function createNestedElements( $this->dispatchAfterBuildingFinished($newFormElement); if ($originalFormElement instanceof CompositeRenderableInterface) { - foreach ($originalFormElement->getElements() as $originalChildFormElement) { + foreach ($originalFormElement->getElements() as $originalChildFormElement) { // @phpstan-ignore method.notFound $this->createNestedElements($originalChildFormElement, $newFormElement, $identifierOriginal, $identifierReplacement); } } } - /** - * @param string $originalIdentifier - * @return RepeatableContainerInterface|null - */ protected function getRepeatableContainerByOriginalIdentifier(string $originalIdentifier): ?RepeatableContainerInterface { - if ( - !isset($this->repeatableContainersByOriginalIdentifier[$originalIdentifier]) - || $this->repeatableContainersByOriginalIdentifier[$originalIdentifier] === null - ) { - foreach ($this->formDefinition->getRenderablesRecursively() as $formElement) { - $renderingOptions = $formElement->getRenderingOptions(); - if ( - $formElement instanceof RepeatableContainerInterface - && ($renderingOptions['_originalIdentifier'] ?? null) === $originalIdentifier - && (bool)$renderingOptions['_isRootRepeatableContainer'] === true - ) { - $this->repeatableContainersByOriginalIdentifier[$originalIdentifier] = $formElement; - } - } - if (!isset($this->repeatableContainersByOriginalIdentifier[$originalIdentifier])) { - $this->repeatableContainersByOriginalIdentifier[$originalIdentifier] = null; + if (array_key_exists($originalIdentifier, $this->repeatableContainersByOriginalIdentifier)) { + return $this->repeatableContainersByOriginalIdentifier[$originalIdentifier]; + } + + foreach ($this->formDefinition->getRenderablesRecursively() as $formElement) { + $renderingOptions = $formElement->getRenderingOptions(); + if ( + $formElement instanceof RepeatableContainerInterface + && ($renderingOptions['_originalIdentifier'] ?? null) === $originalIdentifier + && (bool) ($renderingOptions['_isRootRepeatableContainer'] ?? false) + ) { + $this->repeatableContainersByOriginalIdentifier[$originalIdentifier] = $formElement; + + return $formElement; } } - return $this->repeatableContainersByOriginalIdentifier[$originalIdentifier]; + $this->repeatableContainersByOriginalIdentifier[$originalIdentifier] = null; + + return null; } - /** - * @param FormElementInterface $formElement - * @param int $timestamp - * @param string $defaultMessage - */ protected function addError( - FormElementInterface $formElement, + FormElementInterface|RepeatableContainerInterface $formElement, int $timestamp, string $defaultMessage = '', ): void { @@ -316,49 +275,37 @@ protected function addError( } /** - * @param array $requestArguments - * @param array $argumentPath + * @param array $requestArguments + * @param array $argumentPath */ protected function removeDeletedRepeatableContainersFromFormValuesByRequest( array $requestArguments, array $argumentPath = [], ): void { foreach ($requestArguments as $argumentKey => $argumentValue) { - if (is_array($argumentValue)) { - $originalContainer = $this->getRepeatableContainerByOriginalIdentifier((string)$argumentKey); - $argumentPath[] = $argumentKey; - $copyIndexes = array_keys($argumentValue); - - if ( - $originalContainer - && count(array_filter(array_keys($copyIndexes), 'is_string')) === 0 - ) { - $currentArgumentPath = implode('.', $argumentPath); - $formValue = $this->formState->getFormValue($currentArgumentPath); - if ($formValue !== null) { - foreach ($formValue as $key => $_) { - if (!in_array($key, $copyIndexes)) { - unset($formValue[$key]); - } - } - $this->formState->setFormValue($currentArgumentPath, $formValue); - } - } + if (!is_array($argumentValue)) { + continue; + } + + $originalContainer = $this->getRepeatableContainerByOriginalIdentifier($argumentKey); + $argumentPath[] = $argumentKey; + $copyIndexes = array_keys($argumentValue); - $this->removeDeletedRepeatableContainersFromFormValuesByRequest($argumentValue, $argumentPath); - array_pop($argumentPath); + if ($originalContainer instanceof RepeatableContainerInterface + && [] === array_filter(array_keys($copyIndexes), is_string(...)) // @phpstan-ignore identical.alwaysTrue + ) { + $this->pruneDeletedContainerValues(implode('.', $argumentPath), $copyIndexes); } + + $this->removeDeletedRepeatableContainersFromFormValuesByRequest($argumentValue, $argumentPath); + array_pop($argumentPath); } } /** * This function fetches variants of the original form element and copies them into the * new form element. - * Extendable by listening for @see CopyVariantEvent - * - * @param FormElementInterface $originalFormElement - * @param FormElementInterface $newFormElement - * @param string $newIdentifier + * Extendable by listening for @see CopyVariantEvent. */ protected function copyVariants( FormElementInterface $originalFormElement, @@ -369,15 +316,15 @@ protected function copyVariants( return; } - $originalVariants = $originalFormElement->getVariants(); + $originalVariants = $originalFormElement->getVariants(); // @phpstan-ignore method.notFound foreach ($originalVariants as $originalIdentifier => $originalVariant) { // make sure that we only copy variants that are missing in the copied element if ($originalVariant instanceof RenderableVariant - && !in_array($originalIdentifier, array_keys($newFormElement->getVariants())) + && !in_array($originalIdentifier, array_keys($newFormElement->getVariants()), true) // @phpstan-ignore method.notFound ) { // variant properties are protected and class is marked internal, // so we use reflection - $reflectionClass = new \ReflectionClass(RenderableVariant::class); + $reflectionClass = new ReflectionClass(RenderableVariant::class); $propOption = $reflectionClass->getProperty('options'); $propCondition = $reflectionClass->getProperty('condition'); $options = $propOption->getValue($originalVariant); @@ -395,7 +342,7 @@ protected function copyVariants( } $options = $event->getOptions(); - $newFormElement->createVariant($options); + $newFormElement->createVariant($options); // @phpstan-ignore method.notFound } } } @@ -408,7 +355,7 @@ protected function copyVariants( protected function dispatchAfterBuildingFinished(RenderableInterface $renderable): void { foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterBuildingFinished'] ?? [] as $className) { - $hookObj = GeneralUtility::makeInstance($className); + $hookObj = GeneralUtility::makeInstance($className); // @phpstan-ignore argument.templateType if (method_exists($hookObj, 'afterBuildingFinished')) { $hookObj->afterBuildingFinished($renderable); } @@ -416,4 +363,84 @@ protected function dispatchAfterBuildingFinished(RenderableInterface $renderable $this->eventDispatcher->dispatch(new AfterBuildingFinishedEvent($renderable)); } + + /** + * @param array $copyIndexes + */ + private function pruneDeletedContainerValues(string $formValuePath, array $copyIndexes): void + { + $formValue = $this->formState->getFormValue($formValuePath); + if (!is_array($formValue)) { + return; + } + + foreach ($formValue as $key => $_) { + if (!in_array($key, $copyIndexes, true)) { + unset($formValue[$key]); + } + } + $this->formState->setFormValue($formValuePath, $formValue); + } + + /** + * @param array $copyIndexes + * @param array $argumentPath + */ + private function processCopyIndexes( + RepeatableContainerInterface $originalContainer, + array $copyIndexes, + array $argumentPath, + ): void { + $copyIndexes = ArrayUtility::sortArrayWithIntegerKeys($copyIndexes); + + $referenceContainer = $this->resolveReferenceContainer($originalContainer, $argumentPath); + if (!$referenceContainer instanceof RepeatableContainerInterface) { + return; + } + + $firstReferenceContainer = $referenceContainer; + $firstReferenceContainer->setRenderingOption('_isReferenceContainer', true); // @phpstan-ignore method.notFound + $firstReferenceContainer->setRenderingOption('_copyMother', $originalContainer->getIdentifier()); // @phpstan-ignore method.notFound + + $minimumCopies = (int) ($firstReferenceContainer->getProperties()['minimumCopies'] ?? 0); + $maximumCopies = (int) ($firstReferenceContainer->getProperties()['maximumCopies'] ?? 0); + + $copyNumber = 1; + foreach ($copyIndexes as $copyIndex) { + $contextPath = $argumentPath; + $contextPath[] = $copyIndex; + $newIdentifier = implode('.', $contextPath); + + $referenceContainer = $this->copyRepeatableContainer($originalContainer, $referenceContainer, $newIdentifier); + $referenceContainer->setRenderingOption('_copyReference', $firstReferenceContainer->getIdentifier()); // @phpstan-ignore method.notFound + + if ($copyNumber > $maximumCopies) { + $this->addError($referenceContainer, 1518701681, 'The maximum number of copies has been reached'); + } + ++$copyNumber; + } + + if ($copyNumber - 1 < $minimumCopies) { + $this->addError($firstReferenceContainer, 1518701682, 'The minimum number of copies has not yet been reached'); + } + } + + /** + * @param array $argumentPath + */ + private function resolveReferenceContainer( + RepeatableContainerInterface $originalContainer, + array $argumentPath, + ): ?RepeatableContainerInterface { + if (count($argumentPath) <= 1) { + return $originalContainer; + } + + $referenceContainerPath = $argumentPath; + $referenceContainerPath[] = 0; + $referenceContainerIdentifier = implode('.', $referenceContainerPath); + $element = $this->formDefinition->getElementByIdentifier($referenceContainerIdentifier); + + return $element instanceof RepeatableContainerInterface ? $element : null; + } } diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE From 36847ac13ec0e252154e2fffbd8d72859d307fe4 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 08:48:30 +0200 Subject: [PATCH 12/21] test: add unit tests for events and event listeners --- .../Event/AfterBuildingFinishedEventTest.php | 38 ++++++ Tests/Unit/Event/CopyVariantEventTest.php | 78 +++++++++++ ...AdaptVariantConditionEventListenerTest.php | 121 ++++++++++++++++++ composer.json | 32 ++--- 4 files changed, 246 insertions(+), 23 deletions(-) create mode 100644 Tests/Unit/Event/AfterBuildingFinishedEventTest.php create mode 100644 Tests/Unit/Event/CopyVariantEventTest.php create mode 100644 Tests/Unit/EventListener/AdaptVariantConditionEventListenerTest.php diff --git a/Tests/Unit/Event/AfterBuildingFinishedEventTest.php b/Tests/Unit/Event/AfterBuildingFinishedEventTest.php new file mode 100644 index 0000000..7e7940e --- /dev/null +++ b/Tests/Unit/Event/AfterBuildingFinishedEventTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TRITUM\RepeatableFormElements\Tests\Unit\Event; + +use PHPUnit\Framework\Attributes\{CoversClass, Test}; +use PHPUnit\Framework\TestCase; +use TRITUM\RepeatableFormElements\Event\AfterBuildingFinishedEvent; +use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface; + +/** + * AfterBuildingFinishedEventTest. + * + * @author Konrad Michalik + */ +#[CoversClass(AfterBuildingFinishedEvent::class)] +final class AfterBuildingFinishedEventTest extends TestCase +{ + #[Test] + public function constructorExposesRenderable(): void + { + $renderable = self::createStub(RenderableInterface::class); + + $event = new AfterBuildingFinishedEvent($renderable); + + self::assertSame($renderable, $event->renderable); + } +} diff --git a/Tests/Unit/Event/CopyVariantEventTest.php b/Tests/Unit/Event/CopyVariantEventTest.php new file mode 100644 index 0000000..85cfe2c --- /dev/null +++ b/Tests/Unit/Event/CopyVariantEventTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TRITUM\RepeatableFormElements\Tests\Unit\Event; + +use PHPUnit\Framework\Attributes\{CoversClass, Test}; +use PHPUnit\Framework\TestCase; +use TRITUM\RepeatableFormElements\Event\CopyVariantEvent; +use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; + +/** + * CopyVariantEventTest. + * + * @author Konrad Michalik + */ +#[CoversClass(CopyVariantEvent::class)] +final class CopyVariantEventTest extends TestCase +{ + private CopyVariantEvent $subject; + private FormElementInterface $originalFormElement; + private FormElementInterface $newFormElement; + + protected function setUp(): void + { + $this->originalFormElement = self::createStub(FormElementInterface::class); + $this->newFormElement = self::createStub(FormElementInterface::class); + + $this->subject = new CopyVariantEvent( + ['condition' => 'true', 'identifier' => 'variant-1'], + $this->originalFormElement, + $this->newFormElement, + 'container.1.field-1', + ); + } + + #[Test] + public function constructorSetsAllProperties(): void + { + self::assertSame(['condition' => 'true', 'identifier' => 'variant-1'], $this->subject->getOptions()); + self::assertSame($this->originalFormElement, $this->subject->getOriginalFormElement()); + self::assertSame($this->newFormElement, $this->subject->getNewFormElement()); + self::assertSame('container.1.field-1', $this->subject->getNewIdentifier()); + } + + #[Test] + public function variantIsEnabledByDefault(): void + { + self::assertTrue($this->subject->isVariantEnabled()); + } + + #[Test] + public function setVariantEnabledChangesState(): void + { + $this->subject->setVariantEnabled(false); + + self::assertFalse($this->subject->isVariantEnabled()); + } + + #[Test] + public function setOptionsReplacesOptions(): void + { + $newOptions = ['condition' => 'false', 'identifier' => 'variant-2']; + + $this->subject->setOptions($newOptions); + + self::assertSame($newOptions, $this->subject->getOptions()); + } +} diff --git a/Tests/Unit/EventListener/AdaptVariantConditionEventListenerTest.php b/Tests/Unit/EventListener/AdaptVariantConditionEventListenerTest.php new file mode 100644 index 0000000..852c0a6 --- /dev/null +++ b/Tests/Unit/EventListener/AdaptVariantConditionEventListenerTest.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TRITUM\RepeatableFormElements\Tests\Unit\EventListener; + +use PHPUnit\Framework\Attributes\{CoversClass, Test}; +use PHPUnit\Framework\TestCase; +use TRITUM\RepeatableFormElements\Event\CopyVariantEvent; +use TRITUM\RepeatableFormElements\EventListener\AdaptVariantConditionEventListener; +use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; + +/** + * AdaptVariantConditionEventListenerTest. + * + * @author Konrad Michalik + */ +#[CoversClass(AdaptVariantConditionEventListener::class)] +final class AdaptVariantConditionEventListenerTest extends TestCase +{ + private AdaptVariantConditionEventListener $subject; + + protected function setUp(): void + { + $this->subject = new AdaptVariantConditionEventListener(); + } + + #[Test] + public function conditionIdentifierIsReplacedWithNewIdentifier(): void + { + $originalElement = self::createStub(FormElementInterface::class); + $originalElement->method('getIdentifier')->willReturn('repeatablecontainer-1.0.checkbox-1'); + + $event = new CopyVariantEvent( + ['condition' => "traverse(formValues, 'repeatablecontainer-1.0.checkbox-1') == 1"], + $originalElement, + self::createStub(FormElementInterface::class), + 'repeatablecontainer-1.1.checkbox-1', + ); + + ($this->subject)($event); + + self::assertSame( + "traverse(formValues, 'repeatablecontainer-1.1.checkbox-1') == 1", + $event->getOptions()['condition'], + ); + } + + #[Test] + public function pathStyleIdentifierIsAlsoReplaced(): void + { + $originalElement = self::createStub(FormElementInterface::class); + $originalElement->method('getIdentifier')->willReturn('container.0.field-1'); + + $event = new CopyVariantEvent( + ['condition' => "traverse(formValues, 'container/0/field-1') == 'yes'"], + $originalElement, + self::createStub(FormElementInterface::class), + 'container.1.field-1', + ); + + ($this->subject)($event); + + self::assertSame( + "traverse(formValues, 'container/1/field-1') == 'yes'", + $event->getOptions()['condition'], + ); + } + + #[Test] + public function conditionWithoutMatchingIdentifierRemainsUnchanged(): void + { + $originalElement = self::createStub(FormElementInterface::class); + $originalElement->method('getIdentifier')->willReturn('container.0.field-1'); + + $condition = "traverse(formValues, 'unrelated-field') == 1"; + $event = new CopyVariantEvent( + ['condition' => $condition], + $originalElement, + self::createStub(FormElementInterface::class), + 'container.1.field-1', + ); + + ($this->subject)($event); + + self::assertSame($condition, $event->getOptions()['condition']); + } + + #[Test] + public function otherOptionsArePreserved(): void + { + $originalElement = self::createStub(FormElementInterface::class); + $originalElement->method('getIdentifier')->willReturn('container.0.field-1'); + + $event = new CopyVariantEvent( + [ + 'condition' => "traverse(formValues, 'container.0.field-1') == 1", + 'identifier' => 'variant-1', + 'properties' => ['fluidAdditionalAttributes' => ['class' => 'hidden']], + ], + $originalElement, + self::createStub(FormElementInterface::class), + 'container.1.field-1', + ); + + ($this->subject)($event); + + $options = $event->getOptions(); + self::assertSame('variant-1', $options['identifier']); + self::assertSame(['fluidAdditionalAttributes' => ['class' => 'hidden']], $options['properties']); + } +} diff --git a/composer.json b/composer.json index b8aaab7..944a5dd 100644 --- a/composer.json +++ b/composer.json @@ -1,32 +1,13 @@ { - "name": "tritum/repeatable-form-elements", + "name": "move-elevator/typo3-repeatable-form-elements", "description": "Adds a new form element which allows the editor to create new container elements with any type fields in them. In the frontend, a user can create any number of new containers. This is an extension for TYPO3 CMS.", "license": "GPL-2.0-or-later", "type": "typo3-cms-extension", "authors": [ { - "name": "Ralf Zimmermann", - "email": "r.zimmermann@dreistrom.land", - "homepage": "https://dreistrom.land", - "role": "Developer" - }, - { - "name": "Falko Linke", - "email": "f.linke@dreistrom.land", - "homepage": "https://dreistrom.land", - "role": "Developer" - }, - { - "name": "Elias HΓ€ußler", - "email": "elias@haeussler.dev", - "homepage": "https://haeussler.dev", - "role": "Developer" - }, - { - "name": "Christian Seyfferth", - "email": "c.seyfferth@dreistrom.land", - "homepage": "https://dreistrom.land", - "role": "Developer" + "name": "Konrad Michalik", + "email": "km@move-elevator.de", + "role": "Maintainer" } ], "require": { @@ -46,6 +27,11 @@ "TRITUM\\RepeatableFormElements\\": "Classes/" } }, + "autoload-dev": { + "psr-4": { + "TRITUM\\RepeatableFormElements\\Tests\\": "Tests/" + } + }, "config": { "allow-plugins": { "eliashaeussler/version-bumper": true, From a81b8469270710a2ecabcc70f729c4509688c568 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 08:48:39 +0200 Subject: [PATCH 13/21] chore: add project metadata, CI release workflow and tooling --- .gitattributes | 21 ++++++++++++--- .github/workflows/release.yml | 13 +++++++++ .github/workflows/tests.yml | 2 +- .gitignore | 5 ++++ CODEOWNERS | 1 + CODE_OF_CONDUCT.md | 7 +++++ SECURITY.md | 19 +++++++++++++ Tests/CGL/composer-dependency-analyser.php | 31 ++++++++++++++++++++++ packaging_exclude.php | 9 +++++++ renovate.json | 7 +++++ 10 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 CODEOWNERS create mode 100644 CODE_OF_CONDUCT.md create mode 100644 SECURITY.md create mode 100644 Tests/CGL/composer-dependency-analyser.php create mode 100644 renovate.json diff --git a/.gitattributes b/.gitattributes index 3e4645c..45bca05 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,18 @@ -* text=auto -/.gitattributes export-ignore -/.gitignore export-ignore +* text=auto +/.ddev export-ignore +/.github export-ignore +/Tests export-ignore +/Documentation export-ignore +/docs export-ignore +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/CODEOWNERS export-ignore +/CODE_OF_CONDUCT.md export-ignore +/CONTRIBUTING.md export-ignore +/SECURITY.md export-ignore +/composer.lock export-ignore +/packaging_exclude.php export-ignore +/phpunit.xml export-ignore +/renovate.json export-ignore +/version-bumper.yaml export-ignore diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1c23831 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,13 @@ +name: Release +on: + push: + tags: + - '*' + +jobs: + ter-publish: + uses: konradmichalik/reusable-github-actions/.github/workflows/release-typo3.yml@main + secrets: + typo3-api-token: ${{ secrets.TYPO3_API_TOKEN }} + with: + typo3-extension-key: 'repeatable_form_elements' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d6462fc..bfed81c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,6 +8,6 @@ jobs: tests: uses: konradmichalik/reusable-github-actions/.github/workflows/tests-typo3.yml@main with: - php-versions: '["8.2", "8.3", "8.4"]' + php-versions: '["8.2", "8.3", "8.4", "8.5"]' typo3-versions: '["13.4", "14.2"]' dependencies: '["highest", "lowest"]' diff --git a/.gitignore b/.gitignore index 58c35f6..d993391 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ .Build/ .claude +.idea /composer.lock /public +!Resources/Public /vendor +/var /Tests/CGL/vendor +.phpunit.result.cache +Tests/CGL/.php-cs-fixer.cache diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..14340ec --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* km@move-elevator.de diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..3538386 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,7 @@ +# Code of Conduct + +This project uses the +[TYPO3 Code of Conduct](https://typo3.org/community/values/code-of-conduct). + +When you contribute to this project or interact with community members, +you agree to adhere to this code of conduct. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..47e821d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +If you believe you have found a vulnerability in this project, please follow the guidelines below. + +> [!CAUTION] +> Please do not create GitHub issues for security vulnerabilities! + +## Reporting a Vulnerability + +Please contact [km@move-elevator.de](mailto:hej@move-elevator.de) instead. +Your report should include the following information: + +- The exact version or version range you analyzed. +- The TYPO3 version used during your analysis. +- A step-by-step description of how to exploit the potential vulnerability. + +> [!TIP] +> If you suspect a critical vulnerability, you may also contact the _TYPO3 Security Team_. For further details, please +> refer to [TYPO3's Security Policy](https://github.com/TYPO3/typo3/blob/main/SECURITY.md). diff --git a/Tests/CGL/composer-dependency-analyser.php b/Tests/CGL/composer-dependency-analyser.php new file mode 100644 index 0000000..f917aee --- /dev/null +++ b/Tests/CGL/composer-dependency-analyser.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Composer\Autoload; +use ShipMonk\ComposerDependencyAnalyser; + +$rootPath = dirname(__DIR__, 2); + +/** @var Autoload\ClassLoader $loader */ +$loader = require $rootPath.'/vendor/autoload.php'; +$loader->register(); + +$configuration = new ComposerDependencyAnalyser\Config\Configuration(); +$configuration + ->addPathToScan($rootPath.'/Configuration', false) + ->addPathsToExclude([ + $rootPath.'/Tests/CGL', + ]) +; + +return $configuration; diff --git a/packaging_exclude.php b/packaging_exclude.php index bcd2868..011eb94 100644 --- a/packaging_exclude.php +++ b/packaging_exclude.php @@ -2,6 +2,15 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'directories' => [ '.Build', diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..490bb52 --- /dev/null +++ b/renovate.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "github>konradmichalik/renovate-config", + "github>konradmichalik/renovate-config:typo3-extension" + ] +} From 33884ed3166ab5424f94bf5087099187d4c9425d Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 08:50:36 +0200 Subject: [PATCH 14/21] chore: normalize file headers and apply CGL formatting --- Classes/Configuration/Extension.php | 25 +++++-------------- Classes/Event/AfterBuildingFinishedEvent.php | 19 +++++++++----- .../AdaptVariantConditionEventListener.php | 17 ++++++++++--- .../BeforeRenderableIsRenderedListener.php | 18 ++++++++++--- Classes/FormElements/RepeatableContainer.php | 16 +++++++++++- Configuration/Icons.php | 9 +++++++ Configuration/JavaScriptModules.php | 11 ++++++++ Configuration/TCA/Overrides/sys_template.php | 19 +++++++++++--- ext_localconf.php | 13 +++++++++- 9 files changed, 109 insertions(+), 38 deletions(-) diff --git a/Classes/Configuration/Extension.php b/Classes/Configuration/Extension.php index 9a89806..d0097aa 100644 --- a/Classes/Configuration/Extension.php +++ b/Classes/Configuration/Extension.php @@ -3,23 +3,12 @@ declare(strict_types=1); /* - * This file is part of the TYPO3 CMS extension "repeatable_form_elements". + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. * - * Copyright (C) 2018 Ralf Zimmermann dreistrom.land AG - * Copyright (C) 2021 Elias HΓ€ußler + * (c) 2018-2026 Konrad Michalik * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ namespace TRITUM\RepeatableFormElements\Configuration; @@ -28,11 +17,9 @@ use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; /** - * Extension + * Extension. * - * @author Ralf Zimmermann | dreistrom.land AG - * @author Elias HΓ€ußler - * @author Christian Seyfferth | dreistrom.land AG + * @author Konrad Michalik * @license GPL-2.0-or-later */ final class Extension diff --git a/Classes/Event/AfterBuildingFinishedEvent.php b/Classes/Event/AfterBuildingFinishedEvent.php index 62db7fe..aaca436 100644 --- a/Classes/Event/AfterBuildingFinishedEvent.php +++ b/Classes/Event/AfterBuildingFinishedEvent.php @@ -2,20 +2,27 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace TRITUM\RepeatableFormElements\Event; use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface; /** - * Dispatched after a form renderable has been built/copied by the repeatable container logic. + * AfterBuildingFinishedEvent. * - * This event replaces the former SC_OPTIONS hook - * $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterBuildingFinished'] - * which was removed in TYPO3 v14 (Breaking #98239). + * @author Konrad Michalik */ -final class AfterBuildingFinishedEvent +final readonly class AfterBuildingFinishedEvent { public function __construct( - public readonly RenderableInterface $renderable, + public RenderableInterface $renderable, ) {} } diff --git a/Classes/EventListener/AdaptVariantConditionEventListener.php b/Classes/EventListener/AdaptVariantConditionEventListener.php index f211f29..9d65aa3 100644 --- a/Classes/EventListener/AdaptVariantConditionEventListener.php +++ b/Classes/EventListener/AdaptVariantConditionEventListener.php @@ -2,9 +2,18 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace TRITUM\RepeatableFormElements\EventListener; -/** +/* * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. * * For the full copyright and license information, please read the @@ -14,15 +23,17 @@ use TRITUM\RepeatableFormElements\Event\CopyVariantEvent; /** - * Replace original identifiers inside variant condition with identifiers of new element + * AdaptVariantConditionEventListener. * * @param CopyVariantEvent $event + * + * @author Konrad Michalik */ final class AdaptVariantConditionEventListener { public function __invoke(CopyVariantEvent $event): void { - $options = $event->getOptions(); + $options = $event->getOptions(); $originalIdentifier = $event->getOriginalFormElement()->getIdentifier(); // get path strings for identifiers for replacement in condition diff --git a/Classes/EventListener/BeforeRenderableIsRenderedListener.php b/Classes/EventListener/BeforeRenderableIsRenderedListener.php index dc301b5..7024b8c 100644 --- a/Classes/EventListener/BeforeRenderableIsRenderedListener.php +++ b/Classes/EventListener/BeforeRenderableIsRenderedListener.php @@ -2,19 +2,29 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace TRITUM\RepeatableFormElements\EventListener; use TRITUM\RepeatableFormElements\Hooks\FormHooks; use TYPO3\CMS\Form\Event\BeforeRenderableIsRenderedEvent; /** - * PSR-14 replacement for the beforeRendering SC_OPTIONS hook. - * This event listener is used in TYPO3 v14+ where the hook was removed. + * BeforeRenderableIsRenderedListener. + * + * @author Konrad Michalik */ -final class BeforeRenderableIsRenderedListener +final readonly class BeforeRenderableIsRenderedListener { public function __construct( - private readonly FormHooks $formHooks, + private FormHooks $formHooks, ) {} public function __invoke(BeforeRenderableIsRenderedEvent $event): void diff --git a/Classes/FormElements/RepeatableContainer.php b/Classes/FormElements/RepeatableContainer.php index 78d5aa9..932a1d3 100644 --- a/Classes/FormElements/RepeatableContainer.php +++ b/Classes/FormElements/RepeatableContainer.php @@ -2,9 +2,18 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace TRITUM\RepeatableFormElements\FormElements; -/** +/* * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. * * For the full copyright and license information, please read the @@ -12,4 +21,9 @@ */ use TYPO3\CMS\Form\Domain\Model\FormElements\Section; +/** + * RepeatableContainer. + * + * @author Konrad Michalik + */ class RepeatableContainer extends Section implements RepeatableContainerInterface {} diff --git a/Configuration/Icons.php b/Configuration/Icons.php index bce6345..c916032 100644 --- a/Configuration/Icons.php +++ b/Configuration/Icons.php @@ -2,6 +2,15 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider; return [ diff --git a/Configuration/JavaScriptModules.php b/Configuration/JavaScriptModules.php index 07c3a1e..76d929e 100644 --- a/Configuration/JavaScriptModules.php +++ b/Configuration/JavaScriptModules.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'dependencies' => ['form'], 'imports' => [ diff --git a/Configuration/TCA/Overrides/sys_template.php b/Configuration/TCA/Overrides/sys_template.php index 8c2d83b..0b1eb51 100644 --- a/Configuration/TCA/Overrides/sys_template.php +++ b/Configuration/TCA/Overrides/sys_template.php @@ -1,10 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +defined('TYPO3') || exit; + +call_user_func(static function (): void { + TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile( + TRITUM\RepeatableFormElements\Configuration\Extension::KEY, 'Configuration/TypoScript', 'Repeatable form configuration', ); diff --git a/ext_localconf.php b/ext_localconf.php index 2bc0d8c..c82c1e9 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,8 +1,19 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + use TRITUM\RepeatableFormElements\Configuration\Extension; -defined('TYPO3') or die(); +defined('TYPO3') || exit; Extension::addTypoScriptSetup(); Extension::registerHooks(); From 51f0538c72aec287cdc5e94f5ed2e6cebaf930ed Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 08:50:41 +0200 Subject: [PATCH 15/21] chore: update CGL tooling config, ddev template and ext_emconf --- .ddev/.setup/templates/index.php | 6 +++--- CONTRIBUTING.md | 6 +++--- Tests/CGL/.php-cs-fixer.php | 11 ++++++++++- Tests/CGL/composer.json | 2 +- Tests/CGL/rector.php | 9 +++++++++ ext_emconf.php | 6 +++--- 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/.ddev/.setup/templates/index.php b/.ddev/.setup/templates/index.php index db5d9cc..3bf44be 100644 --- a/.ddev/.setup/templates/index.php +++ b/.ddev/.setup/templates/index.php @@ -5,7 +5,7 @@ $supportedVersions = explode(' ', getenv('TYPO3_VERSIONS')); // Check if composer.json exists -$composerJsonPath = __DIR__ . '/../composer.json'; +$composerJsonPath = __DIR__.'/../composer.json'; if (file_exists($composerJsonPath)) { $composerJsonContent = file_get_contents($composerJsonPath); $composerData = json_decode($composerJsonContent, true); @@ -42,7 +42,7 @@

Run ddev install all to install all TYPO3 instances below:

{$version}
Frontend
Backend
"; } else { @@ -64,7 +64,7 @@ $filePath = $fileInfo->getPathname(); $fileName = $fileInfo->getFilename(); - if ($fileName[0] === '.' || $fileInfo->isDir()) { + if ('.' === $fileName[0] || $fileInfo->isDir()) { continue; } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb4c33b..0c11575 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,8 +12,8 @@ Please note that this project adheres to the [TYPO3 Code of Conduct](https://typ ```bash # Clone repository -git clone https://github.com/tritum/repeatable_form_elements.git -cd repeatable_form_elements +git clone https://github.com/move-elevator/typo3-repeatable-form-elements.git +cd typo3-repeatable-form-elements # Start the project with DDEV ddev start @@ -84,7 +84,7 @@ ddev all typo3 database:updateschema ## Workflow -1. Fork the repository and create a feature branch from `master`. +1. Fork the repository and create a feature branch from `main`. 2. Make your changes and ensure all linters and tests pass. 3. Use descriptive commit messages following the conventional commits format: `: ` (e.g. `feat: add nested repeatable container support`, `fix: resolve copy button state after removal`). diff --git a/Tests/CGL/.php-cs-fixer.php b/Tests/CGL/.php-cs-fixer.php index 35036db..c5ffdf8 100644 --- a/Tests/CGL/.php-cs-fixer.php +++ b/Tests/CGL/.php-cs-fixer.php @@ -2,6 +2,15 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + use KonradMichalik\PhpCsFixerPreset\Config; use KonradMichalik\PhpCsFixerPreset\Package\{Author, CopyrightRange, Type}; use KonradMichalik\PhpCsFixerPreset\Rules\Header; @@ -20,7 +29,7 @@ Header::create( 'repeatable_form_elements', Type::TYPO3Extension, - Author::create('Ralf Zimmermann', 'r.zimmermann@dreistrom.land'), + Author::create('Konrad Michalik', 'km@move-elevator.de'), CopyrightRange::from(2018), ), ) diff --git a/Tests/CGL/composer.json b/Tests/CGL/composer.json index 5ebd0b8..9c79ee0 100644 --- a/Tests/CGL/composer.json +++ b/Tests/CGL/composer.json @@ -2,7 +2,7 @@ "authors": [ { "name": "Konrad Michalik", - "email": "hej@konradmichalik.dev", + "email": "km@move-elevator.de", "role": "Maintainer" } ], diff --git a/Tests/CGL/rector.php b/Tests/CGL/rector.php index 154debc..f372af1 100644 --- a/Tests/CGL/rector.php +++ b/Tests/CGL/rector.php @@ -2,6 +2,15 @@ declare(strict_types=1); +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. + * + * (c) 2018-2026 Konrad Michalik + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + use Rector\Config\RectorConfig; use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector; use Rector\PostRector\Rector\NameImportingPostRector; diff --git a/ext_emconf.php b/ext_emconf.php index 39fecf6..3613c58 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -5,9 +5,9 @@ 'description' => 'Adds a new form element which allows the editor to create new container elements with any type fields in them. In the frontend, a user can create any number of new containers. This is an extension for TYPO3 CMS.', 'category' => 'fe', 'state' => 'stable', - 'author' => 'Ralf Zimmermann, Elias HΓ€ußler, Christian Seyfferth', - 'author_email' => 'r.zimmermann@dreistrom.land, elias@haeussler.dev, c.seyfferth@dreistrom.land', - 'version' => '6.0.0', + 'author' => 'Konrad Michalik', + 'author_email' => 'km@move-elevator.de', + 'version' => '6.0.0-alpha', 'constraints' => [ 'depends' => [ 'typo3' => '13.4.0-14.99.99', From e3c7c002b79a17b0519b7abb5b7a52137486050d Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 08:56:33 +0200 Subject: [PATCH 16/21] refactor: remove dead code, duplicate headers and unused parameters --- Classes/Finisher/SaveToDatabaseFinisher.php | 4 +- .../RepeatableContainerInterface.php | 6 --- Classes/Hooks/FormHooks.php | 39 +++++---------- Classes/Service/CopyService.php | 49 +++++++------------ 4 files changed, 32 insertions(+), 66 deletions(-) diff --git a/Classes/Finisher/SaveToDatabaseFinisher.php b/Classes/Finisher/SaveToDatabaseFinisher.php index f9dd5e3..5de9610 100644 --- a/Classes/Finisher/SaveToDatabaseFinisher.php +++ b/Classes/Finisher/SaveToDatabaseFinisher.php @@ -212,10 +212,10 @@ private function resolveElementValue(mixed $elementValue, mixed $elementConfig): */ private function canValueBeHandled(mixed $elementValue, array $elementsConfiguration, string $elementIdentifier, string $prefix): bool { - $elementConfig = $elementsConfiguration[$elementIdentifier]; - if (!isset($elementConfig)) { + if (!isset($elementsConfiguration[$elementIdentifier])) { return false; } + $elementConfig = $elementsConfiguration[$elementIdentifier]; if ( (null === $elementValue || '' === $elementValue) diff --git a/Classes/FormElements/RepeatableContainerInterface.php b/Classes/FormElements/RepeatableContainerInterface.php index 54a8a71..87bd875 100644 --- a/Classes/FormElements/RepeatableContainerInterface.php +++ b/Classes/FormElements/RepeatableContainerInterface.php @@ -13,12 +13,6 @@ namespace TRITUM\RepeatableFormElements\FormElements; -/* - * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - */ use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface; diff --git a/Classes/Hooks/FormHooks.php b/Classes/Hooks/FormHooks.php index 9c72753..43991ed 100644 --- a/Classes/Hooks/FormHooks.php +++ b/Classes/Hooks/FormHooks.php @@ -13,14 +13,6 @@ namespace TRITUM\RepeatableFormElements\Hooks; -/* - * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - */ -use Psr\EventDispatcher\EventDispatcherInterface; -use TRITUM\RepeatableFormElements\Event\AfterBuildingFinishedEvent; use TRITUM\RepeatableFormElements\FormElements\RepeatableContainerInterface; use TRITUM\RepeatableFormElements\Service\CopyService; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -48,8 +40,10 @@ public function afterInitializeCurrentPage( ?CompositeRenderableInterface $lastPage = null, array $rawRequestArguments = [], ): ?CompositeRenderableInterface { + $copyService = GeneralUtility::makeInstance(CopyService::class, $formRuntime); + foreach ($formRuntime->getPages() as $page) { - $this->setRootRepeatableContainerIdentifiers($page, $formRuntime); + $this->setRootRepeatableContainerIdentifiers($page, $formRuntime, $copyService); } // first request @@ -57,8 +51,7 @@ public function afterInitializeCurrentPage( return $currentPage; } - $copyService = GeneralUtility::makeInstance(CopyService::class, $formRuntime); - if ($this->userWentBackToPreviousStep($formRuntime, $currentPage, $lastPage)) { + if ($this->userWentBackToPreviousStep($currentPage, $lastPage)) { $copyService->createCopiesFromFormState(); } else { $copyService->createCopiesFromCurrentRequest(); @@ -75,8 +68,8 @@ public function beforeRendering(FormRuntime $formRuntime, RootRenderableInterfac $fluidAdditionalAttributes = $properties['fluidAdditionalAttributes'] ?? []; $fluidAdditionalAttributes['data-element-type'] = $renderable->getType(); if ('DatePicker' === $renderable->getType()) { - $fluidAdditionalAttributes['data-element-datepicker-enabled'] = (int) $renderable->getProperties()['enableDatePicker']; - $fluidAdditionalAttributes['data-element-datepicker-date-format'] = $renderable->getProperties()['dateFormat']; + $fluidAdditionalAttributes['data-element-datepicker-enabled'] = (int) $properties['enableDatePicker']; + $fluidAdditionalAttributes['data-element-datepicker-date-format'] = $properties['dateFormat']; } $renderable->setProperty('fluidAdditionalAttributes', $fluidAdditionalAttributes); @@ -91,6 +84,7 @@ public function beforeRendering(FormRuntime $formRuntime, RootRenderableInterfac private function setRootRepeatableContainerIdentifiers( RenderableInterface $renderable, FormRuntime $formRuntime, + CopyService $copyService, array $repeatableContainerIdentifiers = [], ): void { $isRepeatableContainer = $renderable instanceof RepeatableContainerInterface; @@ -105,12 +99,12 @@ private function setRootRepeatableContainerIdentifiers( } if ([] !== $repeatableContainerIdentifiers && !$hasOriginalIdentifier) { - $this->rewriteRenderableIdentifier($renderable, $formRuntime, $repeatableContainerIdentifiers, $isRepeatableContainer); + $this->rewriteRenderableIdentifier($renderable, $formRuntime, $copyService, $repeatableContainerIdentifiers, $isRepeatableContainer); } if ($renderable instanceof RepeatableContainerInterface) { foreach ($renderable->getElements() as $childRenderable) { - $this->setRootRepeatableContainerIdentifiers($childRenderable, $formRuntime, $repeatableContainerIdentifiers); + $this->setRootRepeatableContainerIdentifiers($childRenderable, $formRuntime, $copyService, $repeatableContainerIdentifiers); } } } @@ -123,6 +117,7 @@ private function setRootRepeatableContainerIdentifiers( private function rewriteRenderableIdentifier( RenderableInterface $renderable, FormRuntime $formRuntime, + CopyService $copyService, array $repeatableContainerIdentifiers, bool $isRepeatableContainer, ): void { @@ -145,7 +140,6 @@ private function rewriteRenderableIdentifier( $renderable->setIdentifier($newIdentifier); $formRuntime->getFormDefinition()->registerRenderable($renderable); - $copyService = GeneralUtility::makeInstance(CopyService::class, $formRuntime); [$originalProcessingRule] = $copyService->copyProcessingRule($originalIdentifier, $newIdentifier); if ($renderable instanceof FormElementInterface) { @@ -155,24 +149,13 @@ private function rewriteRenderableIdentifier( } } - // Legacy hook (v13 compat, no-op in v14) - foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterBuildingFinished'] ?? [] as $className) { - $hookObj = GeneralUtility::makeInstance($className); // @phpstan-ignore argument.templateType - if (method_exists($hookObj, 'afterBuildingFinished')) { - $hookObj->afterBuildingFinished($renderable); - } - } - // PSR-14 event (v13 + v14) - GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch( - new AfterBuildingFinishedEvent($renderable), - ); + $copyService->dispatchAfterBuildingFinished($renderable); } /** * returns TRUE if the user went back to any previous step in the form. */ private function userWentBackToPreviousStep( - FormRuntime $formRuntime, ?CompositeRenderableInterface $currentPage = null, ?CompositeRenderableInterface $lastPage = null, ): bool { diff --git a/Classes/Service/CopyService.php b/Classes/Service/CopyService.php index d01d5d4..46c8c04 100644 --- a/Classes/Service/CopyService.php +++ b/Classes/Service/CopyService.php @@ -13,13 +13,6 @@ namespace TRITUM\RepeatableFormElements\Service; -/* - * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - */ - use Psr\EventDispatcher\EventDispatcherInterface; use ReflectionClass; use TRITUM\RepeatableFormElements\Event\{AfterBuildingFinishedEvent, CopyVariantEvent}; @@ -75,7 +68,7 @@ public function __construct(FormRuntime $formRuntime) /** * @api */ - public function createCopiesFromCurrentRequest(): self + public function createCopiesFromCurrentRequest(): void { $requestArguments = $this->formRuntime->getRequest()->getArguments(); $this->removeDeletedRepeatableContainersFromFormValuesByRequest($requestArguments); @@ -85,18 +78,14 @@ public function createCopiesFromCurrentRequest(): self ); $this->copyRepeatableContainersFromArguments($requestArguments); - - return $this; } /** * @api */ - public function createCopiesFromFormState(): self + public function createCopiesFromFormState(): void { $this->copyRepeatableContainersFromArguments($this->formState->getFormValues()); - - return $this; } /** @@ -121,6 +110,23 @@ public function copyProcessingRule( return [$originalProcessingRule, $newProcessingRule]; } + /** + * Dispatch the afterBuildingFinished event/hook for a renderable. + * In v13: dispatches both the legacy SC_OPTIONS hook and the PSR-14 event. + * In v14+: the SC_OPTIONS hook no longer exists, only the PSR-14 event fires. + */ + public function dispatchAfterBuildingFinished(RenderableInterface $renderable): void + { + foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterBuildingFinished'] ?? [] as $className) { + $hookObj = GeneralUtility::makeInstance($className); // @phpstan-ignore argument.templateType + if (method_exists($hookObj, 'afterBuildingFinished')) { + $hookObj->afterBuildingFinished($renderable); + } + } + + $this->eventDispatcher->dispatch(new AfterBuildingFinishedEvent($renderable)); + } + /** * @param array $requestArguments * @param array $argumentPath @@ -347,23 +353,6 @@ protected function copyVariants( } } - /** - * Dispatch the afterBuildingFinished event/hook for a renderable. - * In v13: dispatches both the legacy SC_OPTIONS hook and the PSR-14 event. - * In v14+: the SC_OPTIONS hook no longer exists, only the PSR-14 event fires. - */ - protected function dispatchAfterBuildingFinished(RenderableInterface $renderable): void - { - foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterBuildingFinished'] ?? [] as $className) { - $hookObj = GeneralUtility::makeInstance($className); // @phpstan-ignore argument.templateType - if (method_exists($hookObj, 'afterBuildingFinished')) { - $hookObj->afterBuildingFinished($renderable); - } - } - - $this->eventDispatcher->dispatch(new AfterBuildingFinishedEvent($renderable)); - } - /** * @param array $copyIndexes */ From 9bc3a40744f078d86066412f9e2ddb4f91f1de10 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 10:01:19 +0200 Subject: [PATCH 17/21] docs: add extension icon to README, fix LICENSE link and PHP version range --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a9299b9..fa8fc5d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ > [!NOTE] > This is a fork of [tritum/repeatable_form_elements](https://github.com/tritum/repeatable_form_elements), the original extension by Ralf Zimmermann / dreistrom.land. This fork adds TYPO3 v14 compatibility, PSR-14 event migration, CI/CD infrastructure and a DDEV-based multi-version test environment. -# πŸ“¦ Repeatable Form Elements +Extension icon + +# Repeatable Form Elements A TYPO3 extension that adds a **Repeatable container** element to the TYPO3 form framework. It allows editors to create container elements with any type of fields. In the frontend, users can dynamically add and remove copies of the container. Validation is copied automatically and all form finishers are aware of the duplicated fields. @@ -16,7 +18,7 @@ A TYPO3 extension that adds a **Repeatable container** element to the TYPO3 form | Requirement | Version | |-------------|---------| -| PHP | 8.2 – 8.4 | +| PHP | 8.2 – 8.5 | | TYPO3 | 13.4 LTS, 14.x | ## πŸš€ Installation @@ -75,4 +77,4 @@ This fork is maintained by [move:elevator](https://move-elevator.de). ## πŸ“„ License -GPL-2.0-or-later β€” see [LICENSE](LICENSE.txt) for details. +GPL-2.0-or-later β€” see [LICENSE](LICENSE) for details. From 3a704cf18be417ce9a9669ea31aece988bc353f9 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 10:02:50 +0200 Subject: [PATCH 18/21] docs: center README header with icon and add shields.io badges --- README.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fa8fc5d..9b79095 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,22 @@ - -![TYPO3 extension](https://typo3-badges.dev/badge/repeatable_form_elements/extension/shields.svg) -![Total downloads](https://typo3-badges.dev/badge/repeatable_form_elements/downloads/shields.svg) +
+ +![Extension icon](Resources/Public/Icons/Extension.svg) + +# TYPO3 extension `repeatable_form_elements` + +[![Latest Stable Version](https://typo3-badges.dev/badge/repeatable_form_elements/version/shields.svg)](https://extensions.typo3.org/extension/repeatable_form_elements) +[![Supported TYPO3 versions](https://typo3-badges.dev/badge/repeatable_form_elements/typo3/shields.svg)](https://extensions.typo3.org/extension/repeatable_form_elements) +[![Supported PHP Versions](https://img.shields.io/packagist/dependency-v/move-elevator/typo3-repeatable-form-elements/php?logo=php)](https://packagist.org/packages/move-elevator/typo3-repeatable-form-elements) ![Stability](https://typo3-badges.dev/badge/repeatable_form_elements/stability/shields.svg) -![TYPO3 versions](https://typo3-badges.dev/badge/repeatable_form_elements/typo3/shields.svg) -![Latest version](https://typo3-badges.dev/badge/repeatable_form_elements/version/shields.svg) +[![CGL](https://img.shields.io/github/actions/workflow/status/move-elevator/typo3-repeatable-form-elements/cgl.yml?label=cgl&logo=github)](https://github.com/move-elevator/typo3-repeatable-form-elements/actions/workflows/cgl.yml) +[![Tests](https://img.shields.io/github/actions/workflow/status/move-elevator/typo3-repeatable-form-elements/tests.yml?label=tests&logo=github)](https://github.com/move-elevator/typo3-repeatable-form-elements/actions/workflows/tests.yml) +[![License](https://poser.pugx.org/move-elevator/typo3-repeatable-form-elements/license)](LICENSE) + +
> [!NOTE] > This is a fork of [tritum/repeatable_form_elements](https://github.com/tritum/repeatable_form_elements), the original extension by Ralf Zimmermann / dreistrom.land. This fork adds TYPO3 v14 compatibility, PSR-14 event migration, CI/CD infrastructure and a DDEV-based multi-version test environment. -Extension icon - -# Repeatable Form Elements - A TYPO3 extension that adds a **Repeatable container** element to the TYPO3 form framework. It allows editors to create container elements with any type of fields. In the frontend, users can dynamically add and remove copies of the container. Validation is copied automatically and all form finishers are aware of the duplicated fields. ## πŸ“‹ Requirements From 5858035e1c9283bd32585b84344e9e0f5c491317 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 10:03:51 +0200 Subject: [PATCH 19/21] docs: reduce extension icon size in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b79095..d490c12 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-![Extension icon](Resources/Public/Icons/Extension.svg) +Extension icon # TYPO3 extension `repeatable_form_elements` From 0e6348705fcf56ffcd7d0ec554171d51d52d6ad0 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 10:17:17 +0200 Subject: [PATCH 20/21] fix: declare php and psr/event-dispatcher as explicit dependencies --- composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composer.json b/composer.json index 944a5dd..dd51a0b 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,8 @@ } ], "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/event-dispatcher": "^1.0", "typo3/cms-core": "^13.4 || ^14.0", "typo3/cms-extbase": "^13.4 || ^14.0", "typo3/cms-form": "^13.4 || ^14.0" From b24a8873c6d584ef4b006f9fda1edd59fbd6baa5 Mon Sep 17 00:00:00 2001 From: Konrad Michalik Date: Tue, 21 Apr 2026 10:32:23 +0200 Subject: [PATCH 21/21] chore: add composer.lock file --- .gitignore | 1 - composer.lock | 9436 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 9436 insertions(+), 1 deletion(-) create mode 100644 composer.lock diff --git a/.gitignore b/.gitignore index d993391..876c4b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .Build/ .claude .idea -/composer.lock /public !Resources/Public /vendor diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..364b0b1 --- /dev/null +++ b/composer.lock @@ -0,0 +1,9436 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "238c13e92978cf0ddc25288b502c993b", + "packages": [ + { + "name": "bacon/bacon-qr-code", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2", + "reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^8.1" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.12", + "phpunit/phpunit": "^10.5.11 || ^11.0.4", + "spatie/phpunit-snapshot-assertions": "^5.1.5", + "spatie/pixelmatch-php": "^1.2.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/v3.1.1" + }, + "time": "2026-04-05T21:06:35+00:00" + }, + { + "name": "christian-riesen/base32", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/ChristianRiesen/base32.git", + "reference": "2e82dab3baa008e24a505649b0d583c31d31e894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/2e82dab3baa008e24a505649b0d583c31d31e894", + "reference": "2e82dab3baa008e24a505649b0d583c31d31e894", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8.5.13 || ^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Base32\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "description": "Base32 encoder/decoder according to RFC 4648", + "homepage": "https://github.com/ChristianRiesen/base32", + "keywords": [ + "base32", + "decode", + "encode", + "rfc4648" + ], + "support": { + "issues": "https://github.com/ChristianRiesen/base32/issues", + "source": "https://github.com/ChristianRiesen/base32/tree/1.6.0" + }, + "time": "2021-02-26T10:19:33+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": "dasprid/enum", + "version": "1.0.7", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" + }, + "time": "2025-09-16T12:23:56+00:00" + }, + { + "name": "doctrine/dbal", + "version": "4.3.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "1b31b54601346254c9e33e6ea1bd1ceae76f419f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/1b31b54601346254c9e33e6ea1bd1ceae76f419f", + "reference": "1b31b54601346254c9e33e6ea1bd1ceae76f419f", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1.5", + "php": "^8.2", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" + }, + "require-dev": { + "doctrine/coding-standard": "14.0.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2023.2", + "phpstan/phpstan": "2.1.30", + "phpstan/phpstan-phpunit": "2.0.7", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "11.5.23", + "slevomat/coding-standard": "8.24.0", + "squizlabs/php_codesniffer": "4.0.0", + "symfony/cache": "^6.3.8|^7.0|^8.0", + "symfony/console": "^5.4|^6.3|^7.0|^8.0" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\DBAL\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", + "homepage": "https://www.doctrine-project.org/projects/dbal.html", + "keywords": [ + "abstraction", + "database", + "db2", + "dbal", + "mariadb", + "mssql", + "mysql", + "oci8", + "oracle", + "pdo", + "pgsql", + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlite", + "sqlserver", + "sqlsrv" + ], + "support": { + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/4.3.5" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", + "type": "tidelift" + } + ], + "time": "2025-11-29T10:47:43+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.6", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/dda33921b198841ca8dbad2eaa5d4d34769d18cf", + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/common": "<2.9" + }, + "require-dev": { + "doctrine/coding-standard": "^14", + "phpdocumentor/guides-cli": "^1.4", + "phpstan/phpstan": "^2.1.32", + "phpunit/phpunit": "^10.5.58" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "event dispatcher", + "event manager", + "event system", + "events" + ], + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/2.1.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "type": "tidelift" + } + ], + "time": "2026-01-29T07:11:08+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "enshrined/svg-sanitize", + "version": "0.22.0", + "source": { + "type": "git", + "url": "https://github.com/darylldoyle/svg-sanitizer.git", + "reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/0afa95ea74be155a7bcd6c6fb60c276c39984500", + "reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^8.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "enshrined\\svgSanitize\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Daryll Doyle", + "email": "daryll@enshrined.co.uk" + } + ], + "description": "An SVG sanitizer for PHP", + "support": { + "issues": "https://github.com/darylldoyle/svg-sanitizer/issues", + "source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.22.0" + }, + "time": "2025-08-12T10:13:48+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v7.0.5", + "source": { + "type": "git", + "url": "https://github.com/googleapis/php-jwt.git", + "reference": "47ad26bab5e7c70ae8a6f08ed25ff83631121380" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/47ad26bab5e7c70ae8a6f08ed25ff83631121380", + "reference": "47ad26bab5e7c70ae8a6f08ed25ff83631121380", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpfastcache/phpfastcache": "^9.2", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/googleapis/php-jwt/issues", + "source": "https://github.com/googleapis/php-jwt/tree/v7.0.5" + }, + "time": "2026-04-01T20:38:03+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.10.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "MÑrk SÑgi-KazÑr", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-08-23T22:36:01+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "481557b130ef3790cf82b713667b43030dc9c957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:34:08+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.9.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "MÑrk SÑgi-KazÑr", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "MÑrk SÑgi-KazÑr", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.9.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2026-03-10T16:41:02+00:00" + }, + { + "name": "lolli42/finediff", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/lolli42/FineDiff.git", + "reference": "38a03ca581ee72d7b20bbb1d89d47c5ecf8b11ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lolli42/FineDiff/zipball/38a03ca581ee72d7b20bbb1d89d47c5ecf8b11ba", + "reference": "38a03ca581ee72d7b20bbb1d89d47c5ecf8b11ba", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2" + }, + "replace": { + "cogpowered/finediff": "*" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.89.1", + "phpstan/phpstan": "^2.1.31", + "phpunit/phpunit": "^11.5.43 || ^12.4.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "cogpowered\\FineDiff\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raymond Hill" + }, + { + "name": "Rob Crowe", + "email": "rob@cogpowered.com" + }, + { + "name": "Christian Kuhn", + "email": "lolli@schwarzbu.ch", + "role": "maintainer" + } + ], + "description": "PHP implementation of a Fine granularity Diff engine", + "homepage": "https://github.com/lolli42/FineDiff", + "keywords": [ + "diff", + "finediff", + "opcode", + "string", + "text" + ], + "support": { + "issues": "https://github.com/lolli42/FineDiff/issues", + "source": "https://github.com/lolli42/FineDiff/tree/1.1.2" + }, + "time": "2025-10-31T12:28:27+00:00" + }, + { + "name": "masterminds/html5", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "fcf91eb64359852f00d921887b219479b4f21251" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" + }, + "time": "2025-07-25T09:04:22+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/7bae67520aa9f5ecc506d646810bd40d9da54582", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^2.0", + "phpstan/phpdoc-parser": "^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26", + "shipmonk/dead-code-detector": "^0.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.3" + }, + "time": "2026-03-18T20:49:53+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + }, + "time": "2026-01-06T21:53:42+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" + }, + "time": "2026-01-25T14:56:51+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+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": "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/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/http-server-handler", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side request handler", + "keywords": [ + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" + ], + "support": { + "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2" + }, + "time": "2023-04-10T20:06:20+00:00" + }, + { + "name": "psr/http-server-middleware", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side middleware", + "keywords": [ + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2" + }, + "time": "2023-04-11T06:14:47+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": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "symfony/cache", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "467464da294734b0fb17e853e5712abc8470f819" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/467464da294734b0fb17e853e5712abc8470f819", + "reference": "467464da294734b0fb17e853e5712abc8470f819", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^3.6", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.4|^7.0|^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "ext-redis": "<6.1", + "ext-relay": "<0.12.1", + "symfony/dependency-injection": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/var-dumper": "<6.4" + }, + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "classmap": [ + "Traits/ValueWrapper.php" + ], + "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 extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "support": { + "source": "https://github.com/symfony/cache/tree/v7.4.8" + }, + "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-30T15:15:47+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/5d68a57d66910405e5c0b63d6f0af941e66fc868", + "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^3.0" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-13T15:25:07+00:00" + }, + { + "name": "symfony/clock", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/674fa3b98e21531dd040e613479f5f6fa8f32111", + "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "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": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/config", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "2d19dde43fa2ff720b9a40763ace7226594f503b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/2d19dde43fa2ff720b9a40763ace7226594f503b", + "reference": "2d19dde43fa2ff720b9a40763ace7226594f503b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^7.1|^8.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "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": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/console", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", + "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "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/v7.4.8" + }, + "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-30T13:54:39+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "f7025fd7b687c240426562f86ada06a93b1e771d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f7025fd7b687c240426562f86ada06a93b1e771d", + "reference": "f7025fd7b687c240426562f86ada06a93b1e771d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^3.6", + "symfony/var-exporter": "^6.4.20|^7.2.5|^8.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2", + "symfony/config": "<6.4", + "symfony/finder": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "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": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v7.4.8" + }, + "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-31T06:50:29+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-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.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/doctrine-messenger", + "version": "v7.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/doctrine-messenger.git", + "reference": "a429cd95983eaea2371ea279bed3b8a93b9ecdd3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/a429cd95983eaea2371ea279bed3b8a93b9ecdd3", + "reference": "a429cd95983eaea2371ea279bed3b8a93b9ecdd3", + "shasum": "" + }, + "require": { + "doctrine/dbal": "^3.6|^4", + "php": ">=8.2", + "symfony/messenger": "^7.2|^8.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/persistence": "<1.3" + }, + "require-dev": { + "doctrine/persistence": "^1.3|^2|^3", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0" + }, + "type": "symfony-messenger-bridge", + "autoload": { + "psr-4": { + "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\": "" + }, + "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": "Symfony Doctrine Messenger Bridge", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/doctrine-messenger/tree/v7.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-02-18T09:40:04+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "f57b899fa736fd71121168ef268f23c206083f0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f57b899fa736fd71121168ef268f23c206083f0a", + "reference": "f57b899fa736fd71121168ef268f23c206083f0a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.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": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0|^8.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/v7.4.8" + }, + "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-30T13:54:39+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "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.6-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.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/expression-language", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/expression-language.git", + "reference": "87ff95687748f4af65e4d5a6e917d448ec52aa83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/87ff95687748f4af65e4d5a6e917d448ec52aa83", + "reference": "87ff95687748f4af65e4d5a6e917d448ec52aa83", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ExpressionLanguage\\": "" + }, + "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 engine that can compile and evaluate expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/expression-language/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "58b9790d12f9670b7f53a1c1738febd3108970a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/58b9790d12f9670b7f53a1c1738febd3108970a5", + "reference": "58b9790d12f9670b7f53a1c1738febd3108970a5", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "e0be088d22278583a82da281886e8c3592fbf149" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149", + "reference": "e0be088d22278583a82da281886e8c3592fbf149", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "9381209597ec66c25be154cbf2289076e64d1eab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9381209597ec66c25be154cbf2289076e64d1eab", + "reference": "9381209597ec66c25be154cbf2289076e64d1eab", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.1" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "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": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/f6ea532250b476bfc1b56699b388a1bdbf168f62", + "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "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": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/messenger", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/messenger.git", + "reference": "ddf5ab29bc0329ece30e16f01c86abb6241e92d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/messenger/zipball/ddf5ab29bc0329ece30e16f01c86abb6241e92d8", + "reference": "ddf5ab29bc0329ece30e16f01c86abb6241e92d8", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/console": "<7.2", + "symfony/event-dispatcher": "<6.4", + "symfony/event-dispatcher-contracts": "<2.5", + "symfony/framework-bundle": "<6.4", + "symfony/http-kernel": "<7.3", + "symfony/lock": "<7.4", + "symfony/serializer": "<6.4.32|>=7.3,<7.3.10|>=7.4,<7.4.4|>=8.0,<8.0.4" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/console": "^7.2|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^7.3|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.32|~7.3.10|^7.4.4|^8.0.4", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Messenger\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Samuel Roze", + "email": "samuel.roze@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps applications send and receive messages to/from other applications or via message queues", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/messenger/tree/v7.4.8" + }, + "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-30T12:55:43+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "6df02f99998081032da3407a8d6c4e1dcb5d4379" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/6df02f99998081032da3407a8d6c4e1dcb5d4379", + "reference": "6df02f99998081032da3407a8d6c4e1dcb5d4379", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "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": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v7.4.8" + }, + "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-30T14:11:46+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/2888fcdc4dc2fd5f7c7397be78631e8af12e02b4", + "reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "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/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.36.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.36.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.36.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/ad1b7b9092976d6c948b8a187cec9faaea9ec1df", + "reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df", + "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.36.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-idn", + "version": "v1.36.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "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\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.36.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-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.36.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.36.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.36.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.36.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/polyfill-php83", + "version": "v1.36.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/3600c2cb22399e25bb226e4a135ce91eeb2a6149", + "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149", + "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\\Php83\\": "" + }, + "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.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.36.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/polyfill-uuid", + "version": "v1.36.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.36.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/process", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "60f19cd3badc8de688421e21e4305eba50f8089a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/60f19cd3badc8de688421e21e4305eba50f8089a", + "reference": "60f19cd3badc8de688421e21e4305eba50f8089a", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/property-access", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-access.git", + "reference": "b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-access/zipball/b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc", + "reference": "b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/property-info": "^6.4.32|~7.3.10|^7.4.4|^8.0.4" + }, + "require-dev": { + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4.1|^7.0.1|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyAccess\\": "" + }, + "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 functions to read and write from/to an object or array using a simple string notation", + "homepage": "https://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property-path", + "reflection" + ], + "support": { + "source": "https://github.com/symfony/property-access/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/property-info", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-info.git", + "reference": "ac5e82528b986c4f7cfccbf7764b5d2e824d6175" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-info/zipball/ac5e82528b986c4f7cfccbf7764b5d2e824d6175", + "reference": "ac5e82528b986c4f7cfccbf7764b5d2e824d6175", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/type-info": "^7.4.7|^8.0.7" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/cache": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/serializer": "<6.4" + }, + "require-dev": { + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "phpstan/phpdoc-parser": "^1.0|^2.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyInfo\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "dunglas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Extracts information about PHP class' properties using metadata of popular sources", + "homepage": "https://symfony.com", + "keywords": [ + "doctrine", + "phpdoc", + "property", + "symfony", + "type", + "validator" + ], + "support": { + "source": "https://github.com/symfony/property-info/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/rate-limiter", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/rate-limiter.git", + "reference": "d55de9ec479418f58464e122e68d33886cf6f1fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/rate-limiter/zipball/d55de9ec479418f58464e122e68d33886cf6f1fb", + "reference": "d55de9ec479418f58464e122e68d33886cf6f1fb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/options-resolver": "^7.3|^8.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/lock": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\RateLimiter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Wouter de Jong", + "email": "wouter@wouterj.nl" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a Token Bucket implementation to rate limit input and output in your application", + "homepage": "https://symfony.com", + "keywords": [ + "limiter", + "rate-limiter" + ], + "support": { + "source": "https://github.com/symfony/rate-limiter/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b", + "reference": "9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "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": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.6.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "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.6-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.6.1" + }, + "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-15T11:30:57+00:00" + }, + { + "name": "symfony/string", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "114ac57257d75df748eda23dd003878080b8e688" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/114ac57257d75df748eda23dd003878080b8e688", + "reference": "114ac57257d75df748eda23dd003878080b8e688", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.33", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0|^8.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/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/translation", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "33600f8489485425bfcddd0d983391038d3422e7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/33600f8489485425bfcddd0d983391038d3422e7", + "reference": "33600f8489485425bfcddd0d983391038d3422e7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5.3|^3.3" + }, + "conflict": { + "nikic/php-parser": "<5.0", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "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 to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.6.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "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 translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" + }, + "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-15T13:41:35+00:00" + }, + { + "name": "symfony/type-info", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/type-info.git", + "reference": "6bf34da885ff5143a3dfd8f1b863bb8ab95f50bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/type-info/zipball/6bf34da885ff5143a3dfd8f1b863bb8ab95f50bd", + "reference": "6bf34da885ff5143a3dfd8f1b863bb8ab95f50bd", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "phpstan/phpdoc-parser": "<1.30" + }, + "require-dev": { + "phpstan/phpdoc-parser": "^1.30|^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\TypeInfo\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Arlaud", + "email": "mathias.arlaud@gmail.com" + }, + { + "name": "Baptiste LEDUC", + "email": "baptiste.leduc@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Extracts PHP types information.", + "homepage": "https://symfony.com", + "keywords": [ + "PHPStan", + "phpdoc", + "symfony", + "type" + ], + "support": { + "source": "https://github.com/symfony/type-info/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/uid", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "6883ebdf7bf6a12b37519dbc0df62b0222401b56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/6883ebdf7bf6a12b37519dbc0df62b0222401b56", + "reference": "6883ebdf7bf6a12b37519dbc0df62b0222401b56", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/validator", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/validator.git", + "reference": "8f73cbddae916756f319b3e195088da216f0f12f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/validator/zipball/8f73cbddae916756f319b3e195088da216f0f12f", + "reference": "8f73cbddae916756f319b3e195088da216f0f12f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php83": "^1.27", + "symfony/translation-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/lexer": "<1.1", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<7.0", + "symfony/expression-language": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/intl": "<6.4", + "symfony/property-info": "<6.4", + "symfony/translation": "<6.4.3|>=7.0,<7.0.3", + "symfony/var-exporter": "<6.4.25|>=7.0,<7.3.3", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3|^4", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4.3|^7.0.3|^8.0", + "symfony/type-info": "^7.1.8", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Validator\\": "" + }, + "exclude-from-classmap": [ + "/Tests/", + "/Resources/bin/" + ] + }, + "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 to validate values", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/validator/tree/v7.4.8" + }, + "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-30T12:55:43+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "398907e89a2a56fe426f7955c6fa943ec0c77225" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/398907e89a2a56fe426f7955c6fa943ec0c77225", + "reference": "398907e89a2a56fe426f7955c6fa943ec0c77225", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "require-dev": { + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "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": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "c58fdf7b3d6c2995368264c49e4e8b05bcff2883" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c58fdf7b3d6c2995368264c49e4e8b05bcff2883", + "reference": "c58fdf7b3d6c2995368264c49e4e8b05bcff2883", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "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": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.4.8" + }, + "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-24T13:12:05+00:00" + }, + { + "name": "typo3/class-alias-loader", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/TYPO3/class-alias-loader.git", + "reference": "9e385e64ddf8a1ad354a4d62d4d0f90bce4dcbc2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3/class-alias-loader/zipball/9e385e64ddf8a1ad354a4d62d4d0f90bce4dcbc2", + "reference": "9e385e64ddf8a1ad354a4d62d4d0f90bce4dcbc2", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": ">=7.1" + }, + "replace": { + "helhum/class-alias-loader": "*" + }, + "require-dev": { + "composer/composer": "^2.0@dev", + "mikey179/vfsstream": "~1.4.0@dev", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "composer-plugin", + "extra": { + "class": "TYPO3\\ClassAliasLoader\\Plugin", + "branch-alias": { + "dev-main": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\ClassAliasLoader\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Helmut Hummel", + "email": "info@helhum.io" + } + ], + "description": "Amends the composer class loader to support class aliases to provide backwards compatibility for packages", + "homepage": "http://github.com/TYPO3/class-alias-loader", + "keywords": [ + "alias", + "autoloader", + "classloader", + "composer" + ], + "support": { + "issues": "https://github.com/TYPO3/class-alias-loader/issues", + "source": "https://github.com/TYPO3/class-alias-loader/tree/v1.2.2" + }, + "time": "2026-04-17T09:41:06+00:00" + }, + { + "name": "typo3/cms-cli", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/TYPO3/cms-cli.git", + "reference": "7e26bf51bd8dbd74f86f7c68cb14965c678930f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3/cms-cli/zipball/7e26bf51bd8dbd74f86f7c68cb14965c678930f8", + "reference": "7e26bf51bd8dbd74f86f7c68cb14965c678930f8", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "bin": [ + "typo3" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "TYPO3 command line binary", + "homepage": "https://typo3.org", + "support": { + "issues": "https://github.com/TYPO3/cms-cli/issues", + "source": "https://github.com/TYPO3/cms-cli/tree/v3.1.3" + }, + "time": "2025-12-31T13:41:20+00:00" + }, + { + "name": "typo3/cms-composer-installers", + "version": "v5.0.2", + "source": { + "type": "git", + "url": "https://github.com/TYPO3/CmsComposerInstallers.git", + "reference": "becd622c869eb5cba6b8ea13965a73ae84e37346" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3/CmsComposerInstallers/zipball/becd622c869eb5cba6b8ea13965a73ae84e37346", + "reference": "becd622c869eb5cba6b8ea13965a73ae84e37346", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.1.0", + "php": "^8.1" + }, + "replace": { + "lw/typo3cms-installers": "*", + "netresearch/composer-installers": "*" + }, + "require-dev": { + "composer/composer": "^2.1", + "friendsofphp/php-cs-fixer": "^3.62.0", + "overtrue/phplint": "^9.4.1", + "phpunit/phpunit": "^10.5.30" + }, + "type": "composer-plugin", + "extra": { + "class": "TYPO3\\CMS\\Composer\\Installer\\Plugin", + "branch-alias": { + "dev-main": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Composer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 CMS Core Team", + "homepage": "https://forge.typo3.org/projects/typo3cms-core", + "role": "Developer" + }, + { + "name": "The TYPO3 Community", + "homepage": "https://typo3.org/community/", + "role": "Contributor" + } + ], + "description": "TYPO3 CMS Installers", + "homepage": "https://github.com/TYPO3/CmsComposerInstallers", + "keywords": [ + "cms", + "core", + "extension", + "installer", + "typo3" + ], + "support": { + "general": "https://typo3.org/support/", + "issues": "https://github.com/TYPO3/CmsComposerInstallers/issues", + "source": "https://github.com/TYPO3/CmsComposerInstallers/tree/v5.0.2" + }, + "time": "2025-11-25T18:08:15+00:00" + }, + { + "name": "typo3/cms-core", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/core.git", + "reference": "7d24961879d568da177391e9e33f9cdd79d2ef01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/core/zipball/7d24961879d568da177391e9e33f9cdd79d2ef01", + "reference": "7d24961879d568da177391e9e33f9cdd79d2ef01", + "shasum": "" + }, + "require": { + "bacon/bacon-qr-code": "^3.0.4", + "christian-riesen/base32": "^1.6", + "composer-runtime-api": "^2.1", + "composer/semver": "^3.4", + "doctrine/dbal": "~4.3.3", + "doctrine/event-manager": "^2.0.1", + "doctrine/lexer": "^3.0.1", + "egulias/email-validator": "^4.0", + "enshrined/svg-sanitize": "~0.22", + "ext-dom": "*", + "ext-intl": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-pcre": "*", + "ext-pdo": "*", + "ext-session": "*", + "ext-sodium": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "firebase/php-jwt": "^6.10.2 || ^7.0.2", + "guzzlehttp/guzzle": "^7.9.2", + "guzzlehttp/psr7": "^2.7.0", + "lolli42/finediff": "^1.1.1", + "masterminds/html5": "^2.10.0", + "php": "^8.2", + "psr/container": "^2.0", + "psr/event-dispatcher": "^1.0", + "psr/http-client": "^1.0.3", + "psr/http-factory": "^1.1.0", + "psr/http-message": "^1.1 || ^2.0", + "psr/http-server-handler": "^1.0", + "psr/http-server-middleware": "^1.0", + "psr/log": "^3.0.1", + "symfony/config": "^7.4", + "symfony/console": "^7.4", + "symfony/dependency-injection": "^7.4", + "symfony/doctrine-messenger": "^7.4", + "symfony/event-dispatcher-contracts": "^3.1", + "symfony/expression-language": "^7.4", + "symfony/filesystem": "^7.4", + "symfony/finder": "^7.4", + "symfony/http-foundation": "^7.4", + "symfony/mailer": "^7.4", + "symfony/messenger": "^7.4", + "symfony/mime": "^7.4", + "symfony/options-resolver": "^7.4", + "symfony/polyfill-php83": "^1.31", + "symfony/process": "^7.4", + "symfony/rate-limiter": "^7.4", + "symfony/routing": "^7.4", + "symfony/translation": "^7.4", + "symfony/uid": "^7.4", + "symfony/yaml": "^7.4", + "typo3/class-alias-loader": "^1.2", + "typo3/cms-cli": "^3.1.3", + "typo3/cms-composer-installers": "^5.0.2", + "typo3/html-sanitizer": "^2.2.0", + "typo3fluid/fluid": "^5.2.0" + }, + "conflict": { + "hoa/core": "*", + "typo3/cms": "*" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "replace": { + "typo3/cms-lang": "self.version", + "typo3/cms-saltedpasswords": "self.version", + "typo3/cms-sv": "self.version" + }, + "suggest": { + "ext-apcu": "Needed when non-default APCU based cache backends are used", + "ext-exif": "Used to extract exif metadata 'Orientation' of uploaded images", + "ext-fileinfo": "Used for proper file type detection in the file abstraction layer", + "ext-gd": "GDlib/Freetype is required for building images with text (GIFBUILDER) and can also be used to scale images", + "ext-mysqli": "", + "ext-openssl": "OpenSSL is required for sending SMTP mails over an encrypted channel endpoint", + "ext-zip": "", + "ext-zlib": "TYPO3 uses zlib for resource compression and un/packing t3x extension files" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "protected": true, + "serviceProvider": "TYPO3\\CMS\\Core\\ServiceProvider", + "partOfFactoryDefault": true, + "partOfMinimalUsableSystem": true + }, + "extension-key": "core" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + }, + "typo3/class-alias-loader": { + "class-alias-maps": [ + "Migrations/Code/ClassAliasMap.php" + ] + } + }, + "autoload": { + "files": [ + "Resources/PHP/GlobalDebugFunctions.php" + ], + "psr-4": { + "TYPO3\\CMS\\Core\\": "Classes/" + }, + "classmap": [ + "DeprecatedClasses/ext-install/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Core", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-extbase", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/extbase.git", + "reference": "0331be005e5bec6dafd2287ec45386207f2fd513" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/extbase/zipball/0331be005e5bec6dafd2287ec45386207f2fd513", + "reference": "0331be005e5bec6dafd2287ec45386207f2fd513", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.5 || ^2.0", + "phpdocumentor/reflection-docblock": "^5.6.5 || ^6.0", + "phpdocumentor/type-resolver": "^1.8.2 || ^2.0", + "phpstan/phpdoc-parser": "^2.1", + "symfony/dependency-injection": "^7.4", + "symfony/property-access": "^7.4", + "symfony/property-info": "^7.4", + "symfony/validator": "^7.4", + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "suggest": { + "typo3/cms-scheduler": "Additional scheduler tasks" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "protected": true, + "serviceProvider": "TYPO3\\CMS\\Extbase\\ServiceProvider", + "partOfFactoryDefault": true, + "partOfMinimalUsableSystem": true + }, + "extension-key": "extbase" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + }, + "typo3/class-alias-loader": { + "class-alias-maps": [ + "Migrations/Code/ClassAliasMap.php" + ] + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Extbase\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Extbase - Extension framework to create TYPO3 frontend plugins and TYPO3 backend modules.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-form", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/form.git", + "reference": "34f16800ea0d623cba15bb16f7b48aa2e1631278" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/form/zipball/34f16800ea0d623cba15bb16f7b48aa2e1631278", + "reference": "34f16800ea0d623cba15bb16f7b48aa2e1631278", + "shasum": "" + }, + "require": { + "psr/http-message": "^1.1 || ^2.0", + "symfony/expression-language": "^7.4", + "typo3/cms-core": "14.2.0", + "typo3/cms-frontend": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "suggest": { + "typo3/cms-filelist": "Listing of files in the directory", + "typo3/cms-impexp": "Import and Export of records from TYPO3 in a custom serialized format (.T3D) for data exchange with other TYPO3 systems.", + "typo3/cms-lowlevel": "To display the YAML configuration in the configuration module" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "form" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Form\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Form - Flexible TYPO3 frontend form framework that comes with a backend editor interface.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/c/typo3/cms-form/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-frontend", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/frontend.git", + "reference": "90be219f199a6760a928eba05fd54c6159092c65" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/frontend/zipball/90be219f199a6760a928eba05fd54c6159092c65", + "reference": "90be219f199a6760a928eba05fd54c6159092c65", + "shasum": "" + }, + "require": { + "ext-libxml": "*", + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "suggest": { + "typo3/cms-adminpanel": "Provides additional information and functionality for backend users in the frontend." + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "protected": true, + "partOfFactoryDefault": true, + "partOfMinimalUsableSystem": true + }, + "extension-key": "frontend" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + }, + "typo3/class-alias-loader": { + "class-alias-maps": [ + "Migrations/Code/ClassAliasMap.php" + ] + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Frontend\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Frontend", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/html-sanitizer", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3/html-sanitizer.git", + "reference": "c672a2e02925de8eed0dcaeb3a3c90d3642049a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3/html-sanitizer/zipball/c672a2e02925de8eed0dcaeb3a3c90d3642049a0", + "reference": "c672a2e02925de8eed0dcaeb3a3c90d3642049a0", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "masterminds/html5": "^2.7.6", + "php": "^7.2 || ^8.0", + "psr/log": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\HtmlSanitizer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Hader", + "email": "oliver@typo3.org" + } + ], + "description": "HTML sanitizer aiming to provide XSS-safe markup based on explicitly allowed tags, attributes and values.", + "support": { + "issues": "https://github.com/TYPO3/html-sanitizer/issues", + "source": "https://github.com/TYPO3/html-sanitizer/tree/v2.2.0" + }, + "time": "2024-07-12T15:52:25+00:00" + }, + { + "name": "typo3fluid/fluid", + "version": "5.3.1", + "source": { + "type": "git", + "url": "https://github.com/TYPO3/Fluid.git", + "reference": "28c42c75b171aef196ddbe151b0d1146c290249d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3/Fluid/zipball/28c42c75b171aef196ddbe151b0d1146c290249d", + "reference": "28c42c75b171aef196ddbe151b0d1146c290249d", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2" + }, + "require-dev": { + "ext-json": "*", + "ext-simplexml": "*", + "friendsofphp/php-cs-fixer": "^3.94.2", + "infection/infection": "^0.32.6", + "phpstan/phpstan": "^2.1.40", + "phpstan/phpstan-phpunit": "^2.0.16", + "phpunit/phpunit": "^11.5.55 || ^12.5.14 || ^13.0.5", + "psr/container": "^2.0.2", + "t3docs/fluid-documentation-generator": "^4.4.2" + }, + "suggest": { + "ext-json": "PHP JSON is needed when using JSONVariableProvider: A relatively rare use case", + "ext-simplexml": "SimpleXML is required for the XSD schema generator" + }, + "bin": [ + "bin/fluid" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3Fluid\\Fluid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "description": "The TYPO3 Fluid template rendering engine", + "homepage": "https://github.com/TYPO3/Fluid", + "support": { + "docs": "https://docs.typo3.org/other/typo3fluid/fluid/main/en-us/", + "issues": "https://github.com/TYPO3/Fluid/issues", + "source": "https://github.com/TYPO3/Fluid" + }, + "time": "2026-04-16T17:09:54+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/eb0d790f735ba6cff25c683a85a1da0eadeff9e4", + "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.3.0" + }, + "time": "2026-04-11T10:33:05+00:00" + } + ], + "packages-dev": [ + { + "name": "cuyz/valinor", + "version": "2.4.0", + "source": { + "type": "git", + "url": "https://github.com/CuyZ/Valinor.git", + "reference": "3b0afa3a287ed7f3a69aab223726cf1139454c34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/3b0afa3a287ed7f3a69aab223726cf1139454c34", + "reference": "3b0afa3a287ed7f3a69aab223726cf1139454c34", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "phpstan/phpstan": "<1.0 || >= 3.0", + "vimeo/psalm": "<5.0 || >=7.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.91", + "infection/infection": "^0.32", + "marcocesarato/php-conventional-changelog": "^1.12", + "mikey179/vfsstream": "^1.6.10", + "phpbench/phpbench": "^1.3", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^11.5", + "psr/http-message": "^2.0", + "rector/rector": "^2.0", + "vimeo/psalm": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "CuyZ\\Valinor\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Romain Canon", + "email": "romain.hydrocanon@gmail.com", + "homepage": "https://github.com/romm" + } + ], + "description": "Dependency free PHP library that helps to map any input into a strongly-typed structure.", + "homepage": "https://github.com/CuyZ/Valinor", + "keywords": [ + "array", + "conversion", + "hydrator", + "json", + "mapper", + "mapping", + "object", + "tree", + "yaml" + ], + "support": { + "issues": "https://github.com/CuyZ/Valinor/issues", + "source": "https://github.com/CuyZ/Valinor/tree/2.4.0" + }, + "funding": [ + { + "url": "https://github.com/romm", + "type": "github" + } + ], + "time": "2026-03-23T17:38:05+00:00" + }, + { + "name": "cypresslab/gitelephant", + "version": "v4.6.0", + "source": { + "type": "git", + "url": "https://github.com/matteosister/GitElephant.git", + "reference": "4e546eee4c9ad1e0226054f5756c4ef5217e2929" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matteosister/GitElephant/zipball/4e546eee4c9ad1e0226054f5756c4ef5217e2929", + "reference": "4e546eee4c9ad1e0226054f5756c4ef5217e2929", + "shasum": "" + }, + "require": { + "php": ">=7.2.0", + "phpoption/phpoption": "1.*", + "symfony/filesystem": ">=3.4", + "symfony/finder": ">=3.4", + "symfony/process": ">=3.4" + }, + "require-dev": { + "mockery/mockery": "~1.1", + "php": ">=7.2.0", + "phpstan/phpstan": "*", + "phpstan/phpstan-phpunit": "*", + "phpunit/phpunit": "~8.0|~9.0", + "rector/rector": "*", + "symplify/easy-coding-standard": "*" + }, + "type": "library", + "autoload": { + "psr-0": { + "GitElephant": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0+" + ], + "authors": [ + { + "name": "Matteo Giachino", + "email": "matteog@gmail.com" + } + ], + "description": "An abstraction layer for git written in PHP", + "homepage": "http://gitelephant.cypresslab.net/", + "keywords": [ + "git" + ], + "support": { + "issues": "https://github.com/matteosister/GitElephant/issues", + "source": "https://github.com/matteosister/GitElephant/tree/v4.6.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/cypresslab/gitelephant", + "type": "tidelift" + } + ], + "time": "2024-11-26T16:23:11+00:00" + }, + { + "name": "eliashaeussler/version-bumper", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/eliashaeussler/version-bumper.git", + "reference": "34743202a78ac30dc56d659772121f3785dc2e87" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/eliashaeussler/version-bumper/zipball/34743202a78ac30dc56d659772121f3785dc2e87", + "reference": "34743202a78ac30dc56d659772121f3785dc2e87", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "cuyz/valinor": "^2.0", + "cypresslab/gitelephant": "^4.5", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "symfony/console": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/options-resolver": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/yaml": "^5.4 || ^6.4 || ^7.0 || ^8.0" + }, + "conflict": { + "cuyz/valinor": "2.2.1" + }, + "require-dev": { + "armin/editorconfig-cli": "^2.0", + "composer/composer": "^2.2", + "eliashaeussler/php-cs-fixer-config": "^3.0", + "eliashaeussler/phpstan-config": "^3.0", + "eliashaeussler/rector-config": "^4.0", + "ergebnis/composer-normalize": "^2.47", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-symfony": "^2.0", + "phpunit/phpunit": "^11.0 || ^12.0", + "shipmonk/composer-dependency-analyser": "^1.8" + }, + "type": "composer-plugin", + "extra": { + "class": "EliasHaeussler\\VersionBumper\\VersionBumperPlugin" + }, + "autoload": { + "psr-4": { + "EliasHaeussler\\VersionBumper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-or-later" + ], + "authors": [ + { + "name": "Elias HÀußler", + "email": "elias@haeussler.dev", + "homepage": "https://haeussler.dev", + "role": "Maintainer" + } + ], + "description": "Composer plugin to bump project versions during release preparations", + "support": { + "issues": "https://github.com/eliashaeussler/version-bumper/issues", + "source": "https://github.com/eliashaeussler/version-bumper/tree/3.1.1" + }, + "time": "2025-12-09T08:36:24+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": "phpoption/phpoption", + "version": "1.9.5", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:41:33+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "12.5.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "876099a072646c7745f673d7aeab5382c4439691" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/876099a072646c7745f673d7aeab5382c4439691", + "reference": "876099a072646c7745f673d7aeab5382c4439691", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.7.0", + "php": ">=8.3", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0.3", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^2.0.1" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.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": "12.5.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/12.5.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2026-04-15T08:23:17+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.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": "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/6.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/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-02-02T14:04:18+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "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": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:58+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.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", + "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/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:16+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:38+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "12.5.23", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969", + "reference": "c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969", + "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.3", + "phpunit/php-code-coverage": "^12.5.6", + "phpunit/php-file-iterator": "^6.0.1", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.2.0", + "sebastian/comparator": "^7.1.6", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.1.0", + "sebastian/exporter": "^7.0.2", + "sebastian/global-state": "^8.0.2", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/recursion-context": "^7.0.1", + "sebastian/type": "^6.0.3", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.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/12.5.23" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-04-18T06:12:49+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.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 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/4.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" + } + ], + "time": "2025-09-14T09:36:45+00:00" + }, + { + "name": "sebastian/comparator", + "version": "7.1.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "c769009dee98f494e0edc3fd4f4087501688f11e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c769009dee98f494e0edc3fd4f4087501688f11e", + "reference": "c769009dee98f494e0edc3fd4f4087501688f11e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.2" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.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": "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/7.1.6" + }, + "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-04-14T08:23:15+00:00" + }, + { + "name": "sebastian/complexity", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.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", + "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/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "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/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:46+00:00" + }, + { + "name": "sebastian/environment", + "version": "8.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "b121608b28a13f721e76ffbbd386d08eff58f3f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/b121608b28a13f721e76ffbbd386d08eff58f3f6", + "reference": "b121608b28a13f721e76ffbbd386d08eff58f3f6", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.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/8.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2026-04-15T12:13:01+00:00" + }, + { + "name": "sebastian/exporter", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "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/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:16:11+00:00" + }, + { + "name": "sebastian/global-state", + "version": "8.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.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/8.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" + } + ], + "time": "2025-08-29T11:29:25+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for 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/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:28+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:48+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.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": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:17+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "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/7.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-13T04:44:59+00:00" + }, + { + "name": "sebastian/type", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.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": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:57:12+00:00" + }, + { + "name": "sebastian/version", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^8.1" + }, + "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/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-12-08T11:19:18+00:00" + }, + { + "name": "typo3/cms-backend", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/backend.git", + "reference": "33221addc693666c9d94f0903be0161887932336" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/backend/zipball/33221addc693666c9d94f0903be0161887932336", + "reference": "33221addc693666c9d94f0903be0161887932336", + "shasum": "" + }, + "require": { + "ext-intl": "*", + "ext-libxml": "*", + "psr/event-dispatcher": "^1.0", + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "replace": { + "typo3/cms-about": "self.version", + "typo3/cms-context-help": "self.version", + "typo3/cms-cshmanual": "self.version", + "typo3/cms-func-wizards": "self.version", + "typo3/cms-recordlist": "self.version", + "typo3/cms-t3editor": "self.version", + "typo3/cms-wizard-crpages": "self.version", + "typo3/cms-wizard-sortpages": "self.version" + }, + "suggest": { + "typo3/cms-install": "Displays a link to the Environment module in the System Information toolbar." + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "protected": true, + "serviceProvider": "TYPO3\\CMS\\Backend\\ServiceProvider", + "partOfFactoryDefault": true, + "partOfMinimalUsableSystem": true + }, + "extension-key": "backend" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Backend\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS backend", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-base-distribution", + "version": "v14.0.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3/TYPO3.CMS.BaseDistribution.git", + "reference": "a42b869c4000245d410606decfe653a8b0c5f8b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3/TYPO3.CMS.BaseDistribution/zipball/a42b869c4000245d410606decfe653a8b0c5f8b2", + "reference": "a42b869c4000245d410606decfe653a8b0c5f8b2", + "shasum": "" + }, + "require": { + "typo3/cms-backend": "^14.0", + "typo3/cms-belog": "^14.0", + "typo3/cms-beuser": "^14.0", + "typo3/cms-core": "^14.0", + "typo3/cms-dashboard": "^14.0", + "typo3/cms-extbase": "^14.0", + "typo3/cms-felogin": "^14.0", + "typo3/cms-filelist": "^14.0", + "typo3/cms-fluid": "^14.0", + "typo3/cms-fluid-styled-content": "^14.0", + "typo3/cms-form": "^14.0", + "typo3/cms-frontend": "^14.0", + "typo3/cms-impexp": "^14.0", + "typo3/cms-info": "^14.0", + "typo3/cms-install": "^14.0", + "typo3/cms-reactions": "^14.0", + "typo3/cms-recycler": "^14.0", + "typo3/cms-rte-ckeditor": "^14.0", + "typo3/cms-seo": "^14.0", + "typo3/cms-setup": "^14.0", + "typo3/cms-sys-note": "^14.0", + "typo3/cms-tstemplate": "^14.0", + "typo3/cms-viewpage": "^14.0", + "typo3/cms-webhooks": "^14.0" + }, + "type": "project", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "TYPO3 CMS Base Distribution", + "support": { + "issues": "https://github.com/TYPO3/TYPO3.CMS.BaseDistribution/issues", + "source": "https://github.com/TYPO3/TYPO3.CMS.BaseDistribution/tree/v14.0.0" + }, + "time": "2025-11-24T18:23:50+00:00" + }, + { + "name": "typo3/cms-belog", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/belog.git", + "reference": "ecd9cd786752cccc8a0704f824d983fdaa5bc353" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/belog/zipball/ecd9cd786752cccc8a0704f824d983fdaa5bc353", + "reference": "ecd9cd786752cccc8a0704f824d983fdaa5bc353", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "belog" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Belog\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Log - View logs from the sys_log table in the TYPO3 backend modules System>Log", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-beuser", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/beuser.git", + "reference": "aa5a796ec14619666c3c71fe25c6cffd934f5b4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/beuser/zipball/aa5a796ec14619666c3c71fe25c6cffd934f5b4e", + "reference": "aa5a796ec14619666c3c71fe25c6cffd934f5b4e", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "beuser" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Beuser\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Backend User - TYPO3 backend module Administration > Users for managing backend users and groups.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-dashboard", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/dashboard.git", + "reference": "5428ea03f96143a8f32d592022e530be41833bd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/dashboard/zipball/5428ea03f96143a8f32d592022e530be41833bd5", + "reference": "5428ea03f96143a8f32d592022e530be41833bd5", + "shasum": "" + }, + "require": { + "typo3/cms-backend": "14.2.0", + "typo3/cms-core": "14.2.0", + "typo3/cms-extbase": "14.2.0", + "typo3/cms-fluid": "14.2.0", + "typo3/cms-frontend": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "serviceProvider": "TYPO3\\CMS\\Dashboard\\ServiceProvider", + "partOfFactoryDefault": true + }, + "extension-key": "dashboard" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Dashboard\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Dashboard - TYPO3 backend module used to configure and create backend widgets.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/c/typo3/cms-dashboard/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-felogin", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/felogin.git", + "reference": "9ae732533f2707d9cdfe624d140f1b10cf9e6e04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/felogin/zipball/9ae732533f2707d9cdfe624d140f1b10cf9e6e04", + "reference": "9ae732533f2707d9cdfe624d140f1b10cf9e6e04", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "felogin" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\FrontendLogin\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Frontend Login - A template-based plugin to log in website users in the TYPO3 frontend.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/c/typo3/cms-felogin/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-filelist", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/filelist.git", + "reference": "cf7cd863c66d4c93e2eeaa6c980e72b934de76a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/filelist/zipball/cf7cd863c66d4c93e2eeaa6c980e72b934de76a3", + "reference": "cf7cd863c66d4c93e2eeaa6c980e72b934de76a3", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "protected": true, + "partOfFactoryDefault": true, + "partOfMinimalUsableSystem": true + }, + "extension-key": "filelist" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Filelist\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Filelist - TYPO3 backend module 'Media' used for managing files.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-fluid", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/fluid.git", + "reference": "194d3d197b171ad7f12b2e7836a995f028f18385" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/fluid/zipball/194d3d197b171ad7f12b2e7836a995f028f18385", + "reference": "194d3d197b171ad7f12b2e7836a995f028f18385", + "shasum": "" + }, + "require": { + "symfony/dependency-injection": "^7.4", + "symfony/finder": "^7.4", + "typo3/cms-core": "14.2.0", + "typo3/cms-extbase": "14.2.0", + "typo3fluid/fluid": "^5.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "protected": true, + "serviceProvider": "TYPO3\\CMS\\Fluid\\ServiceProvider", + "partOfFactoryDefault": true, + "partOfMinimalUsableSystem": true + }, + "extension-key": "fluid" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Fluid\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Fluid Integration - Integration of the Fluid templating engine into TYPO3.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/other/typo3/view-helper-reference/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-fluid-styled-content", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/fluid_styled_content.git", + "reference": "2b14cc282b3eb7894ddfcce699568cbd18ae1f71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/fluid_styled_content/zipball/2b14cc282b3eb7894ddfcce699568cbd18ae1f71", + "reference": "2b14cc282b3eb7894ddfcce699568cbd18ae1f71", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0", + "typo3/cms-fluid": "14.2.0", + "typo3/cms-frontend": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "fluid_styled_content" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\FluidStyledContent\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Fluid Styled Content - Fluid templates for TYPO3 content elements.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/c/typo3/cms-fluid-styled-content/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-impexp", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/impexp.git", + "reference": "5ace7e7dd3354cce475a27a04d0665f1f459214e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/impexp/zipball/5ace7e7dd3354cce475a27a04d0665f1f459214e", + "reference": "5ace7e7dd3354cce475a27a04d0665f1f459214e", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "impexp" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Impexp\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Import/Export - Tool for importing and exporting records using XML or the custom T3D format.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/c/typo3/cms-impexp/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-info", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/info.git", + "reference": "153407e4c71e5619435932b6c238add749f72d3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/info/zipball/153407e4c71e5619435932b6c238add749f72d3d", + "reference": "153407e4c71e5619435932b6c238add749f72d3d", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "replace": { + "typo3/cms-info-pagetsconfig": "self.version" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "info" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Info\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Info - TYPO3 backend module for displaying information, such as a pagetree overview and localization information.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-install", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/install.git", + "reference": "fe4191ec389cb86d9bc0c914dfbfd3f3321c55ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/install/zipball/fe4191ec389cb86d9bc0c914dfbfd3f3321c55ec", + "reference": "fe4191ec389cb86d9bc0c914dfbfd3f3321c55ec", + "shasum": "" + }, + "require": { + "doctrine/dbal": "~4.3.3", + "guzzlehttp/promises": "^2.0.3", + "nikic/php-parser": "^5.4.0", + "symfony/finder": "^7.4", + "symfony/http-foundation": "^7.4", + "typo3/cms-core": "14.2.0", + "typo3/cms-extbase": "14.2.0", + "typo3/cms-fluid": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "protected": true, + "serviceProvider": "TYPO3\\CMS\\Install\\ServiceProvider", + "partOfFactoryDefault": true, + "partOfMinimalUsableSystem": true + }, + "extension-key": "install" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Install\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Install Tool - The Install Tool is used for installation, upgrade, system administration and setup tasks.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-reactions", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/reactions.git", + "reference": "860342afb8eddc789fb0752e47b8614e953850cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/reactions/zipball/860342afb8eddc789fb0752e47b8614e953850cf", + "reference": "860342afb8eddc789fb0752e47b8614e953850cf", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "suggest": { + "typo3/cms-lowlevel": "To display registered reactions in the configuration module" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "extension-key": "reactions" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Reactions\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Reactions - Handle incoming Webhooks for TYPO3", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/c/typo3/cms-reactions/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-recycler", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/recycler.git", + "reference": "dc0e04b77f0daed12b2d8c4c514e6527744c6d05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/recycler/zipball/dc0e04b77f0daed12b2d8c4c514e6527744c6d05", + "reference": "dc0e04b77f0daed12b2d8c4c514e6527744c6d05", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "suggest": { + "typo3/cms-scheduler": "Remove deleted records after given time" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "recycler" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Recycler\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Recycler - Restore deleted records or remove them from the database permanently.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/c/typo3/cms-recycler/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-rte-ckeditor", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/rte_ckeditor.git", + "reference": "4920e2c7529ed4444932124fb407eb4cd7a675aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/rte_ckeditor/zipball/4920e2c7529ed4444932124fb407eb4cd7a675aa", + "reference": "4920e2c7529ed4444932124fb407eb4cd7a675aa", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "suggest": { + "typo3/cms-setup": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "rte_ckeditor" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\RteCKEditor\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS RTE CKEditor - Integration of CKEditor as a Rich Text Editor for the TYPO3 backend.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/c/typo3/cms-rte-ckeditor/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-seo", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/seo.git", + "reference": "296bb392e60361fab6003a3ed7f7792db5dc9e05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/seo/zipball/296bb392e60361fab6003a3ed7f7792db5dc9e05", + "reference": "296bb392e60361fab6003a3ed7f7792db5dc9e05", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0", + "typo3/cms-extbase": "14.2.0", + "typo3/cms-frontend": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "suggest": { + "typo3/cms-dashboard": "TYPO3 users can add widgets that can help to optimise their website for search engines" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "seo" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Seo\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS SEO - SEO features including specific fields for SEO purposes, rendering of HTML meta tags and sitemaps.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/c/typo3/cms-seo/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-setup", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/setup.git", + "reference": "e1544864b76983c4bed85e0150dcedb786198d26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/setup/zipball/e1544864b76983c4bed85e0150dcedb786198d26", + "reference": "e1544864b76983c4bed85e0150dcedb786198d26", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "setup" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Setup\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Setup - Allows users to edit a limited set of options for their user profile, including preferred language, their name and email address.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-sys-note", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/sys_note.git", + "reference": "439c02af194bbf169dc9479e1613f055386f1e5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/sys_note/zipball/439c02af194bbf169dc9479e1613f055386f1e5d", + "reference": "439c02af194bbf169dc9479e1613f055386f1e5d", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "suggest": { + "typo3/cms-dashboard": "TYPO3 users can add widgets that can help to quickly see existing sys_note records" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "sys_note" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\SysNote\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS System Notes - Records with messages which can be placed on any page and contain instructions or other information related to a page or section.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-tstemplate", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/tstemplate.git", + "reference": "e8436068ffe36bb92045e58a708e26a9dcef8a79" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/tstemplate/zipball/e8436068ffe36bb92045e58a708e26a9dcef8a79", + "reference": "e8436068ffe36bb92045e58a708e26a9dcef8a79", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "tstemplate" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Tstemplate\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS TypoScript - TYPO3 backend module for the management of TypoScript records for the CMS frontend.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-viewpage", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/viewpage.git", + "reference": "72cb85aea1cf3d9fbdd6a1a5d0f4992c51482b0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/viewpage/zipball/72cb85aea1cf3d9fbdd6a1a5d0f4992c51482b0b", + "reference": "72cb85aea1cf3d9fbdd6a1a5d0f4992c51482b0b", + "shasum": "" + }, + "require": { + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + }, + "extension-key": "viewpage" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Viewpage\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Viewpage - Use the (Web>View) backend module to view a frontend page inside the TYPO3 backend.", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + }, + { + "name": "typo3/cms-webhooks", + "version": "v14.2.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/webhooks.git", + "reference": "f0260969944d1a4adddfaf93b43f0535aee17223" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/webhooks/zipball/f0260969944d1a4adddfaf93b43f0535aee17223", + "reference": "f0260969944d1a4adddfaf93b43f0535aee17223", + "shasum": "" + }, + "require": { + "symfony/uid": "^7.4", + "typo3/cms-core": "14.2.0" + }, + "conflict": { + "typo3/cms": "*" + }, + "suggest": { + "typo3/cms-lowlevel": "To display registered webhooks in the configuration module" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "extension-key": "webhooks" + }, + "branch-alias": { + "dev-main": "14.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Webhooks\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Webhooks - Handle outgoing Webhooks for TYPO3", + "homepage": "https://typo3.community/", + "support": { + "chat": "https://typo3.community/meet/slack/", + "docs": "https://docs.typo3.org/c/typo3/cms-webhooks/main/en-us/", + "forum": "https://talk.typo3.org/", + "issues": "https://forge.typo3.org/issues/", + "rss": "https://news.typo3.com/rss/", + "security": "https://typo3.org/security/", + "source": "https://github.com/TYPO3/typo3/" + }, + "funding": [ + { + "url": "https://typo3.org/membership", + "type": "membership" + } + ], + "time": "2026-03-31T07:23:09+00:00" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +}