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
https://{$version}.{$extensionKey}.ddev.site
https://{$version}.{$extensionKey}.ddev.site/typo3
"; } 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/.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/.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/.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/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..bfed81c --- /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", "8.5"]' + typo3-versions: '["13.4", "14.2"]' + dependencies: '["highest", "lowest"]' diff --git a/.gitignore b/.gitignore index bd5576a..876c4b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,10 @@ +.Build/ +.claude +.idea /public +!Resources/Public /vendor -/composer.lock -.Build/ +/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/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0c11575 --- /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/move-elevator/typo3-repeatable-form-elements.git +cd typo3-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 `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`). + +## 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 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/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/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/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/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/Finisher/SaveToDatabaseFinisher.php b/Classes/Finisher/SaveToDatabaseFinisher.php index efe20fa..5de9610 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,73 +136,96 @@ 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 { - $elementConfig = $elementsConfiguration[$elementIdentifier]; - if (!isset($elementConfig)) { + if (!isset($elementsConfiguration[$elementIdentifier])) { return false; } + $elementConfig = $elementsConfiguration[$elementIdentifier]; 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/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/Classes/FormElements/RepeatableContainerInterface.php b/Classes/FormElements/RepeatableContainerInterface.php index 4707240..87bd875 100644 --- a/Classes/FormElements/RepeatableContainerInterface.php +++ b/Classes/FormElements/RepeatableContainerInterface.php @@ -2,14 +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\FormElements; +use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; +use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface; + /** - * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. + * RepeatableContainerInterface. * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. + * @author Konrad Michalik */ -use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface; +interface RepeatableContainerInterface extends CompositeRenderableInterface +{ + /** + * @return array + */ + public function getElements(): array; -interface RepeatableContainerInterface extends CompositeRenderableInterface {} + /** + * @return array + */ + public function getProperties(): array; +} diff --git a/Classes/Hooks/FormHooks.php b/Classes/Hooks/FormHooks.php index a74a8fb..43991ed 100644 --- a/Classes/Hooks/FormHooks.php +++ b/Classes/Hooks/FormHooks.php @@ -2,37 +2,36 @@ declare(strict_types=1); -namespace TRITUM\RepeatableFormElements\Hooks; - -/** - * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. + * (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 Psr\EventDispatcher\EventDispatcherInterface; -use TRITUM\RepeatableFormElements\Event\AfterBuildingFinishedEvent; + +namespace TRITUM\RepeatableFormElements\Hooks; + use TRITUM\RepeatableFormElements\FormElements\RepeatableContainerInterface; use TRITUM\RepeatableFormElements\Service\CopyService; 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( @@ -41,17 +40,18 @@ 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 - if (!$lastPage) { + if (null === $lastPage) { return $currentPage; } - $copyService = GeneralUtility::makeInstance(CopyService::class, $formRuntime); - if ($this->userWentBackToPreviousStep($formRuntime, $currentPage, $lastPage)) { + if ($this->userWentBackToPreviousStep($currentPage, $lastPage)) { $copyService->createCopiesFromFormState(); } else { $copyService->createCopiesFromCurrentRequest(); @@ -60,10 +60,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,9 +67,9 @@ 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']; - $fluidAdditionalAttributes['data-element-datepicker-date-format'] = $renderable->getProperties()['dateFormat']; + if ('DatePicker' === $renderable->getType()) { + $fluidAdditionalAttributes['data-element-datepicker-enabled'] = (int) $properties['enableDatePicker']; + $fluidAdditionalAttributes['data-element-datepicker-date-format'] = $properties['dateFormat']; } $renderable->setProperty('fluidAdditionalAttributes', $fluidAdditionalAttributes); @@ -81,15 +77,14 @@ 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, + CopyService $copyService, array $repeatableContainerIdentifiers = [], ): void { $isRepeatableContainer = $renderable instanceof RepeatableContainerInterface; @@ -98,71 +93,74 @@ 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, $copyService, $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, $copyService, $repeatableContainerIdentifiers); } + } + } + + /** + * @param array $repeatableContainerIdentifiers + * + * @throws DuplicateFormElementException + */ + private function rewriteRenderableIdentifier( + RenderableInterface $renderable, + FormRuntime $formRuntime, + CopyService $copyService, + array $repeatableContainerIdentifiers, + bool $isRepeatableContainer, + ): void { + if (!$renderable instanceof AbstractRenderable) { + return; + } - $formRuntime->getFormDefinition()->unregisterRenderable($renderable); - $renderable->setIdentifier($newIdentifier); - $formRuntime->getFormDefinition()->registerRenderable($renderable); + $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); + [$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); - } - } + $copyService->dispatchAfterBuildingFinished($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( - FormRuntime $formRuntime, + private function userWentBackToPreviousStep( ?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..46c8c04 100644 --- a/Classes/Service/CopyService.php +++ b/Classes/Service/CopyService.php @@ -2,52 +2,63 @@ declare(strict_types=1); -namespace TRITUM\RepeatableFormElements\Service; - -/** - * This file is part of the "repeatable_form_elements" Extension for TYPO3 CMS. +/* + * This file is part of the "repeatable_form_elements" TYPO3 CMS extension. * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. + * (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; + 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 +66,9 @@ public function __construct(FormRuntime $formRuntime) } /** - * @return CopyService * @api */ - public function createCopiesFromCurrentRequest(): CopyService + public function createCopiesFromCurrentRequest(): void { $requestArguments = $this->formRuntime->getRequest()->getArguments(); $this->removeDeletedRepeatableContainersFromFormValuesByRequest($requestArguments); @@ -68,23 +78,19 @@ public function createCopiesFromCurrentRequest(): CopyService ); $this->copyRepeatableContainersFromArguments($requestArguments); - return $this; } /** - * @return CopyService * @api */ - public function createCopiesFromFormState(): CopyService + public function createCopiesFromFormState(): void { $this->copyRepeatableContainersFromArguments($this->formState->getFormValues()); - return $this; } /** - * @param string $originalFormElement - * @param string $newElementCopy * @return ProcessingRule[] + * * @internal */ public function copyProcessingRule( @@ -98,82 +104,58 @@ public function copyProcessingRule( try { $newProcessingRule->setDataType($originalProcessingRule->getDataType()); - } catch (\TypeError $error) { + } catch (TypeError) { } return [$originalProcessingRule, $newProcessingRule]; } /** - * @param array $requestArguments - * @param array $argumentPath + * 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 */ 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 +163,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 +182,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 +210,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 +217,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 +228,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 +281,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; + } - $this->removeDeletedRepeatableContainersFromFormValuesByRequest($argumentValue, $argumentPath); - array_pop($argumentPath); + $originalContainer = $this->getRepeatableContainerByOriginalIdentifier($argumentKey); + $argumentPath[] = $argumentKey; + $copyIndexes = array_keys($argumentValue); + + 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 +322,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,25 +348,88 @@ protected function copyVariants( } $options = $event->getOptions(); - $newFormElement->createVariant($options); + $newFormElement->createVariant($options); // @phpstan-ignore method.notFound } } } /** - * 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. + * @param array $copyIndexes */ - protected function dispatchAfterBuildingFinished(RenderableInterface $renderable): void + private function pruneDeletedContainerValues(string $formValuePath, array $copyIndexes): void { - foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterBuildingFinished'] ?? [] as $className) { - $hookObj = GeneralUtility::makeInstance($className); - if (method_exists($hookObj, 'afterBuildingFinished')) { - $hookObj->afterBuildingFinished($renderable); + $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; } - $this->eventDispatcher->dispatch(new AfterBuildingFinishedEvent($renderable)); + 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/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/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/README.md b/README.md index c4f3b78..d490c12 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,85 @@ - -![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 + +# 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. + +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 -# Custom form element "Repeatable container" +| Requirement | Version | +|-------------|---------| +| PHP | 8.2 – 8.5 | +| TYPO3 | 13.4 LTS, 14.x | -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). +## 🚀 Installation -## Preferred installation +```bash +composer require move-elevator/typo3-repeatable-form-elements +``` -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). +Add the site set `tritum/repeatable-form-elements` to the dependencies of your site package's site set: -## Usage +```yaml +# Configuration/Sets/YourSitePackage/config.yaml +dependencies: + - tritum/repeatable-form-elements +``` -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". +## 💡 Usage -Add the desired fields with the favored validators to the "Repeatable container". +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. -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. +### Extended SaveToDatabaseFinisher -The newly implemented extended version of SaveToDatabaseFinisher can be used as seen [here](Resources/Private/ExampleFormDefinitions/extended-save-to-database-finisher.form.yaml). +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). -## Configuration +## ⚙️ Configuration -To deactivate the copying of variants, the feature `repeatableFormElements.copyVariants` can be used +To deactivate the copying of variants, disable the feature flag: -## Extendability +```php +$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['repeatableFormElements.copyVariants'] = false; +``` -The following options can be used to extend the behavior when copying. +## 🔌 Extendability -| Name | Description | -|------------------|------------------------------------------------------------------| -| CopyVariantEvent | Extend manipulation of copied variants or disable specific ones. | +| 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. | -## Credits +## 🤝 Contributing -This TYPO3 extension was created by Ralf Zimmermann (https://dreistrom.land). +Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, linting, testing and the PR workflow. -## Thank you +## 📝 Changelog -Nora Winter - "Faktenkopf" at www.faktenhaus.de - sponsored this great extension. -The fine people at www.b13.de connected all the people involved. +See [CHANGELOG.md](CHANGELOG.md) for a list of changes. -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! +## 🏆 Credits -Uwe - Hawkeye1909 - for removing jQuery as dependency. +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. -Alexander Opitz @ extrameile-gehen.de - for his work on saving repeatable elements to database. +This fork is maintained by [move:elevator](https://move-elevator.de). +## 📄 License -especially to all others who have contributed to the improvement of the extension. +GPL-2.0-or-later — see [LICENSE](LICENSE) for details. 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/.php-cs-fixer.php b/Tests/CGL/.php-cs-fixer.php new file mode 100644 index 0000000..c5ffdf8 --- /dev/null +++ b/Tests/CGL/.php-cs-fixer.php @@ -0,0 +1,46 @@ + + * + * 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; +use KonradMichalik\PhpCsFixerPreset\Rules\Set\Set; +use KonradMichalik\PhpDocBlockHeaderFixer\Generators\DocBlockHeader; +use KonradMichalik\PhpDocBlockHeaderFixer\Rules\DocBlockHeaderFixer; +use Symfony\Component\Finder\Finder; + +$rootPath = dirname(__DIR__, 2); + +return Config::create() + ->registerCustomFixers([ + new DocBlockHeaderFixer(), + ]) + ->withRule( + Header::create( + 'repeatable_form_elements', + Type::TYPO3Extension, + Author::create('Konrad Michalik', 'km@move-elevator.de'), + 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-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/Tests/CGL/composer.json b/Tests/CGL/composer.json new file mode 100644 index 0000000..9c79ee0 --- /dev/null +++ b/Tests/CGL/composer.json @@ -0,0 +1,73 @@ +{ + "authors": [ + { + "name": "Konrad Michalik", + "email": "km@move-elevator.de", + "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..f372af1 --- /dev/null +++ b/Tests/CGL/rector.php @@ -0,0 +1,64 @@ + + * + * 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; +use Rector\Set\ValueObject\LevelSetList; +use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector; +use Rector\ValueObject\PhpVersion; +use Ssch\TYPO3Rector\CodeQuality\General\ExtEmConfRector; +use Ssch\TYPO3Rector\Configuration\Typo3Option; +use Ssch\TYPO3Rector\Set\{Typo3LevelSetList, Typo3SetList}; + +$rootPath = dirname(__DIR__, 2); + +return RectorConfig::configure() + ->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) +; 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 2fbd6d2..dd51a0b 100644 --- a/composer.json +++ b/composer.json @@ -1,54 +1,61 @@ { - "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": { + "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" }, + "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/" } }, + "autoload-dev": { + "psr-4": { + "TRITUM\\RepeatableFormElements\\Tests\\": "Tests/" + } + }, "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" } } 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" +} 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', 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(); diff --git a/packaging_exclude.php b/packaging_exclude.php new file mode 100644 index 0000000..011eb94 --- /dev/null +++ b/packaging_exclude.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'directories' => [ + '.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/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..82776b6 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,33 @@ + + + + + + + + Tests/Unit + + + + + + + + + + + + + + + Classes + + + 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" + ] +} 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%}'