From 025b5a9d9e8218472731a3d68df80291c3f801c2 Mon Sep 17 00:00:00 2001 From: "c.boelter" Date: Fri, 27 Mar 2026 09:07:29 +0100 Subject: [PATCH 01/15] add optional migration tool for nested fragments, update documentation --- CHANGELOG.md | 4 + README.md | 19 ++-- src/Migration/GridWrapperMigration.php | 128 +++++++++++++++++++++++++ src/Resources/config/config.yaml | 2 + src/Resources/config/services.yaml | 7 ++ 5 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 src/Migration/GridWrapperMigration.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b4c27..feb553f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ Changelog [Unreleased] ------------ +### Added + +- Add support for nested fragments + 3.0.5 (2025-03-02) ------------------ diff --git a/README.md b/README.md index a185717..f5db945 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,11 @@ This extension provides Bootstrap 5 grid tools for Contao CMS. Features -------- - - Manage grid definition in your theme settings - - Content elements - - Form elements - - Grid module - - Import/Export with your theme settings - +- Manage grid definition in your theme settings +- Content elements +- Form elements +- Grid module +- Import/Export with your theme settings Changelog --------- @@ -32,7 +31,6 @@ Requirements - PHP ^8.1 - Contao ^4.13 || ^5.3 - Install ------- @@ -73,3 +71,10 @@ class AppKernel } ``` + +Migration +------- + +To automatically migrate your grid from Start- and Stop-Wrappers to nested fragments. You have to enbale the Migration +by adding `BS_GRID_WRAPPER_MIGRATION=true` parameter to your .env.local. Afterwards you can run the migration in the +contao manager over by cli. diff --git a/src/Migration/GridWrapperMigration.php b/src/Migration/GridWrapperMigration.php new file mode 100644 index 0000000..ffd8472 --- /dev/null +++ b/src/Migration/GridWrapperMigration.php @@ -0,0 +1,128 @@ +enableMigration === false) { + return false; + } + + $schemaManager = $this->connection->createSchemaManager(); + if (! $schemaManager->tablesExist(['tl_bs_grid', 'tl_content'])) { + return false; + } + + $queryBuilder = $this->connection->createQueryBuilder(); + + return $queryBuilder + ->select('COUNT(tc.id) as count') + ->from('tl_content', 'tc') + ->where($queryBuilder->expr()->eq('tc.type', ':type')) + ->setParameter('type', 'bs_gridStart') + ->executeQuery() + ->fetchOne() > 0; + } + + #[Override] + public function run(): MigrationResult + { + $sql = << grid_start.sorting + ORDER BY grid_stop.sorting ASC + LIMIT 1 + ) AS grid_stop_id + FROM tl_content grid_start + LEFT JOIN tl_content el + ON el.pid = grid_start.pid + AND el.ptable = grid_start.ptable + AND el.sorting > grid_start.sorting + AND el.sorting < ( + SELECT MIN(grid_stop.sorting) + FROM tl_content grid_stop + WHERE grid_stop.pid = grid_start.pid + AND grid_stop.ptable = grid_start.ptable + AND grid_stop.type = 'bs_gridStop' + AND grid_stop.sorting > grid_start.sorting + ) + WHERE grid_start.type = 'bs_gridStart' + ORDER BY grid_start.pid, grid_start.ptable, grid_start.sorting, el.sorting +SQL; + + $contentElements = $this->connection->executeQuery($sql)->fetchAllAssociative(); + + $gridContainers = array_reduce($contentElements, function (array $carry, array $row) { + $startId = $row['grid_start_id']; + + $carry[$startId] ??= [ + 'start_id' => $startId, + 'stop_id' => $row['grid_stop_id'], + 'elements' => [], + ]; + + if ($row['element_id'] !== null) { + $carry[$startId]['elements'][] = $row; + } + + return $carry; + }, []); + + $elementCount = array_sum( + array_map( + static fn (array $gridContainer) => count($gridContainer['elements']), + $gridContainers + ) + ); + + foreach ($gridContainers as $gridContainer) { + $this->connection->update( + 'tl_content', + ['type' => 'bs_grid_wrapper'], + ['id' => $gridContainer['start_id']] + ); + + foreach ($gridContainer['elements'] as $element) { + $this->connection->update( + 'tl_content', + ['pid' => $gridContainer['start_id'], 'ptable' => 'tl_content'], + ['id' => $element['element_id']] + ); + } + + $this->connection->delete('tl_content', ['id' => $gridContainer['stop_id']]); + } + + return $this->createResult( + true, + 'Migrated ' . count($gridContainers) . ' grid containers and ' . $elementCount . ' elements.' + ); + } +} diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index a72bdf7..bf6b0ef 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -4,3 +4,5 @@ parameters: - 'Multilingual' - 'Contao\DC_Table' - 'Terminal42\DcMultilingualBundle\Driver' + + contao_bootstrap.grid.enable_wrapper_migration: '%env(bool:default::BS_GRID_WRAPPER_MIGRATION)%' diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index b49788c..cb28f80 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -104,3 +104,10 @@ services: - '@database_connection' tags: - { name: 'contao.migration' } + + ContaoBootstrap\Grid\Migration\GridWrapperMigration: + arguments: + - '@database_connection' + - '%contao_bootstrap.grid.enable_wrapper_migration%' + tags: + - { name: 'contao.migration' } From 7a46fb0455099ec398c52d2567ade2f08b7cf2e4 Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 15 Apr 2026 09:26:24 +0200 Subject: [PATCH 02/15] rework backend view to wildcard template, add listener to show parent grid field only on old start / stop elements, change original nested element to get working with separator elements # Conflicts: # .phpcq.lock --- .phpcq.lock | 2 +- .../GridSeparatorElementController.php | 8 +++-- .../GridWrapperElementController.php | 16 +++++++--- src/Listener/Dca/ContentListener.php | 31 ++++++++++++++---- src/Resources/contao/dca/tl_content.php | 5 +++ .../twig/backend/grid_wildcard.html.twig | 7 ++++ .../content_element/bs_grid_wrapper.html.twig | 32 +++++-------------- 7 files changed, 62 insertions(+), 39 deletions(-) create mode 100644 src/Resources/contao/templates/twig/backend/grid_wildcard.html.twig diff --git a/.phpcq.lock b/.phpcq.lock index 2d695cc..3fd452c 100644 --- a/.phpcq.lock +++ b/.phpcq.lock @@ -1 +1 @@ -{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"40b84043be9c25e6feb38a9e014f1a9f\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-11T04:32:07+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"14.0.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/897a7dc209e49ee6cf04e689c41112df17967130\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.23\",\n \"squizlabs/php_codesniffer\": \"^4\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/14.0.0\"\n },\n \"time\": \"2025-09-21T18:21:47+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.28.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"66151cfbd25b50e8becd9f809fb704f01fd4d6f2\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/66151cfbd25b50e8becd9f809fb704f01fd4d6f2\",\n \"reference\": \"66151cfbd25b50e8becd9f809fb704f01fd4d6f2\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.7 || ^1.2.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.2\",\n \"squizlabs/php_codesniffer\": \"^4.0.1\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.2\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.42\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.4\",\n \"phpstan/phpstan-phpunit\": \"2.0.16\",\n \"phpstan/phpstan-strict-rules\": \"2.0.10\",\n \"phpunit/phpunit\": \"9.6.34|10.5.63|11.4.4|11.5.50|12.5.14\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.28.1\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2026-03-22T17:22:38+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"4.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=7.2.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-10T16:43:36+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.50.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.24.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.16.1","url":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar","requirements":{"php":{"php":"~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file +{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"e6054cafc39eba4b0e4ca4ca45683799\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-11T04:32:07+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"13.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"0affd62169186f32de725ca612e6129e81186a21\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/0affd62169186f32de725ca612e6129e81186a21\",\n \"reference\": \"0affd62169186f32de725ca612e6129e81186a21\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.16\",\n \"squizlabs/php_codesniffer\": \"^3.7\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/13.0.1\"\n },\n \"time\": \"2025-05-14T10:54:19+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.22.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"1dd80bf3b93692bedb21a6623c496887fad05fec\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/1dd80bf3b93692bedb21a6623c496887fad05fec\",\n \"reference\": \"1dd80bf3b93692bedb21a6623c496887fad05fec\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.1.2\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.0\",\n \"squizlabs/php_codesniffer\": \"^3.13.4\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.0\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.24\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.3\",\n \"phpstan/phpstan-phpunit\": \"2.0.7\",\n \"phpstan/phpstan-strict-rules\": \"2.0.6\",\n \"phpunit/phpunit\": \"9.6.8|10.5.48|11.4.4|11.5.36|12.3.10\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.22.1\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2025-09-13T08:53:30+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"3.13.5\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0ca86845ce43291e8f5692c7356fccf3bcf02bf4\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4\",\n \"reference\": \"0ca86845ce43291e8f5692c7356fccf3bcf02bf4\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=5.4.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-04T16:30:35+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.50.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.18.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.18.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.18.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.5.0","url":"https://github.com/vimeo/psalm/releases/download/6.5.0/psalm.phar","requirements":{"php":{"php":"~8.1.17 || ~8.2.4 || ~8.3.0 || ~8.4.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.5.0/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} diff --git a/src/Component/ContentElement/GridSeparatorElementController.php b/src/Component/ContentElement/GridSeparatorElementController.php index b0bcc83..3356525 100644 --- a/src/Component/ContentElement/GridSeparatorElementController.php +++ b/src/Component/ContentElement/GridSeparatorElementController.php @@ -89,7 +89,7 @@ protected function getIterator(ContentModel $model): GridIterator|null if ($parent) { try { - $iterator = $provider->getIterator('ce:' . $parent->id, (int) $parent->bs_grid); + $iterator = $provider->getIterator('ce:' . $parent->id, (int)$parent->bs_grid); $this->tagResponse('contao.db.tl_bs_grid.' . $parent->bs_grid); return $iterator; @@ -110,6 +110,10 @@ protected function getIterator(ContentModel $model): GridIterator|null */ protected function getParent(ContentModel $model): ContentModel|null { - return $this->repositories->getRepository(ContentModel::class)->find((int) $model->bs_grid_parent); + if ($model->ptable === 'tl_content') { + return $this->repositories->getRepository(ContentModel::class)->find($model->pid); + } + + return $this->repositories->getRepository(ContentModel::class)->find((int)$model->bs_grid_parent); } } diff --git a/src/Component/ContentElement/GridWrapperElementController.php b/src/Component/ContentElement/GridWrapperElementController.php index 75450e6..2bb9889 100644 --- a/src/Component/ContentElement/GridWrapperElementController.php +++ b/src/Component/ContentElement/GridWrapperElementController.php @@ -29,10 +29,16 @@ public function __construct( #[Override] protected function getResponse(FragmentTemplate $template, ContentModel $model, Request $request): Response { - $template->iterator = $this->getIterator($model); - $template->name = $model->bs_grid_name; - $template->color = $this->colorRotate->getColor('ce:' . $model->id); - $template->isBackend = $this->isBackendScope($request); + if ($this->isBackendScope($request)) { + $template->setName('backend/grid_wildcard'); + + $template->set('title', $model->bs_grid_name); + $template->set('color', $this->colorRotate->getColor('ce:' . $model->id)); + + return $template->getResponse(); + } + + $template->set('iterator', $this->getIterator($model)); return $template->getResponse(); } @@ -40,7 +46,7 @@ protected function getResponse(FragmentTemplate $template, ContentModel $model, protected function getIterator(ContentModel $model): GridIterator|null { try { - $iterator = $this->provider->getIterator('ce:' . $model->id, (int) $model->bs_grid); + $iterator = $this->provider->getIterator('ce:' . $model->id, (int)$model->bs_grid); $this->tagResponse('contao.db.tl_bs_grid.' . $model->bs_grid); return $iterator; diff --git a/src/Listener/Dca/ContentListener.php b/src/Listener/Dca/ContentListener.php index 66498f1..4bf9212 100644 --- a/src/Listener/Dca/ContentListener.php +++ b/src/Listener/Dca/ContentListener.php @@ -8,6 +8,7 @@ use Contao\Config; use Contao\ContentModel; use Contao\Controller; +use Contao\CoreBundle\DataContainer\PaletteManipulator; use Contao\CoreBundle\Framework\ContaoFramework; use Contao\CoreBundle\Image\ImageSizes; use Contao\Database\Result; @@ -18,9 +19,9 @@ use ContaoBootstrap\Core\Environment; use Netzmacht\Contao\Toolkit\Data\Model\RepositoryManager; use Override; -use stdClass; - use function sprintf; + +use stdClass; use function time; /** @@ -31,10 +32,10 @@ final class ContentListener extends AbstractWrapperDcaListener { /** - * @param Environment $environment Bootstrap environment. - * @param ContaoFramework $framework Contao framework. - * @param ImageSizes $imageSizes Image sizes. - * @param BackendUser $user Contao backend user. + * @param Environment $environment Bootstrap environment. + * @param ContaoFramework $framework Contao framework. + * @param ImageSizes $imageSizes Image sizes. + * @param BackendUser $user Contao backend user. */ public function __construct( Environment $environment, @@ -70,6 +71,22 @@ public function initializeDca(): void ]; } + public function updatePaletteOnNestedParent(DataContainer $dataContainer): void + { + $input = $this->framework->getAdapter(Input::class); + $currentRecord = $dataContainer->getCurrentRecord(); + + if ($input->get('act') !== 'edit' || $currentRecord === null) { + return; + } + + if ($currentRecord['type'] !== 'bs_gridSeparator' || $currentRecord['ptable'] !== 'tl_content') { + return; + } + + PaletteManipulator::create()->removeField('bs_grid_parent')->applyToPalette('bs_gridSeparator', 'tl_content'); + } + /** * Get all grid parent options. * @@ -123,7 +140,7 @@ public function getGalleryTemplates(): array /** * Dynamically add flags to the "multiSRC" field. * - * @param mixed $value Given value. + * @param mixed $value Given value. * @param DataContainer $dataContainer Data Container driver. * * @SuppressWarnings(PHPMD.Superglobals) diff --git a/src/Resources/contao/dca/tl_content.php b/src/Resources/contao/dca/tl_content.php index c9193db..80cffcd 100644 --- a/src/Resources/contao/dca/tl_content.php +++ b/src/Resources/contao/dca/tl_content.php @@ -13,6 +13,11 @@ 'initializeDca', ]; +$GLOBALS['TL_DCA']['tl_content']['config']['onload_callback'][] = [ + 'contao_bootstrap.grid.listeners.dca.content', + 'updatePaletteOnNestedParent', +]; + $GLOBALS['TL_DCA']['tl_content']['config']['oncopy_callback'][] = [ ContentFixParentRelationListener::class, 'onCopy', diff --git a/src/Resources/contao/templates/twig/backend/grid_wildcard.html.twig b/src/Resources/contao/templates/twig/backend/grid_wildcard.html.twig new file mode 100644 index 0000000..5c4bae3 --- /dev/null +++ b/src/Resources/contao/templates/twig/backend/grid_wildcard.html.twig @@ -0,0 +1,7 @@ +{% trans_default_domain 'contao_modules' %} + +
+ ### {{ ('CTE.bootstrap')|trans }}: {{ ('CTE.' ~ type ~ '.0')|trans }} ### +
+ {{ title }} +
diff --git a/src/Resources/contao/templates/twig/content_element/bs_grid_wrapper.html.twig b/src/Resources/contao/templates/twig/content_element/bs_grid_wrapper.html.twig index 199f371..51b135b 100644 --- a/src/Resources/contao/templates/twig/content_element/bs_grid_wrapper.html.twig +++ b/src/Resources/contao/templates/twig/content_element/bs_grid_wrapper.html.twig @@ -1,34 +1,18 @@ {% extends "@Contao/content_element/_base.html.twig" %} {% block content %} - {% if isBackend %} {% if iterator is not null %} - {% for fragment in nested_fragments %} -
{{ name }} [{{ iterator.current }}]
- {{ content_element(fragment) }} - {% endfor %} +
+
+ {% for fragment in nested_fragments %} + {{ content_element(fragment) }} + {% endfor %} +
+
{% else %} + {{ 'ERR.bsGridParentMissing'|trans({}, 'contao_default') }} {% for fragment in nested_fragments %} {{ content_element(fragment) }} {% endfor %} {% endif %} - {% else %} - {% if iterator is not null %} -
- {% for fragment in nested_fragments %} - {% for reset in iterator.resets %} -
- {% endfor %} -
- {{ content_element(fragment) }} -
- {% endfor %} -
- {% else %} - {{ 'ERR.bsGridParentMissing'|trans({}, 'contao_default') }} - {% for fragment in nested_fragments %} - {{ content_element(fragment) }} - {% endfor %} - {% endif %} - {% endif %} {% endblock %} From ebc14614e919098d458eeb35b17cc64df02b0acb Mon Sep 17 00:00:00 2001 From: David Molineus Date: Mon, 13 Apr 2026 14:08:02 +0200 Subject: [PATCH 03/15] Update dependencies # Conflicts: # .phpcq.lock # .phpcq.yaml.dist # composer.json --- .phpcq.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.phpcq.lock b/.phpcq.lock index 3fd452c..12296a7 100644 --- a/.phpcq.lock +++ b/.phpcq.lock @@ -1 +1 @@ -{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"e6054cafc39eba4b0e4ca4ca45683799\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-11T04:32:07+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"13.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"0affd62169186f32de725ca612e6129e81186a21\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/0affd62169186f32de725ca612e6129e81186a21\",\n \"reference\": \"0affd62169186f32de725ca612e6129e81186a21\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.16\",\n \"squizlabs/php_codesniffer\": \"^3.7\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/13.0.1\"\n },\n \"time\": \"2025-05-14T10:54:19+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.22.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"1dd80bf3b93692bedb21a6623c496887fad05fec\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/1dd80bf3b93692bedb21a6623c496887fad05fec\",\n \"reference\": \"1dd80bf3b93692bedb21a6623c496887fad05fec\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.1.2\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.0\",\n \"squizlabs/php_codesniffer\": \"^3.13.4\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.0\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.24\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.3\",\n \"phpstan/phpstan-phpunit\": \"2.0.7\",\n \"phpstan/phpstan-strict-rules\": \"2.0.6\",\n \"phpunit/phpunit\": \"9.6.8|10.5.48|11.4.4|11.5.36|12.3.10\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.22.1\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2025-09-13T08:53:30+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"3.13.5\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0ca86845ce43291e8f5692c7356fccf3bcf02bf4\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4\",\n \"reference\": \"0ca86845ce43291e8f5692c7356fccf3bcf02bf4\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=5.4.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-04T16:30:35+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.50.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.18.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.18.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.18.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.5.0","url":"https://github.com/vimeo/psalm/releases/download/6.5.0/psalm.phar","requirements":{"php":{"php":"~8.1.17 || ~8.2.4 || ~8.3.0 || ~8.4.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.5.0/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} +{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"e6054cafc39eba4b0e4ca4ca45683799\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-11T04:32:07+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"13.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"0affd62169186f32de725ca612e6129e81186a21\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/0affd62169186f32de725ca612e6129e81186a21\",\n \"reference\": \"0affd62169186f32de725ca612e6129e81186a21\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.16\",\n \"squizlabs/php_codesniffer\": \"^3.7\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/13.0.1\"\n },\n \"time\": \"2025-05-14T10:54:19+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.22.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"1dd80bf3b93692bedb21a6623c496887fad05fec\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/1dd80bf3b93692bedb21a6623c496887fad05fec\",\n \"reference\": \"1dd80bf3b93692bedb21a6623c496887fad05fec\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.1.2\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.0\",\n \"squizlabs/php_codesniffer\": \"^3.13.4\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.0\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.24\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.3\",\n \"phpstan/phpstan-phpunit\": \"2.0.7\",\n \"phpstan/phpstan-strict-rules\": \"2.0.6\",\n \"phpunit/phpunit\": \"9.6.8|10.5.48|11.4.4|11.5.36|12.3.10\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.22.1\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2025-09-13T08:53:30+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"3.13.5\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0ca86845ce43291e8f5692c7356fccf3bcf02bf4\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4\",\n \"reference\": \"0ca86845ce43291e8f5692c7356fccf3bcf02bf4\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=5.4.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-04T16:30:35+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.50.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.24.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.16.1","url":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar","requirements":{"php":{"php":"~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file From 22f7a521d86582064feaa3b442d87a75ee3db054 Mon Sep 17 00:00:00 2001 From: David Molineus Date: Mon, 13 Apr 2026 14:27:36 +0200 Subject: [PATCH 04/15] Get phpcs working and fix detected issues --- .phpcq.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.phpcq.lock b/.phpcq.lock index 12296a7..2d695cc 100644 --- a/.phpcq.lock +++ b/.phpcq.lock @@ -1 +1 @@ -{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"e6054cafc39eba4b0e4ca4ca45683799\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-11T04:32:07+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"13.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"0affd62169186f32de725ca612e6129e81186a21\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/0affd62169186f32de725ca612e6129e81186a21\",\n \"reference\": \"0affd62169186f32de725ca612e6129e81186a21\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.16\",\n \"squizlabs/php_codesniffer\": \"^3.7\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/13.0.1\"\n },\n \"time\": \"2025-05-14T10:54:19+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.22.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"1dd80bf3b93692bedb21a6623c496887fad05fec\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/1dd80bf3b93692bedb21a6623c496887fad05fec\",\n \"reference\": \"1dd80bf3b93692bedb21a6623c496887fad05fec\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.1.2\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.0\",\n \"squizlabs/php_codesniffer\": \"^3.13.4\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.0\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.24\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.3\",\n \"phpstan/phpstan-phpunit\": \"2.0.7\",\n \"phpstan/phpstan-strict-rules\": \"2.0.6\",\n \"phpunit/phpunit\": \"9.6.8|10.5.48|11.4.4|11.5.36|12.3.10\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.22.1\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2025-09-13T08:53:30+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"3.13.5\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0ca86845ce43291e8f5692c7356fccf3bcf02bf4\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4\",\n \"reference\": \"0ca86845ce43291e8f5692c7356fccf3bcf02bf4\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=5.4.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-04T16:30:35+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.50.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.24.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.16.1","url":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar","requirements":{"php":{"php":"~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file +{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"40b84043be9c25e6feb38a9e014f1a9f\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-11T04:32:07+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"14.0.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/897a7dc209e49ee6cf04e689c41112df17967130\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.23\",\n \"squizlabs/php_codesniffer\": \"^4\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/14.0.0\"\n },\n \"time\": \"2025-09-21T18:21:47+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.28.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"66151cfbd25b50e8becd9f809fb704f01fd4d6f2\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/66151cfbd25b50e8becd9f809fb704f01fd4d6f2\",\n \"reference\": \"66151cfbd25b50e8becd9f809fb704f01fd4d6f2\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.7 || ^1.2.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.2\",\n \"squizlabs/php_codesniffer\": \"^4.0.1\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.2\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.42\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.4\",\n \"phpstan/phpstan-phpunit\": \"2.0.16\",\n \"phpstan/phpstan-strict-rules\": \"2.0.10\",\n \"phpunit/phpunit\": \"9.6.34|10.5.63|11.4.4|11.5.50|12.5.14\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.28.1\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2026-03-22T17:22:38+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"4.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=7.2.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-10T16:43:36+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.50.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.24.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.16.1","url":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar","requirements":{"php":{"php":"~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file From 61b88ed95f98560a3f6a87c9d4f432cc34b53a46 Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 15 Apr 2026 09:36:13 +0200 Subject: [PATCH 05/15] Update .phpcq.lock file --- .phpcq.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.phpcq.lock b/.phpcq.lock index 2d695cc..58f5971 100644 --- a/.phpcq.lock +++ b/.phpcq.lock @@ -1 +1 @@ -{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"40b84043be9c25e6feb38a9e014f1a9f\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-11T04:32:07+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"14.0.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/897a7dc209e49ee6cf04e689c41112df17967130\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.23\",\n \"squizlabs/php_codesniffer\": \"^4\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/14.0.0\"\n },\n \"time\": \"2025-09-21T18:21:47+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.28.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"66151cfbd25b50e8becd9f809fb704f01fd4d6f2\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/66151cfbd25b50e8becd9f809fb704f01fd4d6f2\",\n \"reference\": \"66151cfbd25b50e8becd9f809fb704f01fd4d6f2\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.7 || ^1.2.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.2\",\n \"squizlabs/php_codesniffer\": \"^4.0.1\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.2\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.42\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.4\",\n \"phpstan/phpstan-phpunit\": \"2.0.16\",\n \"phpstan/phpstan-strict-rules\": \"2.0.10\",\n \"phpunit/phpunit\": \"9.6.34|10.5.63|11.4.4|11.5.50|12.5.14\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.28.1\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2026-03-22T17:22:38+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"4.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=7.2.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-10T16:43:36+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.50.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.50.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.24.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.16.1","url":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar","requirements":{"php":{"php":"~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file +{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"40b84043be9c25e6feb38a9e014f1a9f\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-11T04:32:07+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"14.0.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/897a7dc209e49ee6cf04e689c41112df17967130\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.23\",\n \"squizlabs/php_codesniffer\": \"^4\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/14.0.0\"\n },\n \"time\": \"2025-09-21T18:21:47+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.28.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"66151cfbd25b50e8becd9f809fb704f01fd4d6f2\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/66151cfbd25b50e8becd9f809fb704f01fd4d6f2\",\n \"reference\": \"66151cfbd25b50e8becd9f809fb704f01fd4d6f2\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.7 || ^1.2.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.2\",\n \"squizlabs/php_codesniffer\": \"^4.0.1\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.2\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.42\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.4\",\n \"phpstan/phpstan-phpunit\": \"2.0.16\",\n \"phpstan/phpstan-strict-rules\": \"2.0.10\",\n \"phpunit/phpunit\": \"9.6.34|10.5.63|11.4.4|11.5.50|12.5.14\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.28.1\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2026-03-22T17:22:38+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"4.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=7.2.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-10T16:43:36+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.51.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.51.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.51.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.24.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.16.1","url":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar","requirements":{"php":{"php":"~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file From fcad46213cb42c2a6921b0e9c19aa625a4cd4553 Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 15 Apr 2026 09:36:26 +0200 Subject: [PATCH 06/15] Fix code style issues --- .../GridSeparatorElementController.php | 4 +- .../GridWrapperElementController.php | 2 +- src/Listener/Dca/ContentListener.php | 14 ++-- src/Migration/GridWrapperMigration.php | 84 ++++++++++--------- 4 files changed, 54 insertions(+), 50 deletions(-) diff --git a/src/Component/ContentElement/GridSeparatorElementController.php b/src/Component/ContentElement/GridSeparatorElementController.php index 3356525..d8207d4 100644 --- a/src/Component/ContentElement/GridSeparatorElementController.php +++ b/src/Component/ContentElement/GridSeparatorElementController.php @@ -89,7 +89,7 @@ protected function getIterator(ContentModel $model): GridIterator|null if ($parent) { try { - $iterator = $provider->getIterator('ce:' . $parent->id, (int)$parent->bs_grid); + $iterator = $provider->getIterator('ce:' . $parent->id, (int) $parent->bs_grid); $this->tagResponse('contao.db.tl_bs_grid.' . $parent->bs_grid); return $iterator; @@ -114,6 +114,6 @@ protected function getParent(ContentModel $model): ContentModel|null return $this->repositories->getRepository(ContentModel::class)->find($model->pid); } - return $this->repositories->getRepository(ContentModel::class)->find((int)$model->bs_grid_parent); + return $this->repositories->getRepository(ContentModel::class)->find((int) $model->bs_grid_parent); } } diff --git a/src/Component/ContentElement/GridWrapperElementController.php b/src/Component/ContentElement/GridWrapperElementController.php index 2bb9889..96fe5e9 100644 --- a/src/Component/ContentElement/GridWrapperElementController.php +++ b/src/Component/ContentElement/GridWrapperElementController.php @@ -46,7 +46,7 @@ protected function getResponse(FragmentTemplate $template, ContentModel $model, protected function getIterator(ContentModel $model): GridIterator|null { try { - $iterator = $this->provider->getIterator('ce:' . $model->id, (int)$model->bs_grid); + $iterator = $this->provider->getIterator('ce:' . $model->id, (int) $model->bs_grid); $this->tagResponse('contao.db.tl_bs_grid.' . $model->bs_grid); return $iterator; diff --git a/src/Listener/Dca/ContentListener.php b/src/Listener/Dca/ContentListener.php index 4bf9212..f944bda 100644 --- a/src/Listener/Dca/ContentListener.php +++ b/src/Listener/Dca/ContentListener.php @@ -19,9 +19,9 @@ use ContaoBootstrap\Core\Environment; use Netzmacht\Contao\Toolkit\Data\Model\RepositoryManager; use Override; -use function sprintf; - use stdClass; + +use function sprintf; use function time; /** @@ -32,10 +32,10 @@ final class ContentListener extends AbstractWrapperDcaListener { /** - * @param Environment $environment Bootstrap environment. - * @param ContaoFramework $framework Contao framework. - * @param ImageSizes $imageSizes Image sizes. - * @param BackendUser $user Contao backend user. + * @param Environment $environment Bootstrap environment. + * @param ContaoFramework $framework Contao framework. + * @param ImageSizes $imageSizes Image sizes. + * @param BackendUser $user Contao backend user. */ public function __construct( Environment $environment, @@ -140,7 +140,7 @@ public function getGalleryTemplates(): array /** * Dynamically add flags to the "multiSRC" field. * - * @param mixed $value Given value. + * @param mixed $value Given value. * @param DataContainer $dataContainer Data Container driver. * * @SuppressWarnings(PHPMD.Superglobals) diff --git a/src/Migration/GridWrapperMigration.php b/src/Migration/GridWrapperMigration.php index ffd8472..36742df 100644 --- a/src/Migration/GridWrapperMigration.php +++ b/src/Migration/GridWrapperMigration.php @@ -7,9 +7,13 @@ use Contao\CoreBundle\Migration\AbstractMigration; use Contao\CoreBundle\Migration\MigrationResult; use Doctrine\DBAL\Connection; - use Override; +use function array_map; +use function array_reduce; +use function array_sum; +use function count; + final class GridWrapperMigration extends AbstractMigration { public function __construct(private readonly Connection $connection, private readonly bool $enableMigration = false) @@ -42,44 +46,44 @@ public function shouldRun(): bool #[Override] public function run(): MigrationResult { - $sql = << grid_start.sorting - ORDER BY grid_stop.sorting ASC - LIMIT 1 - ) AS grid_stop_id - FROM tl_content grid_start - LEFT JOIN tl_content el - ON el.pid = grid_start.pid - AND el.ptable = grid_start.ptable - AND el.sorting > grid_start.sorting - AND el.sorting < ( - SELECT MIN(grid_stop.sorting) - FROM tl_content grid_stop - WHERE grid_stop.pid = grid_start.pid - AND grid_stop.ptable = grid_start.ptable - AND grid_stop.type = 'bs_gridStop' - AND grid_stop.sorting > grid_start.sorting - ) - WHERE grid_start.type = 'bs_gridStart' - ORDER BY grid_start.pid, grid_start.ptable, grid_start.sorting, el.sorting + $sql = <<<'SQL' + SELECT + grid_start.id AS grid_start_id, + grid_start.pid AS pid, + grid_start.ptable AS ptable, + el.id AS element_id, + el.type AS element_type, + el.sorting AS element_sorting, + ( + SELECT grid_stop.id + FROM tl_content grid_stop + WHERE grid_stop.pid = grid_start.pid + AND grid_stop.ptable = grid_start.ptable + AND grid_stop.type = 'bs_gridStop' + AND grid_stop.sorting > grid_start.sorting + ORDER BY grid_stop.sorting ASC + LIMIT 1 + ) AS grid_stop_id + FROM tl_content grid_start + LEFT JOIN tl_content el + ON el.pid = grid_start.pid + AND el.ptable = grid_start.ptable + AND el.sorting > grid_start.sorting + AND el.sorting < ( + SELECT MIN(grid_stop.sorting) + FROM tl_content grid_stop + WHERE grid_stop.pid = grid_start.pid + AND grid_stop.ptable = grid_start.ptable + AND grid_stop.type = 'bs_gridStop' + AND grid_stop.sorting > grid_start.sorting + ) + WHERE grid_start.type = 'bs_gridStart' + ORDER BY grid_start.pid, grid_start.ptable, grid_start.sorting, el.sorting SQL; $contentElements = $this->connection->executeQuery($sql)->fetchAllAssociative(); - $gridContainers = array_reduce($contentElements, function (array $carry, array $row) { + $gridContainers = array_reduce($contentElements, static function (array $carry, array $row) { $startId = $row['grid_start_id']; $carry[$startId] ??= [ @@ -98,22 +102,22 @@ public function run(): MigrationResult $elementCount = array_sum( array_map( static fn (array $gridContainer) => count($gridContainer['elements']), - $gridContainers - ) + $gridContainers, + ), ); foreach ($gridContainers as $gridContainer) { $this->connection->update( 'tl_content', ['type' => 'bs_grid_wrapper'], - ['id' => $gridContainer['start_id']] + ['id' => $gridContainer['start_id']], ); foreach ($gridContainer['elements'] as $element) { $this->connection->update( 'tl_content', ['pid' => $gridContainer['start_id'], 'ptable' => 'tl_content'], - ['id' => $element['element_id']] + ['id' => $element['element_id']], ); } @@ -122,7 +126,7 @@ public function run(): MigrationResult return $this->createResult( true, - 'Migrated ' . count($gridContainers) . ' grid containers and ' . $elementCount . ' elements.' + 'Migrated ' . count($gridContainers) . ' grid containers and ' . $elementCount . ' elements.', ); } } From a129dc64cbb37fa1314701853aca0fb6e13f2473 Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 15 Apr 2026 10:41:53 +0200 Subject: [PATCH 07/15] Use bundle configuration to enable wrapper migration --- README.md | 19 ++++++++++--- psalm.xml | 1 + src/DependencyInjection/Configuration.php | 27 +++++++++++++++++++ .../ContaoBootstrapGridExtension.php | 8 ++++++ src/Resources/config/config.yaml | 2 -- 5 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 src/DependencyInjection/Configuration.php diff --git a/README.md b/README.md index f5db945..6176442 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,19 @@ class AppKernel ``` Migration -------- +--------- + +To automatically migrate your grid from Start- and Stop-Wrappers to nested fragments, you have to enable the migration +via the bundle configuration. Create or extend the file `config/packages/contao_bootstrap_grid.yaml` in your Symfony +application: + +```yaml +contao_bootstrap_grid: + enable_wrapper_migration: true +``` + +Afterwards you can run the migration in the Contao Manager or via CLI: -To automatically migrate your grid from Start- and Stop-Wrappers to nested fragments. You have to enbale the Migration -by adding `BS_GRID_WRAPPER_MIGRATION=true` parameter to your .env.local. Afterwards you can run the migration in the -contao manager over by cli. +```bash +$ php vendor/bin/contao-console contao:migrate +``` diff --git a/psalm.xml b/psalm.xml index 4499dac..dc16451 100644 --- a/psalm.xml +++ b/psalm.xml @@ -4,6 +4,7 @@ + diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php new file mode 100644 index 0000000..1ec2c39 --- /dev/null +++ b/src/DependencyInjection/Configuration.php @@ -0,0 +1,27 @@ +getRootNode() + ->children() + ->booleanNode('enable_wrapper_migration') + ->defaultFalse() + ->end() + ->end(); + + return $treeBuilder; + } +} diff --git a/src/DependencyInjection/ContaoBootstrapGridExtension.php b/src/DependencyInjection/ContaoBootstrapGridExtension.php index 998871b..2f97e49 100644 --- a/src/DependencyInjection/ContaoBootstrapGridExtension.php +++ b/src/DependencyInjection/ContaoBootstrapGridExtension.php @@ -18,6 +18,14 @@ final class ContaoBootstrapGridExtension extends Extension #[Override] public function load(array $configs, ContainerBuilder $container): void { + $configuration = new Configuration(); + $config = $this->processConfiguration($configuration, $configs); + + $container->setParameter( + 'contao_bootstrap.grid.enable_wrapper_migration', + $config['enable_wrapper_migration'], + ); + $loader = new YamlFileLoader( $container, new FileLocator(__DIR__ . '/../Resources/config'), diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index bf6b0ef..a72bdf7 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -4,5 +4,3 @@ parameters: - 'Multilingual' - 'Contao\DC_Table' - 'Terminal42\DcMultilingualBundle\Driver' - - contao_bootstrap.grid.enable_wrapper_migration: '%env(bool:default::BS_GRID_WRAPPER_MIGRATION)%' From d1c41cb5c549cd5e558dd1e932e1889733a9adde Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 15 Apr 2026 10:56:28 +0200 Subject: [PATCH 08/15] Wrap grid wrapper migration in a database transaction --- src/Migration/GridWrapperMigration.php | 32 +++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/Migration/GridWrapperMigration.php b/src/Migration/GridWrapperMigration.php index 36742df..d7411bc 100644 --- a/src/Migration/GridWrapperMigration.php +++ b/src/Migration/GridWrapperMigration.php @@ -106,23 +106,29 @@ public function run(): MigrationResult ), ); - foreach ($gridContainers as $gridContainer) { - $this->connection->update( - 'tl_content', - ['type' => 'bs_grid_wrapper'], - ['id' => $gridContainer['start_id']], - ); - - foreach ($gridContainer['elements'] as $element) { + $this->connection->transactional(function () use ($gridContainers): void { + foreach ($gridContainers as $gridContainer) { $this->connection->update( 'tl_content', - ['pid' => $gridContainer['start_id'], 'ptable' => 'tl_content'], - ['id' => $element['element_id']], + ['type' => 'bs_grid_wrapper'], + ['id' => $gridContainer['start_id']], ); - } - $this->connection->delete('tl_content', ['id' => $gridContainer['stop_id']]); - } + foreach ($gridContainer['elements'] as $element) { + $this->connection->update( + 'tl_content', + ['pid' => $gridContainer['start_id'], 'ptable' => 'tl_content'], + ['id' => $element['element_id']], + ); + } + + if ($gridContainer['stop_id'] === null) { + continue; + } + + $this->connection->delete('tl_content', ['id' => $gridContainer['stop_id']]); + } + }); return $this->createResult( true, From 22bab5ee14434b033a4c8f14ed66a03906fa4253 Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 15 Apr 2026 11:10:57 +0200 Subject: [PATCH 09/15] Complete twig temnplate - Fix iterator for each column - Add clearfix divs to reset grid rows in bs_grid_wrapper template --- .../twig/content_element/bs_grid_wrapper.html.twig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Resources/contao/templates/twig/content_element/bs_grid_wrapper.html.twig b/src/Resources/contao/templates/twig/content_element/bs_grid_wrapper.html.twig index 51b135b..2999c98 100644 --- a/src/Resources/contao/templates/twig/content_element/bs_grid_wrapper.html.twig +++ b/src/Resources/contao/templates/twig/content_element/bs_grid_wrapper.html.twig @@ -3,11 +3,15 @@ {% block content %} {% if iterator is not null %}
-
- {% for fragment in nested_fragments %} - {{ content_element(fragment) }} + {% for fragment in nested_fragments %} + {% for reset in iterator.resets %} +
{% endfor %} -
+
+ {{ content_element(fragment) }} +
+ {{ iterator.next() }} + {% endfor %}
{% else %} {{ 'ERR.bsGridParentMissing'|trans({}, 'contao_default') }} From b4c94ce5120d76b2119b3eb3f30075b8c64915de Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 29 Apr 2026 11:27:53 +0200 Subject: [PATCH 10/15] Deprecate legacy grid elements and introduce wrapper migration config - Deprecates `bs_gridStart`, `bs_gridSeparator`, and `bs_gridStop`, recommending `bs_grid_wrapper` instead. - Adds `enable_legacy_elements` configuration to toggle legacy elements. - Updates documentation and adds guardrails for deprecated elements. --- CHANGELOG.md | 6 +++ README.md | 13 +++++ .../GridSeparatorElementController.php | 21 +++++++- .../GridStartElementController.php | 48 ++++++++++++++++++- .../GridStopElementController.php | 21 +++++++- .../GridWrapperElementController.php | 2 + src/DependencyInjection/Configuration.php | 3 ++ .../ContaoBootstrapGridExtension.php | 6 +++ src/Resources/config/legacy.yaml | 32 +++++++++++++ src/Resources/config/services.yaml | 32 ------------- 10 files changed, 149 insertions(+), 35 deletions(-) create mode 100644 src/Resources/config/legacy.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index feb553f..77aebc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,15 @@ Changelog [Unreleased] ------------ +### Deprecated + + - Legacy content elements `bs_gridStart`, `bs_gridStop` and `bs_gridSeparator` are deprecated. + Use `bs_grid_wrapper` instead. + ### Added - Add support for nested fragments +- Bundle configuration option `enable_legacy_elements` (default: `true`) to disable legacy content elements. 3.0.5 (2025-03-02) ------------------ diff --git a/README.md b/README.md index 6176442..5925acb 100644 --- a/README.md +++ b/README.md @@ -89,3 +89,16 @@ Afterwards you can run the migration in the Contao Manager or via CLI: ```bash $ php vendor/bin/contao-console contao:migrate ``` + +Deprecated +---------- + +The legacy content elements `bs_gridStart`, `bs_gridStop` and `bs_gridSeparator` are deprecated and will be removed in +a future major version. Use `bs_grid_wrapper` instead. + +To disable the legacy elements now, set the following configuration: + +```yaml +contao_bootstrap_grid: + enable_legacy_elements: false +``` diff --git a/src/Component/ContentElement/GridSeparatorElementController.php b/src/Component/ContentElement/GridSeparatorElementController.php index d8207d4..124ca15 100644 --- a/src/Component/ContentElement/GridSeparatorElementController.php +++ b/src/Component/ContentElement/GridSeparatorElementController.php @@ -21,7 +21,17 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\Translation\TranslatorInterface; -/** @ContentElement("bs_gridSeparator", category="bs_grid", template="ce_bs_gridSeparator") */ +use function sprintf; +use function trigger_error; + +use const E_USER_DEPRECATED; + +/** + * @deprecated Use GridWrapperElementController with bs_grid_wrapper instead. + * Will be removed in a future major version. + * + * @ContentElement("bs_gridSeparator", category="bs_grid", template="ce_bs_gridSeparator") + */ final class GridSeparatorElementController extends AbstractGridElementController { public function __construct( @@ -34,6 +44,15 @@ public function __construct( TranslatorInterface $translator, private readonly RepositoryManager $repositories, ) { + trigger_error( + sprintf( + 'Content element "%s" is deprecated. Use "%s" instead. Will be removed in a future major version.', + 'bs_gridSeparator', + 'bs_grid_wrapper', + ), + E_USER_DEPRECATED, + ); + parent::__construct( $templateRenderer, $scopeMatcher, diff --git a/src/Component/ContentElement/GridStartElementController.php b/src/Component/ContentElement/GridStartElementController.php index c26ef45..b37b714 100644 --- a/src/Component/ContentElement/GridStartElementController.php +++ b/src/Component/ContentElement/GridStartElementController.php @@ -5,17 +5,63 @@ namespace ContaoBootstrap\Grid\Component\ContentElement; use Contao\ContentModel; +use Contao\CoreBundle\Security\Authentication\Token\TokenChecker; use Contao\CoreBundle\ServiceAnnotation\ContentElement; use Contao\Model; +use ContaoBootstrap\Core\Helper\ColorRotate; use ContaoBootstrap\Grid\Exception\GridNotFound; use ContaoBootstrap\Grid\GridIterator; +use ContaoBootstrap\Grid\GridProvider; +use Netzmacht\Contao\Toolkit\Response\ResponseTagger; +use Netzmacht\Contao\Toolkit\Routing\RequestScopeMatcher; +use Netzmacht\Contao\Toolkit\View\Template\TemplateRenderer; use Override; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\Translation\TranslatorInterface; -/** @ContentElement("bs_gridStart", category="bs_grid", template="ce_bs_gridStart") */ +use function sprintf; +use function trigger_error; + +use const E_USER_DEPRECATED; + +/** + * @deprecated Use GridWrapperElementController with bs_grid_wrapper instead. + * Will be removed in a future major version. + * + * @ContentElement("bs_gridStart", category="bs_grid", template="ce_bs_gridStart") + */ final class GridStartElementController extends AbstractGridElementController { + public function __construct( + TemplateRenderer $templateRenderer, + RequestScopeMatcher $scopeMatcher, + ResponseTagger $responseTagger, + TokenChecker $tokenChecker, + GridProvider $gridProvider, + ColorRotate $colorRotate, + TranslatorInterface $translator, + ) { + trigger_error( + sprintf( + 'Content element "%s" is deprecated. Use "%s" instead. Will be removed in a future major version.', + 'bs_gridStart', + 'bs_grid_wrapper', + ), + E_USER_DEPRECATED, + ); + + parent::__construct( + $templateRenderer, + $scopeMatcher, + $responseTagger, + $tokenChecker, + $gridProvider, + $colorRotate, + $translator, + ); + } + /** {@inheritDoc} */ #[Override] protected function preGenerate( diff --git a/src/Component/ContentElement/GridStopElementController.php b/src/Component/ContentElement/GridStopElementController.php index 59f694c..6a2ff6f 100644 --- a/src/Component/ContentElement/GridStopElementController.php +++ b/src/Component/ContentElement/GridStopElementController.php @@ -21,7 +21,17 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\Translation\TranslatorInterface; -/** @ContentElement("bs_gridStop", category="bs_grid", template="ce_bs_gridStop") */ +use function sprintf; +use function trigger_error; + +use const E_USER_DEPRECATED; + +/** + * @deprecated Use GridWrapperElementController with bs_grid_wrapper instead. + * Will be removed in a future major version. + * + * @ContentElement("bs_gridStop", category="bs_grid", template="ce_bs_gridStop") + */ final class GridStopElementController extends AbstractGridElementController { public function __construct( @@ -34,6 +44,15 @@ public function __construct( TranslatorInterface $translator, private readonly RepositoryManager $repositories, ) { + trigger_error( + sprintf( + 'Content element "%s" is deprecated. Use "%s" instead. Will be removed in a future major version.', + 'bs_gridStop', + 'bs_grid_wrapper', + ), + E_USER_DEPRECATED, + ); + parent::__construct( $templateRenderer, $scopeMatcher, diff --git a/src/Component/ContentElement/GridWrapperElementController.php b/src/Component/ContentElement/GridWrapperElementController.php index 96fe5e9..ed1b680 100644 --- a/src/Component/ContentElement/GridWrapperElementController.php +++ b/src/Component/ContentElement/GridWrapperElementController.php @@ -16,6 +16,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use function array_key_first; + /** @psalm-suppress PropertyNotSetInConstructor */ #[AsContentElement('bs_grid_wrapper', 'bs_grid', nestedFragments: true)] final class GridWrapperElementController extends AbstractContentElementController diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 1ec2c39..02106bd 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -20,6 +20,9 @@ public function getConfigTreeBuilder(): TreeBuilder ->booleanNode('enable_wrapper_migration') ->defaultFalse() ->end() + ->booleanNode('enable_legacy_elements') + ->defaultTrue() + ->end() ->end(); return $treeBuilder; diff --git a/src/DependencyInjection/ContaoBootstrapGridExtension.php b/src/DependencyInjection/ContaoBootstrapGridExtension.php index 2f97e49..985f6e8 100644 --- a/src/DependencyInjection/ContaoBootstrapGridExtension.php +++ b/src/DependencyInjection/ContaoBootstrapGridExtension.php @@ -34,5 +34,11 @@ public function load(array $configs, ContainerBuilder $container): void $loader->load('config.yaml'); $loader->load('services.yaml'); $loader->load('listeners.yaml'); + + if (! $config['enable_legacy_elements']) { + return; + } + + $loader->load('legacy.yaml'); } } diff --git a/src/Resources/config/legacy.yaml b/src/Resources/config/legacy.yaml new file mode 100644 index 0000000..36ee1ae --- /dev/null +++ b/src/Resources/config/legacy.yaml @@ -0,0 +1,32 @@ +services: + ContaoBootstrap\Grid\Component\ContentElement\GridStartElementController: + arguments: + - '@netzmacht.contao_toolkit.template_renderer' + - '@netzmacht.contao_toolkit.routing.scope_matcher' + - '@netzmacht.contao_toolkit.response_tagger' + - '@contao.security.token_checker' + - '@contao_bootstrap.grid.grid_provider' + - '@contao_bootstrap.core.helper.color_rotate' + - '@translator' + + ContaoBootstrap\Grid\Component\ContentElement\GridSeparatorElementController: + arguments: + - '@netzmacht.contao_toolkit.template_renderer' + - '@netzmacht.contao_toolkit.routing.scope_matcher' + - '@netzmacht.contao_toolkit.response_tagger' + - '@contao.security.token_checker' + - '@contao_bootstrap.grid.grid_provider' + - '@contao_bootstrap.core.helper.color_rotate' + - '@translator' + - '@netzmacht.contao_toolkit.repository_manager' + + ContaoBootstrap\Grid\Component\ContentElement\GridStopElementController: + arguments: + - '@netzmacht.contao_toolkit.template_renderer' + - '@netzmacht.contao_toolkit.routing.scope_matcher' + - '@netzmacht.contao_toolkit.response_tagger' + - '@contao.security.token_checker' + - '@contao_bootstrap.grid.grid_provider' + - '@contao_bootstrap.core.helper.color_rotate' + - '@translator' + - '@netzmacht.contao_toolkit.repository_manager' diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index cb28f80..995708d 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -18,38 +18,6 @@ services: - '@contao_bootstrap.grid.grid_provider' - '@contao_bootstrap.core.helper.color_rotate' - ContaoBootstrap\Grid\Component\ContentElement\GridStartElementController: - arguments: - - '@netzmacht.contao_toolkit.template_renderer' - - '@netzmacht.contao_toolkit.routing.scope_matcher' - - '@netzmacht.contao_toolkit.response_tagger' - - '@contao.security.token_checker' - - '@contao_bootstrap.grid.grid_provider' - - '@contao_bootstrap.core.helper.color_rotate' - - '@translator' - - ContaoBootstrap\Grid\Component\ContentElement\GridSeparatorElementController: - arguments: - - '@netzmacht.contao_toolkit.template_renderer' - - '@netzmacht.contao_toolkit.routing.scope_matcher' - - '@netzmacht.contao_toolkit.response_tagger' - - '@contao.security.token_checker' - - '@contao_bootstrap.grid.grid_provider' - - '@contao_bootstrap.core.helper.color_rotate' - - '@translator' - - '@netzmacht.contao_toolkit.repository_manager' - - ContaoBootstrap\Grid\Component\ContentElement\GridStopElementController: - arguments: - - '@netzmacht.contao_toolkit.template_renderer' - - '@netzmacht.contao_toolkit.routing.scope_matcher' - - '@netzmacht.contao_toolkit.response_tagger' - - '@contao.security.token_checker' - - '@contao_bootstrap.grid.grid_provider' - - '@contao_bootstrap.core.helper.color_rotate' - - '@translator' - - '@netzmacht.contao_toolkit.repository_manager' - ContaoBootstrap\Grid\Component\ContentElement\GalleryElementController: arguments: - '@netzmacht.contao_toolkit.template_renderer' From 278af1939f0ac579eabe48244684fa6ae6afa232 Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 29 Apr 2026 11:32:02 +0200 Subject: [PATCH 11/15] Remove unused `array_key_first` import from GridWrapperElementController --- src/Component/ContentElement/GridWrapperElementController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Component/ContentElement/GridWrapperElementController.php b/src/Component/ContentElement/GridWrapperElementController.php index ed1b680..96fe5e9 100644 --- a/src/Component/ContentElement/GridWrapperElementController.php +++ b/src/Component/ContentElement/GridWrapperElementController.php @@ -16,8 +16,6 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use function array_key_first; - /** @psalm-suppress PropertyNotSetInConstructor */ #[AsContentElement('bs_grid_wrapper', 'bs_grid', nestedFragments: true)] final class GridWrapperElementController extends AbstractContentElementController From f75022af08ebda59490ebc63af8aed3b7b2b53f7 Mon Sep 17 00:00:00 2001 From: "c.boelter" Date: Thu, 7 May 2026 17:12:27 +0200 Subject: [PATCH 12/15] rework migration to handle also separators and nested grids --- .phpcq.lock | 2 +- src/Migration/GridWrapperMigration.php | 432 ++++++++++++++++++++----- 2 files changed, 345 insertions(+), 89 deletions(-) diff --git a/.phpcq.lock b/.phpcq.lock index 58f5971..a940923 100644 --- a/.phpcq.lock +++ b/.phpcq.lock @@ -1 +1 @@ -{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"40b84043be9c25e6feb38a9e014f1a9f\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"reference\": \"845eb62303d2ca9b289ef216356568ccc075ffd1\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-11T04:32:07+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"14.0.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/897a7dc209e49ee6cf04e689c41112df17967130\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.23\",\n \"squizlabs/php_codesniffer\": \"^4\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/14.0.0\"\n },\n \"time\": \"2025-09-21T18:21:47+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.28.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"66151cfbd25b50e8becd9f809fb704f01fd4d6f2\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/66151cfbd25b50e8becd9f809fb704f01fd4d6f2\",\n \"reference\": \"66151cfbd25b50e8becd9f809fb704f01fd4d6f2\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.7 || ^1.2.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.2\",\n \"squizlabs/php_codesniffer\": \"^4.0.1\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.2\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.42\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.4\",\n \"phpstan/phpstan-phpunit\": \"2.0.16\",\n \"phpstan/phpstan-strict-rules\": \"2.0.10\",\n \"phpunit/phpunit\": \"9.6.34|10.5.63|11.4.4|11.5.50|12.5.14\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.28.1\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2026-03-22T17:22:38+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"4.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=7.2.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-10T16:43:36+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.51.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.51.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.51.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.24.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.24.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.16.1","url":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar","requirements":{"php":{"php":"~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.16.1/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file +{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"40b84043be9c25e6feb38a9e014f1a9f\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"963f0c67bffde0eac41b56be71ac0e8ba132f0bd\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd\",\n \"reference\": \"963f0c67bffde0eac41b56be71ac0e8ba132f0bd\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2026-05-06T08:26:05+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"14.0.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/897a7dc209e49ee6cf04e689c41112df17967130\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.23\",\n \"squizlabs/php_codesniffer\": \"^4\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/14.0.0\"\n },\n \"time\": \"2025-09-21T18:21:47+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.29.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"81fce13c4ef4b53a03e5cfa6ce36afc191c1598e\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/81fce13c4ef4b53a03e5cfa6ce36afc191c1598e\",\n \"reference\": \"81fce13c4ef4b53a03e5cfa6ce36afc191c1598e\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.7 || ^1.2.1\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.2\",\n \"squizlabs/php_codesniffer\": \"^4.0.1\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.2\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.54\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.4\",\n \"phpstan/phpstan-phpunit\": \"2.0.16\",\n \"phpstan/phpstan-strict-rules\": \"2.0.11\",\n \"phpunit/phpunit\": \"9.6.34|10.5.63|11.4.4|11.5.55|12.5.24\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.29.0\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2026-05-07T05:48:08+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"4.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=7.2.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-10T16:43:36+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.51.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.51.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.51.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.18.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.18.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.18.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.5.0","url":"https://github.com/vimeo/psalm/releases/download/6.5.0/psalm.phar","requirements":{"php":{"php":"~8.1.17 || ~8.2.4 || ~8.3.0 || ~8.4.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.5.0/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file diff --git a/src/Migration/GridWrapperMigration.php b/src/Migration/GridWrapperMigration.php index d7411bc..01a8f70 100644 --- a/src/Migration/GridWrapperMigration.php +++ b/src/Migration/GridWrapperMigration.php @@ -7,11 +7,9 @@ use Contao\CoreBundle\Migration\AbstractMigration; use Contao\CoreBundle\Migration\MigrationResult; use Doctrine\DBAL\Connection; +use Exception; use Override; -use function array_map; -use function array_reduce; -use function array_sum; use function count; final class GridWrapperMigration extends AbstractMigration @@ -20,6 +18,7 @@ public function __construct(private readonly Connection $connection, private rea { } + /** @throws Exception */ #[Override] public function shouldRun(): bool { @@ -28,111 +27,368 @@ public function shouldRun(): bool } $schemaManager = $this->connection->createSchemaManager(); + if (! $schemaManager->tablesExist(['tl_bs_grid', 'tl_content'])) { return false; } - $queryBuilder = $this->connection->createQueryBuilder(); + if (! $schemaManager->tablesExist(['tl_content'])) { + return false; + } + + $count = $this->connection->fetchOne( + "SELECT COUNT(*) FROM tl_content WHERE type IN ('bs_gridStart', 'bs_gridStop', 'bs_gridSeparator')", + ); - return $queryBuilder - ->select('COUNT(tc.id) as count') - ->from('tl_content', 'tc') - ->where($queryBuilder->expr()->eq('tc.type', ':type')) - ->setParameter('type', 'bs_gridStart') - ->executeQuery() - ->fetchOne() > 0; + return $count > 0; } - #[Override] + /** @throws Exception */ public function run(): MigrationResult { - $sql = <<<'SQL' - SELECT - grid_start.id AS grid_start_id, - grid_start.pid AS pid, - grid_start.ptable AS ptable, - el.id AS element_id, - el.type AS element_type, - el.sorting AS element_sorting, - ( - SELECT grid_stop.id - FROM tl_content grid_stop - WHERE grid_stop.pid = grid_start.pid - AND grid_stop.ptable = grid_start.ptable - AND grid_stop.type = 'bs_gridStop' - AND grid_stop.sorting > grid_start.sorting - ORDER BY grid_stop.sorting ASC - LIMIT 1 - ) AS grid_stop_id - FROM tl_content grid_start - LEFT JOIN tl_content el - ON el.pid = grid_start.pid - AND el.ptable = grid_start.ptable - AND el.sorting > grid_start.sorting - AND el.sorting < ( - SELECT MIN(grid_stop.sorting) - FROM tl_content grid_stop - WHERE grid_stop.pid = grid_start.pid - AND grid_stop.ptable = grid_start.ptable - AND grid_stop.type = 'bs_gridStop' - AND grid_stop.sorting > grid_start.sorting - ) - WHERE grid_start.type = 'bs_gridStart' - ORDER BY grid_start.pid, grid_start.ptable, grid_start.sorting, el.sorting -SQL; - - $contentElements = $this->connection->executeQuery($sql)->fetchAllAssociative(); - - $gridContainers = array_reduce($contentElements, static function (array $carry, array $row) { - $startId = $row['grid_start_id']; - - $carry[$startId] ??= [ - 'start_id' => $startId, - 'stop_id' => $row['grid_stop_id'], - 'elements' => [], - ]; - - if ($row['element_id'] !== null) { - $carry[$startId]['elements'][] = $row; + $this->connection->transactional(function (): void { + $roots = $this->connection->fetchAllAssociative( + "SELECT DISTINCT pid, ptable FROM tl_content + WHERE type IN ('bs_gridStart', 'bs_gridStop', 'bs_gridSeparator')", + ); + + foreach ($roots as $root) { + $elements = $this->fetchElements((int) $root['pid'], $root['ptable']); + $this->processElements($elements, (int) $root['pid'], $root['ptable']); } + }); + + return $this->createResult(true); + } + + /** + * @return array> + * + * @throws Exception + */ + private function fetchElements(int $pid, string $ptable): array + { + return $this->connection->fetchAllAssociative( + 'SELECT * FROM tl_content WHERE pid = :pid AND ptable = :ptable ORDER BY sorting ASC', + ['pid' => $pid, 'ptable' => $ptable], + ); + } + + /** + * Main recursive processor. Works with an index pointer through the flat list. + * + * @param array> $elements + * + * @throws Exception + */ + private function processElements(array $elements, int $pid, string $ptable): void + { + $idx = 0; + $count = count($elements); + + while ($idx < $count) { + $element = $elements[$idx]; + + match ($element['type']) { + 'bs_gridStart' => $idx = $this->handleGridStart($elements, $idx, $pid, $ptable), + 'bs_gridSeparator' => $idx = $this->handleSeparator($elements, $idx, $pid, $ptable), + 'bs_gridStop' => $idx = $this->handleGridStop($elements, $idx), + default => $idx++, + }; + } + } - return $carry; - }, []); + /** + * Handles a bs_gridStart: converts it to bs_grid_wrapper, + * collects all children until matching bs_gridStop and moves them into the wrapper. + * If there are bs_gridSeparators at the top level, elements before the first separator + * are wrapped in an implicit element_group. + * + * @param array> $elements + * + * @throws Exception + */ + private function handleGridStart(array $elements, int $index): int + { + $startElement = $elements[$index]; + $wrapperId = (int) $startElement['id']; - $elementCount = array_sum( - array_map( - static fn (array $gridContainer) => count($gridContainer['elements']), - $gridContainers, - ), + $this->connection->executeStatement( + "UPDATE tl_content SET type = 'bs_grid_wrapper' WHERE id = :id", + ['id' => $wrapperId], ); - $this->connection->transactional(function () use ($gridContainers): void { - foreach ($gridContainers as $gridContainer) { - $this->connection->update( - 'tl_content', - ['type' => 'bs_grid_wrapper'], - ['id' => $gridContainer['start_id']], - ); - - foreach ($gridContainer['elements'] as $element) { - $this->connection->update( - 'tl_content', - ['pid' => $gridContainer['start_id'], 'ptable' => 'tl_content'], - ['id' => $element['element_id']], - ); + // Collect all children until matching bs_gridStop + [$children, $nextIndex] = $this->collectUntilMatchingStop($elements, $index + 1); + + // Move children into wrapper with fresh sorting + $this->moveElementsToParent($children, $wrapperId, 'tl_content'); + + // Only wrap leading elements in an implicit group if there are top-level separators + $this->wrapLeadingElementsInGroup($wrapperId, $startElement); + + // Now recursively process the children inside the wrapper + $movedElements = $this->fetchElements($wrapperId, 'tl_content'); + $this->processElements($movedElements, $wrapperId, 'tl_content'); + + return $nextIndex; + } + + /** + * Wraps all elements before the first bs_gridSeparator inside a wrapper + * into a newly created element_group. + * If there is no top-level bs_gridSeparator, nothing is done. + * + * @param array $referenceElement Used to copy tstamp for the new row + * + * @throws Exception + */ + private function wrapLeadingElementsInGroup(int $wrapperPid, array $referenceElement): void + { + $children = $this->fetchElements($wrapperPid, 'tl_content'); + + // If there is no bs_gridSeparator at depth 0, elements stay directly in the wrapper + if (! $this->hasTopLevelSeparator($children)) { + return; + } + + // Collect elements before the first bs_gridSeparator at depth 0 + $leadingElements = []; + $depth = 0; + + foreach ($children as $child) { + $type = $child['type']; + + if ($type === 'bs_gridStart') { + $depth++; + $leadingElements[] = $child; + } elseif ($type === 'bs_gridStop') { + if ($depth === 0) { + break; } - if ($gridContainer['stop_id'] === null) { - continue; + $depth--; + $leadingElements[] = $child; + } elseif ($type === 'bs_gridSeparator') { + if ($depth === 0) { + // Stop here, do not include the separator itself + break; } - $this->connection->delete('tl_content', ['id' => $gridContainer['stop_id']]); + $leadingElements[] = $child; + } else { + $leadingElements[] = $child; } - }); + } - return $this->createResult( - true, - 'Migrated ' . count($gridContainers) . ' grid containers and ' . $elementCount . ' elements.', + // Nothing to wrap (grid starts directly with a separator) + if (empty($leadingElements)) { + return; + } + + // Create a new element_group as first child of the wrapper + // sorting = 64 so it sits before the existing children which start at 128 + $this->connection->executeStatement( + "INSERT INTO tl_content (pid, ptable, sorting, tstamp, type) +VALUES (:pid, :ptable, :sorting, :tstamp, 'element_group')", + [ + 'pid' => $wrapperPid, + 'ptable' => 'tl_content', + 'sorting' => 64, + 'tstamp' => $referenceElement['tstamp'], + ], + ); + + $groupId = (int) $this->connection->lastInsertId(); + + $this->moveElementsToParent($leadingElements, $groupId, 'tl_content'); + } + + /** + * Checks whether there is at least one bs_gridSeparator at depth 0 in the given element list. + * + * @param array> $elements + */ + private function hasTopLevelSeparator(array $elements): bool + { + $depth = 0; + + foreach ($elements as $element) { + $type = $element['type']; + + if ($type === 'bs_gridStart') { + $depth++; + } elseif ($type === 'bs_gridStop') { + $depth--; + } elseif ($type === 'bs_gridSeparator' && $depth === 0) { + return true; + } + } + + return false; + } + + /** + * Handles a bs_gridSeparator: converts it to element_group, + * collects all following siblings until next bs_gridSeparator or bs_gridStop + * and moves them into the group. + * + * @param array> $elements + * + * @throws Exception + */ + private function handleSeparator(array $elements, int $index): int + { + $separatorElement = $elements[$index]; + $groupId = (int) $separatorElement['id']; + + $this->connection->executeStatement( + "UPDATE tl_content SET type = 'element_group' WHERE id = :id", + ['id' => $groupId], + ); + + // Collect following siblings until next separator or stop (depth-aware) + [$children, $nextIndex] = $this->collectUntilNextSeparatorOrStop($elements, $index + 1); + + // Move collected elements into the element_group + $this->moveElementsToParent($children, $groupId, 'tl_content'); + + // Recursively process elements inside the group (e.g. nested grids inside a group) + $movedElements = $this->fetchElements($groupId, 'tl_content'); + $this->processElements($movedElements, $groupId, 'tl_content'); + + return $nextIndex; + } + + /** + * Handles a bs_gridStop: deletes it and advances the index. + * + * @param array> $elements + * + * @throws Exception + */ + private function handleGridStop(array $elements, int $index): int + { + $this->deleteElement((int) $elements[$index]['id']); + + return $index + 1; + } + + /** + * Collects all elements from $startIndex until the matching bs_gridStop (depth-aware). + * The matching bs_gridStop itself is deleted and NOT included in the result. + * + * @param array> $elements + * + * @return array{0: array>, 1: int} + * + * @throws Exception + */ + private function collectUntilMatchingStop(array $elements, int $startIndex): array + { + $children = []; + $depth = 1; + $idx = $startIndex; + $count = count($elements); + + while ($idx < $count && $depth > 0) { + $type = $elements[$idx]['type']; + + if ($type === 'bs_gridStart') { + $depth++; + $children[] = $elements[$idx]; + } elseif ($type === 'bs_gridStop') { + $depth--; + if ($depth === 0) { + $this->deleteElement((int) $elements[$idx]['id']); + $idx++; + break; + } + + $children[] = $elements[$idx]; + } else { + $children[] = $elements[$idx]; + } + + $idx++; + } + + return [$children, $idx]; + } + + /** + * Collects elements from $startIndex until the next bs_gridSeparator or bs_gridStop at depth 0. + * Nested grids are included as a block (depth-aware). + * + * @param array> $elements + * + * @return array{0: array>, 1: int} + */ + private function collectUntilNextSeparatorOrStop(array $elements, int $startIndex): array + { + $children = []; + $depth = 0; + $idx = $startIndex; + $count = count($elements); + + while ($idx < $count) { + $type = $elements[$idx]['type']; + + if ($type === 'bs_gridStart') { + $depth++; + $children[] = $elements[$idx]; + } elseif ($type === 'bs_gridStop') { + if ($depth === 0) { + break; + } + + $depth--; + $children[] = $elements[$idx]; + } elseif ($type === 'bs_gridSeparator') { + if ($depth === 0) { + break; + } + + $children[] = $elements[$idx]; + } else { + $children[] = $elements[$idx]; + } + + $idx++; + } + + return [$children, $idx]; + } + + /** + * @param array> $elements + * + * @throws Exception + */ + private function moveElementsToParent(array $elements, int $newPid, string $newPtable): void + { + $sorting = 128; + + foreach ($elements as $element) { + $this->connection->executeStatement( + 'UPDATE tl_content SET pid = :pid, ptable = :ptable, sorting = :sorting WHERE id = :id', + [ + 'pid' => $newPid, + 'ptable' => $newPtable, + 'sorting' => $sorting, + 'id' => $element['id'], + ], + ); + $sorting += 128; + } + } + + /** @throws Exception */ + private function deleteElement(int $elementId): void + { + $this->connection->executeStatement( + 'DELETE FROM tl_content WHERE id = :id', + ['id' => $elementId], ); } } From 954a2aa56579afde3084f0dcec77cd42d97ff8a5 Mon Sep 17 00:00:00 2001 From: "c.boelter" Date: Thu, 7 May 2026 17:13:37 +0200 Subject: [PATCH 13/15] remove phpcq.lock --- .phpcq.lock | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .phpcq.lock diff --git a/.phpcq.lock b/.phpcq.lock deleted file mode 100644 index a940923..0000000 --- a/.phpcq.lock +++ /dev/null @@ -1 +0,0 @@ -{"plugins":{"doctrine-coding-standard":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/doctrine-coding-standard/doctrine-coding-standard-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"composer":{"doctrine/coding-standard":"^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0 || ^14.0"}},"checksum":{"type":"sha-512","value":"00fab498a6575bf07930e078fd616c0481714570bc1c61ebae4fa277d64c0cb28575aba9190c9731c7bda9f97f57c113516e1eb2920c3b9b7b295e0078be3159"},"tools":{},"composerLock":"{\n \"_readme\": [\n \"This file locks the dependencies of your project to a known state\",\n \"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies\",\n \"This file is @generated automatically\"\n ],\n \"content-hash\": \"40b84043be9c25e6feb38a9e014f1a9f\",\n \"packages\": [\n {\n \"name\": \"dealerdirect/phpcodesniffer-composer-installer\",\n \"version\": \"v1.2.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/composer-installer.git\",\n \"reference\": \"963f0c67bffde0eac41b56be71ac0e8ba132f0bd\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd\",\n \"reference\": \"963f0c67bffde0eac41b56be71ac0e8ba132f0bd\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"composer-plugin-api\": \"^2.2\",\n \"php\": \">=5.4\",\n \"squizlabs/php_codesniffer\": \"^3.1.0 || ^4.0\"\n },\n \"require-dev\": {\n \"composer/composer\": \"^2.2\",\n \"ext-json\": \"*\",\n \"ext-zip\": \"*\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.4.0\",\n \"phpcompatibility/php-compatibility\": \"^9.0 || ^10.0.0@dev\",\n \"yoast/phpunit-polyfills\": \"^1.0\"\n },\n \"type\": \"composer-plugin\",\n \"extra\": {\n \"class\": \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\Plugin\"\n },\n \"autoload\": {\n \"psr-4\": {\n \"PHPCSStandards\\\\Composer\\\\Plugin\\\\Installers\\\\PHPCodeSniffer\\\\\": \"src/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Franck Nijhof\",\n \"email\": \"opensource@frenck.dev\",\n \"homepage\": \"https://frenck.dev\",\n \"role\": \"Open source developer\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/composer-installer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer Standards Composer Installer Plugin\",\n \"keywords\": [\n \"PHPCodeSniffer\",\n \"PHP_CodeSniffer\",\n \"code quality\",\n \"codesniffer\",\n \"composer\",\n \"installer\",\n \"phpcbf\",\n \"phpcs\",\n \"plugin\",\n \"qa\",\n \"quality\",\n \"standard\",\n \"standards\",\n \"style guide\",\n \"stylecheck\",\n \"tests\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/composer-installer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/composer-installer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/composer-installer\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2026-05-06T08:26:05+00:00\"\n },\n {\n \"name\": \"doctrine/coding-standard\",\n \"version\": \"14.0.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/doctrine/coding-standard.git\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/doctrine/coding-standard/zipball/897a7dc209e49ee6cf04e689c41112df17967130\",\n \"reference\": \"897a7dc209e49ee6cf04e689c41112df17967130\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.6.2 || ^0.7 || ^1.0.0\",\n \"php\": \"^7.4 || ^8.0\",\n \"slevomat/coding-standard\": \"^8.23\",\n \"squizlabs/php_codesniffer\": \"^4\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"authors\": [\n {\n \"name\": \"Benjamin Eberlei\",\n \"email\": \"kontakt@beberlei.de\"\n },\n {\n \"name\": \"Steve Müller\",\n \"email\": \"st.mueller@dzh-online.de\"\n }\n ],\n \"description\": \"The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.\",\n \"homepage\": \"https://www.doctrine-project.org/projects/coding-standard.html\",\n \"keywords\": [\n \"checks\",\n \"code\",\n \"coding\",\n \"cs\",\n \"dev\",\n \"doctrine\",\n \"rules\",\n \"sniffer\",\n \"sniffs\",\n \"standard\",\n \"style\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/doctrine/coding-standard/issues\",\n \"source\": \"https://github.com/doctrine/coding-standard/tree/14.0.0\"\n },\n \"time\": \"2025-09-21T18:21:47+00:00\"\n },\n {\n \"name\": \"phpstan/phpdoc-parser\",\n \"version\": \"2.3.2\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/phpstan/phpdoc-parser.git\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"reference\": \"a004701b11273a26cd7955a61d67a7f1e525a45a\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"php\": \"^7.4 || ^8.0\"\n },\n \"require-dev\": {\n \"doctrine/annotations\": \"^2.0\",\n \"nikic/php-parser\": \"^5.3.0\",\n \"php-parallel-lint/php-parallel-lint\": \"^1.2\",\n \"phpstan/extension-installer\": \"^1.0\",\n \"phpstan/phpstan\": \"^2.0\",\n \"phpstan/phpstan-phpunit\": \"^2.0\",\n \"phpstan/phpstan-strict-rules\": \"^2.0\",\n \"phpunit/phpunit\": \"^9.6\",\n \"symfony/process\": \"^5.2\"\n },\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"PHPStan\\\\PhpDocParser\\\\\": [\n \"src/\"\n ]\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"PHPDoc parser with support for nullable, intersection and generic types\",\n \"support\": {\n \"issues\": \"https://github.com/phpstan/phpdoc-parser/issues\",\n \"source\": \"https://github.com/phpstan/phpdoc-parser/tree/2.3.2\"\n },\n \"time\": \"2026-01-25T14:56:51+00:00\"\n },\n {\n \"name\": \"slevomat/coding-standard\",\n \"version\": \"8.29.0\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/slevomat/coding-standard.git\",\n \"reference\": \"81fce13c4ef4b53a03e5cfa6ce36afc191c1598e\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/slevomat/coding-standard/zipball/81fce13c4ef4b53a03e5cfa6ce36afc191c1598e\",\n \"reference\": \"81fce13c4ef4b53a03e5cfa6ce36afc191c1598e\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"dealerdirect/phpcodesniffer-composer-installer\": \"^0.7 || ^1.2.1\",\n \"php\": \"^7.4 || ^8.0\",\n \"phpstan/phpdoc-parser\": \"^2.3.2\",\n \"squizlabs/php_codesniffer\": \"^4.0.1\"\n },\n \"require-dev\": {\n \"phing/phing\": \"3.0.1|3.1.2\",\n \"php-parallel-lint/php-parallel-lint\": \"1.4.0\",\n \"phpstan/phpstan\": \"2.1.54\",\n \"phpstan/phpstan-deprecation-rules\": \"2.0.4\",\n \"phpstan/phpstan-phpunit\": \"2.0.16\",\n \"phpstan/phpstan-strict-rules\": \"2.0.11\",\n \"phpunit/phpunit\": \"9.6.34|10.5.63|11.4.4|11.5.55|12.5.24\"\n },\n \"type\": \"phpcodesniffer-standard\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"8.x-dev\"\n }\n },\n \"autoload\": {\n \"psr-4\": {\n \"SlevomatCodingStandard\\\\\": \"SlevomatCodingStandard/\"\n }\n },\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"MIT\"\n ],\n \"description\": \"Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.\",\n \"keywords\": [\n \"dev\",\n \"phpcs\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/slevomat/coding-standard/issues\",\n \"source\": \"https://github.com/slevomat/coding-standard/tree/8.29.0\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/kukulich\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://tidelift.com/funding/github/packagist/slevomat/coding-standard\",\n \"type\": \"tidelift\"\n }\n ],\n \"time\": \"2026-05-07T05:48:08+00:00\"\n },\n {\n \"name\": \"squizlabs/php_codesniffer\",\n \"version\": \"4.0.1\",\n \"source\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer.git\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\"\n },\n \"dist\": {\n \"type\": \"zip\",\n \"url\": \"https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5\",\n \"reference\": \"0525c73950de35ded110cffafb9892946d7771b5\",\n \"shasum\": \"\"\n },\n \"require\": {\n \"ext-simplexml\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"php\": \">=7.2.0\"\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31\"\n },\n \"bin\": [\n \"bin/phpcbf\",\n \"bin/phpcs\"\n ],\n \"type\": \"library\",\n \"notification-url\": \"https://packagist.org/downloads/\",\n \"license\": [\n \"BSD-3-Clause\"\n ],\n \"authors\": [\n {\n \"name\": \"Greg Sherwood\",\n \"role\": \"Former lead\"\n },\n {\n \"name\": \"Juliette Reinders Folmer\",\n \"role\": \"Current lead\"\n },\n {\n \"name\": \"Contributors\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors\"\n }\n ],\n \"description\": \"PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.\",\n \"homepage\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"keywords\": [\n \"phpcs\",\n \"standards\",\n \"static analysis\"\n ],\n \"support\": {\n \"issues\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/issues\",\n \"security\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy\",\n \"source\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer\",\n \"wiki\": \"https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki\"\n },\n \"funding\": [\n {\n \"url\": \"https://github.com/PHPCSStandards\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://github.com/jrfnl\",\n \"type\": \"github\"\n },\n {\n \"url\": \"https://opencollective.com/php_codesniffer\",\n \"type\": \"open_collective\"\n },\n {\n \"url\": \"https://thanks.dev/u/gh/phpcsstandards\",\n \"type\": \"thanks_dev\"\n }\n ],\n \"time\": \"2025-11-10T16:43:36+00:00\"\n }\n ],\n \"packages-dev\": [],\n \"aliases\": [],\n \"minimum-stability\": \"stable\",\n \"stability-flags\": {},\n \"prefer-stable\": false,\n \"prefer-lowest\": false,\n \"platform\": {},\n \"platform-dev\": {},\n \"plugin-api-version\": \"2.6.0\"\n}\n"},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.51.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.51.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.51.0/composer-normalize.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.18.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.18.0/composer-require-checker.phar","requirements":{"php":{"php":"~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.18.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"6.5.0","url":"https://github.com/vimeo/psalm/releases/download/6.5.0/psalm.phar","requirements":{"php":{"php":"~8.1.17 || ~8.2.4 || ~8.3.0 || ~8.4.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/6.5.0/psalm.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^4.0 || ^3.0 || ^2.0","phpcbf":"^4.0 || ^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"03f1c6c2d94b79d0e8cbd42996382e0d100c7e07f84c3138fa3a8b394e814ec18ce05cbbd257e527913219b2264f062522e4cf3e3bd402b907b9437d96982b44"},"tools":{"phpcs":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcs.phar.asc"},"phpcbf":{"version":"4.0.1","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar","requirements":{"php":{"php":">=7.2.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/4.0.1/phpcbf.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file From a6cfdecc8900232b6a3487fa6d28afa697412021 Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 3 Jun 2026 11:04:52 +0200 Subject: [PATCH 14/15] Refactor `GridWrapperMigration` to simplify grid element migration logic and remove redundant methods. --- src/Migration/GridWrapperMigration.php | 363 +++++++------------------ 1 file changed, 91 insertions(+), 272 deletions(-) diff --git a/src/Migration/GridWrapperMigration.php b/src/Migration/GridWrapperMigration.php index 01a8f70..9ee0aca 100644 --- a/src/Migration/GridWrapperMigration.php +++ b/src/Migration/GridWrapperMigration.php @@ -10,8 +10,6 @@ use Exception; use Override; -use function count; - final class GridWrapperMigration extends AbstractMigration { public function __construct(private readonly Connection $connection, private readonly bool $enableMigration = false) @@ -32,29 +30,20 @@ public function shouldRun(): bool return false; } - if (! $schemaManager->tablesExist(['tl_content'])) { - return false; - } - $count = $this->connection->fetchOne( - "SELECT COUNT(*) FROM tl_content WHERE type IN ('bs_gridStart', 'bs_gridStop', 'bs_gridSeparator')", + "SELECT COUNT(*) FROM tl_content WHERE type = 'bs_gridStart'", ); return $count > 0; } /** @throws Exception */ + #[Override] public function run(): MigrationResult { $this->connection->transactional(function (): void { - $roots = $this->connection->fetchAllAssociative( - "SELECT DISTINCT pid, ptable FROM tl_content - WHERE type IN ('bs_gridStart', 'bs_gridStop', 'bs_gridSeparator')", - ); - - foreach ($roots as $root) { - $elements = $this->fetchElements((int) $root['pid'], $root['ptable']); - $this->processElements($elements, (int) $root['pid'], $root['ptable']); + while ($startElement = $this->fetchNextGridStart()) { + $this->migrateGridStart($startElement); } }); @@ -62,302 +51,137 @@ public function run(): MigrationResult } /** - * @return array> + * @return array|false * * @throws Exception */ - private function fetchElements(int $pid, string $ptable): array + private function fetchNextGridStart(): array|false { - return $this->connection->fetchAllAssociative( - 'SELECT * FROM tl_content WHERE pid = :pid AND ptable = :ptable ORDER BY sorting ASC', - ['pid' => $pid, 'ptable' => $ptable], + return $this->connection->fetchAssociative( + "SELECT * FROM tl_content WHERE type = 'bs_gridStart' ORDER BY id LIMIT 1", ); } /** - * Main recursive processor. Works with an index pointer through the flat list. - * - * @param array> $elements + * @param array $start * * @throws Exception */ - private function processElements(array $elements, int $pid, string $ptable): void + private function migrateGridStart(array $start): void { - $idx = 0; - $count = count($elements); - - while ($idx < $count) { - $element = $elements[$idx]; - - match ($element['type']) { - 'bs_gridStart' => $idx = $this->handleGridStart($elements, $idx, $pid, $ptable), - 'bs_gridSeparator' => $idx = $this->handleSeparator($elements, $idx, $pid, $ptable), - 'bs_gridStop' => $idx = $this->handleGridStop($elements, $idx), - default => $idx++, - }; - } - } + $startId = (int) $start['id']; + $pid = (int) $start['pid']; + $ptable = (string) $start['ptable']; + $startSorting = (int) $start['sorting']; + $tstamp = (int) $start['tstamp']; + + $stop = $this->connection->fetchAssociative( + "SELECT * FROM tl_content WHERE bs_grid_parent = :parent AND type = 'bs_gridStop'", + ['parent' => $startId], + ); - /** - * Handles a bs_gridStart: converts it to bs_grid_wrapper, - * collects all children until matching bs_gridStop and moves them into the wrapper. - * If there are bs_gridSeparators at the top level, elements before the first separator - * are wrapped in an implicit element_group. - * - * @param array> $elements - * - * @throws Exception - */ - private function handleGridStart(array $elements, int $index): int - { - $startElement = $elements[$index]; - $wrapperId = (int) $startElement['id']; + $separators = $this->connection->fetchAllAssociative( + "SELECT * FROM tl_content WHERE bs_grid_parent = :parent AND type = 'bs_gridSeparator' ORDER BY sorting ASC", + ['parent' => $startId], + ); $this->connection->executeStatement( "UPDATE tl_content SET type = 'bs_grid_wrapper' WHERE id = :id", - ['id' => $wrapperId], + ['id' => $startId], ); - // Collect all children until matching bs_gridStop - [$children, $nextIndex] = $this->collectUntilMatchingStop($elements, $index + 1); - - // Move children into wrapper with fresh sorting - $this->moveElementsToParent($children, $wrapperId, 'tl_content'); - - // Only wrap leading elements in an implicit group if there are top-level separators - $this->wrapLeadingElementsInGroup($wrapperId, $startElement); + $stopSorting = $stop !== false ? (int) $stop['sorting'] : PHP_INT_MAX; + $bounds = [$startSorting]; + $wrapperSorting = 128; - // Now recursively process the children inside the wrapper - $movedElements = $this->fetchElements($wrapperId, 'tl_content'); - $this->processElements($movedElements, $wrapperId, 'tl_content'); - - return $nextIndex; - } + foreach ($separators as $sep) { + $bounds[] = (int) $sep['sorting']; + } - /** - * Wraps all elements before the first bs_gridSeparator inside a wrapper - * into a newly created element_group. - * If there is no top-level bs_gridSeparator, nothing is done. - * - * @param array $referenceElement Used to copy tstamp for the new row - * - * @throws Exception - */ - private function wrapLeadingElementsInGroup(int $wrapperPid, array $referenceElement): void - { - $children = $this->fetchElements($wrapperPid, 'tl_content'); + $bounds[] = $stopSorting; - // If there is no bs_gridSeparator at depth 0, elements stay directly in the wrapper - if (! $this->hasTopLevelSeparator($children)) { - return; - } + // Slot 0: elements before first separator (or before stop if no separators) + $slot0 = $this->fetchSlotElements($pid, $ptable, $bounds[0], $bounds[1]); + $wrapperSorting = $this->placeSlotElements($slot0, $startId, null, $wrapperSorting, $tstamp); - // Collect elements before the first bs_gridSeparator at depth 0 - $leadingElements = []; - $depth = 0; - - foreach ($children as $child) { - $type = $child['type']; - - if ($type === 'bs_gridStart') { - $depth++; - $leadingElements[] = $child; - } elseif ($type === 'bs_gridStop') { - if ($depth === 0) { - break; - } - - $depth--; - $leadingElements[] = $child; - } elseif ($type === 'bs_gridSeparator') { - if ($depth === 0) { - // Stop here, do not include the separator itself - break; - } - - $leadingElements[] = $child; - } else { - $leadingElements[] = $child; - } + // One slot per separator: elements after the separator until next separator or stop + foreach ($separators as $i => $sep) { + $sepId = (int) $sep['id']; + $nextBound = $bounds[$i + 2]; + $slotElements = $this->fetchSlotElements($pid, $ptable, (int) $sep['sorting'], $nextBound); + $wrapperSorting = $this->placeSlotElements($slotElements, $startId, $sepId, $wrapperSorting, $tstamp); } - // Nothing to wrap (grid starts directly with a separator) - if (empty($leadingElements)) { - return; + if ($stop !== false) { + $this->deleteElement((int) $stop['id']); } - - // Create a new element_group as first child of the wrapper - // sorting = 64 so it sits before the existing children which start at 128 - $this->connection->executeStatement( - "INSERT INTO tl_content (pid, ptable, sorting, tstamp, type) -VALUES (:pid, :ptable, :sorting, :tstamp, 'element_group')", - [ - 'pid' => $wrapperPid, - 'ptable' => 'tl_content', - 'sorting' => 64, - 'tstamp' => $referenceElement['tstamp'], - ], - ); - - $groupId = (int) $this->connection->lastInsertId(); - - $this->moveElementsToParent($leadingElements, $groupId, 'tl_content'); } /** - * Checks whether there is at least one bs_gridSeparator at depth 0 in the given element list. + * Places slot elements into the wrapper according to count: + * - 0 elements: separator (if any) is deleted + * - 1 element: element moves directly into wrapper, separator (if any) is deleted + * - >1 elements: separator becomes element_group (or new group is created), elements move into it * - * @param array> $elements - */ - private function hasTopLevelSeparator(array $elements): bool - { - $depth = 0; - - foreach ($elements as $element) { - $type = $element['type']; - - if ($type === 'bs_gridStart') { - $depth++; - } elseif ($type === 'bs_gridStop') { - $depth--; - } elseif ($type === 'bs_gridSeparator' && $depth === 0) { - return true; - } - } - - return false; - } - - /** - * Handles a bs_gridSeparator: converts it to element_group, - * collects all following siblings until next bs_gridSeparator or bs_gridStop - * and moves them into the group. + * Returns the next available wrapper sorting value. * * @param array> $elements * * @throws Exception */ - private function handleSeparator(array $elements, int $index): int + private function placeSlotElements(array $elements, int $wrapperId, int|null $separatorId, int $wrapperSorting, int $tstamp): int { - $separatorElement = $elements[$index]; - $groupId = (int) $separatorElement['id']; - - $this->connection->executeStatement( - "UPDATE tl_content SET type = 'element_group' WHERE id = :id", - ['id' => $groupId], - ); - - // Collect following siblings until next separator or stop (depth-aware) - [$children, $nextIndex] = $this->collectUntilNextSeparatorOrStop($elements, $index + 1); - - // Move collected elements into the element_group - $this->moveElementsToParent($children, $groupId, 'tl_content'); - - // Recursively process elements inside the group (e.g. nested grids inside a group) - $movedElements = $this->fetchElements($groupId, 'tl_content'); - $this->processElements($movedElements, $groupId, 'tl_content'); + if (count($elements) > 1) { + if ($separatorId !== null) { + $this->connection->executeStatement( + "UPDATE tl_content SET type = 'element_group', pid = :pid, ptable = 'tl_content', sorting = :sorting WHERE id = :id", + ['pid' => $wrapperId, 'sorting' => $wrapperSorting, 'id' => $separatorId], + ); + $groupId = $separatorId; + } else { + $groupId = $this->insertElementGroup($wrapperId, 'tl_content', $wrapperSorting, $tstamp); + } - return $nextIndex; - } + $wrapperSorting += 128; + $this->moveElementsToParent($elements, $groupId, 'tl_content'); + } elseif (count($elements) === 1) { + if ($separatorId !== null) { + $this->deleteElement($separatorId); + } - /** - * Handles a bs_gridStop: deletes it and advances the index. - * - * @param array> $elements - * - * @throws Exception - */ - private function handleGridStop(array $elements, int $index): int - { - $this->deleteElement((int) $elements[$index]['id']); + $this->moveElementsToParent($elements, $wrapperId, 'tl_content', $wrapperSorting); + $wrapperSorting += 128; + } else { + if ($separatorId !== null) { + $this->deleteElement($separatorId); + } + } - return $index + 1; + return $wrapperSorting; } /** - * Collects all elements from $startIndex until the matching bs_gridStop (depth-aware). - * The matching bs_gridStop itself is deleted and NOT included in the result. - * - * @param array> $elements - * - * @return array{0: array>, 1: int} + * @return array> * * @throws Exception */ - private function collectUntilMatchingStop(array $elements, int $startIndex): array + private function fetchSlotElements(int $pid, string $ptable, int $fromSorting, int $toSorting): array { - $children = []; - $depth = 1; - $idx = $startIndex; - $count = count($elements); - - while ($idx < $count && $depth > 0) { - $type = $elements[$idx]['type']; - - if ($type === 'bs_gridStart') { - $depth++; - $children[] = $elements[$idx]; - } elseif ($type === 'bs_gridStop') { - $depth--; - if ($depth === 0) { - $this->deleteElement((int) $elements[$idx]['id']); - $idx++; - break; - } - - $children[] = $elements[$idx]; - } else { - $children[] = $elements[$idx]; - } - - $idx++; - } - - return [$children, $idx]; + return $this->connection->fetchAllAssociative( + 'SELECT * FROM tl_content WHERE pid = :pid AND ptable = :ptable AND sorting > :from AND sorting < :to ORDER BY sorting ASC', + ['pid' => $pid, 'ptable' => $ptable, 'from' => $fromSorting, 'to' => $toSorting], + ); } - /** - * Collects elements from $startIndex until the next bs_gridSeparator or bs_gridStop at depth 0. - * Nested grids are included as a block (depth-aware). - * - * @param array> $elements - * - * @return array{0: array>, 1: int} - */ - private function collectUntilNextSeparatorOrStop(array $elements, int $startIndex): array + /** @throws Exception */ + private function insertElementGroup(int $pid, string $ptable, int $sorting, int $tstamp): int { - $children = []; - $depth = 0; - $idx = $startIndex; - $count = count($elements); - - while ($idx < $count) { - $type = $elements[$idx]['type']; - - if ($type === 'bs_gridStart') { - $depth++; - $children[] = $elements[$idx]; - } elseif ($type === 'bs_gridStop') { - if ($depth === 0) { - break; - } - - $depth--; - $children[] = $elements[$idx]; - } elseif ($type === 'bs_gridSeparator') { - if ($depth === 0) { - break; - } - - $children[] = $elements[$idx]; - } else { - $children[] = $elements[$idx]; - } - - $idx++; - } + $this->connection->executeStatement( + "INSERT INTO tl_content (pid, ptable, sorting, tstamp, type) VALUES (:pid, :ptable, :sorting, :tstamp, 'element_group')", + ['pid' => $pid, 'ptable' => $ptable, 'sorting' => $sorting, 'tstamp' => $tstamp], + ); - return [$children, $idx]; + return (int) $this->connection->lastInsertId(); } /** @@ -365,30 +189,25 @@ private function collectUntilNextSeparatorOrStop(array $elements, int $startInde * * @throws Exception */ - private function moveElementsToParent(array $elements, int $newPid, string $newPtable): void + private function moveElementsToParent(array $elements, int $newPid, string $newPtable, int $startSorting = 128): void { - $sorting = 128; + $sorting = $startSorting; foreach ($elements as $element) { $this->connection->executeStatement( 'UPDATE tl_content SET pid = :pid, ptable = :ptable, sorting = :sorting WHERE id = :id', - [ - 'pid' => $newPid, - 'ptable' => $newPtable, - 'sorting' => $sorting, - 'id' => $element['id'], - ], + ['pid' => $newPid, 'ptable' => $newPtable, 'sorting' => $sorting, 'id' => $element['id']], ); $sorting += 128; } } /** @throws Exception */ - private function deleteElement(int $elementId): void + private function deleteElement(int $id): void { $this->connection->executeStatement( 'DELETE FROM tl_content WHERE id = :id', - ['id' => $elementId], + ['id' => $id], ); } } From 55181ea3f3e887bb78526533acf7d2d1900c5208 Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 3 Jun 2026 11:32:19 +0200 Subject: [PATCH 15/15] Fix code style --- src/Migration/GridWrapperMigration.php | 79 ++++++++++++++++++-------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/src/Migration/GridWrapperMigration.php b/src/Migration/GridWrapperMigration.php index 9ee0aca..91f1223 100644 --- a/src/Migration/GridWrapperMigration.php +++ b/src/Migration/GridWrapperMigration.php @@ -10,6 +10,10 @@ use Exception; use Override; +use function count; + +use const PHP_INT_MAX; + final class GridWrapperMigration extends AbstractMigration { public function __construct(private readonly Connection $connection, private readonly bool $enableMigration = false) @@ -80,10 +84,12 @@ private function migrateGridStart(array $start): void ['parent' => $startId], ); - $separators = $this->connection->fetchAllAssociative( - "SELECT * FROM tl_content WHERE bs_grid_parent = :parent AND type = 'bs_gridSeparator' ORDER BY sorting ASC", - ['parent' => $startId], - ); + $query = <<<'SQL' + SELECT * FROM tl_content + WHERE bs_grid_parent = :parent AND type = 'bs_gridSeparator' + ORDER BY sorting ASC +SQL; + $separators = $this->connection->fetchAllAssociative($query, ['parent' => $startId]); $this->connection->executeStatement( "UPDATE tl_content SET type = 'bs_grid_wrapper' WHERE id = :id", @@ -101,20 +107,23 @@ private function migrateGridStart(array $start): void $bounds[] = $stopSorting; // Slot 0: elements before first separator (or before stop if no separators) - $slot0 = $this->fetchSlotElements($pid, $ptable, $bounds[0], $bounds[1]); + /** @psalm-suppress PossiblyUndefinedArrayOffset */ + $slot0 = $this->fetchSlotElements($pid, $ptable, $bounds[0], $bounds[1]); $wrapperSorting = $this->placeSlotElements($slot0, $startId, null, $wrapperSorting, $tstamp); // One slot per separator: elements after the separator until next separator or stop foreach ($separators as $i => $sep) { - $sepId = (int) $sep['id']; - $nextBound = $bounds[$i + 2]; - $slotElements = $this->fetchSlotElements($pid, $ptable, (int) $sep['sorting'], $nextBound); + $sepId = (int) $sep['id']; + $nextBound = $bounds[$i + 2]; + $slotElements = $this->fetchSlotElements($pid, $ptable, (int) $sep['sorting'], $nextBound); $wrapperSorting = $this->placeSlotElements($slotElements, $startId, $sepId, $wrapperSorting, $tstamp); } - if ($stop !== false) { - $this->deleteElement((int) $stop['id']); + if ($stop === false) { + return; } + + $this->deleteElement((int) $stop['id']); } /** @@ -129,13 +138,24 @@ private function migrateGridStart(array $start): void * * @throws Exception */ - private function placeSlotElements(array $elements, int $wrapperId, int|null $separatorId, int $wrapperSorting, int $tstamp): int - { + private function placeSlotElements( + array $elements, + int $wrapperId, + int|null $separatorId, + int $wrapperSorting, + int $tstamp, + ): int { if (count($elements) > 1) { if ($separatorId !== null) { - $this->connection->executeStatement( - "UPDATE tl_content SET type = 'element_group', pid = :pid, ptable = 'tl_content', sorting = :sorting WHERE id = :id", - ['pid' => $wrapperId, 'sorting' => $wrapperSorting, 'id' => $separatorId], + $this->connection->update( + 'tl_content', + [ + 'pid' => $wrapperId, + 'ptable' => 'tl_content', + 'type' => 'element_group', + 'sorting' => $wrapperSorting, + ], + ['id' => $separatorId], ); $groupId = $separatorId; } else { @@ -167,8 +187,15 @@ private function placeSlotElements(array $elements, int $wrapperId, int|null $se */ private function fetchSlotElements(int $pid, string $ptable, int $fromSorting, int $toSorting): array { + $query = <<<'SQL' + SELECT * + FROM tl_content + WHERE pid = :pid AND ptable = :ptable AND sorting > :from AND sorting < :to + ORDER BY sorting ASC +SQL; + return $this->connection->fetchAllAssociative( - 'SELECT * FROM tl_content WHERE pid = :pid AND ptable = :ptable AND sorting > :from AND sorting < :to ORDER BY sorting ASC', + $query, ['pid' => $pid, 'ptable' => $ptable, 'from' => $fromSorting, 'to' => $toSorting], ); } @@ -176,12 +203,10 @@ private function fetchSlotElements(int $pid, string $ptable, int $fromSorting, i /** @throws Exception */ private function insertElementGroup(int $pid, string $ptable, int $sorting, int $tstamp): int { - $this->connection->executeStatement( - "INSERT INTO tl_content (pid, ptable, sorting, tstamp, type) VALUES (:pid, :ptable, :sorting, :tstamp, 'element_group')", - ['pid' => $pid, 'ptable' => $ptable, 'sorting' => $sorting, 'tstamp' => $tstamp], + return (int) $this->connection->insert( + 'tl_content', + ['pid' => $pid, 'ptable' => $ptable, 'sorting' => $sorting, 'tstamp' => $tstamp, 'type' => 'element_group'], ); - - return (int) $this->connection->lastInsertId(); } /** @@ -189,8 +214,12 @@ private function insertElementGroup(int $pid, string $ptable, int $sorting, int * * @throws Exception */ - private function moveElementsToParent(array $elements, int $newPid, string $newPtable, int $startSorting = 128): void - { + private function moveElementsToParent( + array $elements, + int $newPid, + string $newPtable, + int $startSorting = 128, + ): void { $sorting = $startSorting; foreach ($elements as $element) { @@ -203,11 +232,11 @@ private function moveElementsToParent(array $elements, int $newPid, string $newP } /** @throws Exception */ - private function deleteElement(int $id): void + private function deleteElement(int $elementId): void { $this->connection->executeStatement( 'DELETE FROM tl_content WHERE id = :id', - ['id' => $id], + ['id' => $elementId], ); } }